From 764f2a154900babc14fb135c0aa92ea4dfa148ca Mon Sep 17 00:00:00 2001 From: Li ZongYing Date: Wed, 7 Feb 2024 19:21:17 +0800 Subject: [PATCH] fix time error --- README.md | 5 + .../java/com/lizongying/mytv/MainActivity.kt | 32 ++++-- .../java/com/lizongying/mytv/MainFragment.kt | 16 ++- .../com/lizongying/mytv/PlayerFragment.kt | 2 +- .../main/java/com/lizongying/mytv/Request.kt | 1 - .../java/com/lizongying/mytv/UpdateManager.kt | 101 ++++++++++++++++++ .../main/java/com/lizongying/mytv/Utils.kt | 53 ++++++++- .../java/com/lizongying/mytv/api/ApiClient.kt | 8 ++ .../main/java/com/lizongying/mytv/api/Info.kt | 31 +++++- .../com/lizongying/mytv/api/ReleaseService.kt | 11 ++ .../com/lizongying/mytv/requests/MyRequest.kt | 44 ++++++++ 11 files changed, 278 insertions(+), 26 deletions(-) create mode 100644 app/src/main/java/com/lizongying/mytv/UpdateManager.kt create mode 100644 app/src/main/java/com/lizongying/mytv/api/ReleaseService.kt create mode 100644 app/src/main/java/com/lizongying/mytv/requests/MyRequest.kt diff --git a/README.md b/README.md index 503024ec..9b879257 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,11 @@ ## 更新日志 +### v1.5.6(通用版) + +* 解决部分设备系统时间不对导致播放失败的问题 +* + ### v1.5.2(通用版) * 修复APP恢复后频道号、频道列表不自动消失的问题 diff --git a/app/src/main/java/com/lizongying/mytv/MainActivity.kt b/app/src/main/java/com/lizongying/mytv/MainActivity.kt index 0ff52c21..e4f0c815 100644 --- a/app/src/main/java/com/lizongying/mytv/MainActivity.kt +++ b/app/src/main/java/com/lizongying/mytv/MainActivity.kt @@ -18,16 +18,22 @@ import android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION import android.view.WindowManager import android.widget.Toast import androidx.fragment.app.FragmentActivity +import androidx.lifecycle.lifecycleScope import com.lizongying.mytv.models.TVViewModel +import kotlinx.coroutines.CoroutineStart +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.async +import kotlinx.coroutines.launch import java.security.MessageDigest class MainActivity : FragmentActivity() { - var playerFragment = PlayerFragment() - private val mainFragment = MainFragment() - private val infoFragment = InfoFragment() - private val channelFragment = ChannelFragment() + private var ready = 0 + private var playerFragment = PlayerFragment() + private var mainFragment = MainFragment() + private var infoFragment = InfoFragment() + private var channelFragment = ChannelFragment() private lateinit var settingFragment: SettingFragment private var doubleBackToExitPressedOnce = false @@ -44,6 +50,16 @@ class MainActivity : FragmentActivity() { private var versionName = "" + init { + lifecycleScope.launch(Dispatchers.IO) { + val utilsJob = async(start = CoroutineStart.LAZY) { Utils.init() } + + utilsJob.start() + + utilsJob.await() + } + } + override fun onCreate(savedInstanceState: Bundle?) { Log.i(TAG, "onCreate") super.onCreate(savedInstanceState) @@ -156,7 +172,11 @@ class MainActivity : FragmentActivity() { } fun fragmentReady() { - mainFragment.fragmentReady() + ready++ + Log.i(TAG, "ready $ready") + if (ready == 4) { + mainFragment.fragmentReady() + } } override fun onTouchEvent(event: MotionEvent?): Boolean { @@ -496,7 +516,7 @@ class MainActivity : FragmentActivity() { override fun onResume() { Log.i(TAG, "onResume") super.onResume() - if (!mainFragment.isHidden){ + if (!mainFragment.isHidden) { handler.postDelayed(hideMain, delayHideMain) } } diff --git a/app/src/main/java/com/lizongying/mytv/MainFragment.kt b/app/src/main/java/com/lizongying/mytv/MainFragment.kt index 7bde53f2..8c1d0b15 100644 --- a/app/src/main/java/com/lizongying/mytv/MainFragment.kt +++ b/app/src/main/java/com/lizongying/mytv/MainFragment.kt @@ -42,8 +42,6 @@ class MainFragment : Fragment(), CardAdapter.ItemListener { private val handler = Handler(Looper.getMainLooper()) private lateinit var mUpdateProgramRunnable: UpdateProgramRunnable - private var ready = 0 - override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? @@ -149,7 +147,7 @@ class MainFragment : Fragment(), CardAdapter.ItemListener { } } - fragmentReady() + (activity as MainActivity).fragmentReady() } } @@ -180,6 +178,8 @@ class MainFragment : Fragment(), CardAdapter.ItemListener { fun setPosition() { val tvViewModel = tvListViewModel.getTVViewModel(itemPosition) + Log.i(TAG, "tvViewModel $tvViewModel") + Log.i(TAG, "rowList ${rowList.size}") rowList[tvViewModel!!.getRowPosition()].post { ((rowList[tvViewModel.getRowPosition()] as RecyclerView).layoutManager as LinearLayoutManager).findViewByPosition( tvViewModel.getItemPosition() @@ -212,12 +212,8 @@ class MainFragment : Fragment(), CardAdapter.ItemListener { } fun fragmentReady() { - ready++ - Log.i(TAG, "ready $ready") - if (ready == 4) { // request.fetchPage() - tvListViewModel.getTVViewModel(itemPosition)?.changed() - } + tvListViewModel.getTVViewModel(itemPosition)?.changed() } fun play(itemPosition: Int) { @@ -299,7 +295,9 @@ class MainFragment : Fragment(), CardAdapter.ItemListener { override fun onDestroy() { Log.i(TAG, "onDestroy") super.onDestroy() - handler.removeCallbacks(mUpdateProgramRunnable) + if (::mUpdateProgramRunnable.isInitialized) { + handler.removeCallbacks(mUpdateProgramRunnable) + } } override fun onDestroyView() { diff --git a/app/src/main/java/com/lizongying/mytv/PlayerFragment.kt b/app/src/main/java/com/lizongying/mytv/PlayerFragment.kt index 89aa03cd..80c5a87b 100644 --- a/app/src/main/java/com/lizongying/mytv/PlayerFragment.kt +++ b/app/src/main/java/com/lizongying/mytv/PlayerFragment.kt @@ -32,7 +32,6 @@ class PlayerFragment : Fragment() { ): View { _binding = PlayerBinding.inflate(inflater, container, false) playerView = _binding!!.playerView - (activity as MainActivity).playerFragment = this playerView?.viewTreeObserver?.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener { override fun onGlobalLayout() { @@ -86,6 +85,7 @@ class PlayerFragment : Fragment() { if (playerView != null && playerView!!.player?.isPlaying == false) { Log.i(TAG, "replay") playerView!!.player?.prepare() + playerView!!.player?.play() } else { Log.i(TAG, "playing") } diff --git a/app/src/main/java/com/lizongying/mytv/Request.kt b/app/src/main/java/com/lizongying/mytv/Request.kt index 2125cb33..58905f42 100644 --- a/app/src/main/java/com/lizongying/mytv/Request.kt +++ b/app/src/main/java/com/lizongying/mytv/Request.kt @@ -364,7 +364,6 @@ class Request { for ((_, v) in TVList.list) { for (v2 in v) { - Log.i(TAG, "$v2") if (v2.title == item.channelName || v2.alias == item.channelName) { v2.pid = item.pid v2.sid = item.streamId diff --git a/app/src/main/java/com/lizongying/mytv/UpdateManager.kt b/app/src/main/java/com/lizongying/mytv/UpdateManager.kt new file mode 100644 index 00000000..2bb5361a --- /dev/null +++ b/app/src/main/java/com/lizongying/mytv/UpdateManager.kt @@ -0,0 +1,101 @@ +package com.lizongying.mytv + +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.net.Uri +import android.os.Build +import android.os.Environment +import android.util.Log +import androidx.annotation.RequiresApi +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 myRequest = MyRequest() + + @RequiresApi(Build.VERSION_CODES.O) + fun checkAndUpdate() { + CoroutineScope(Dispatchers.Main).launch { + try { + val release = myRequest.getRelease() + // 在主线程中更新 UI + updateUI(release) + if (release?.data?.versionCode!! > 0) { + startDownload(release) + } + } catch (e: Exception) { + // 处理异常情况 + Log.e(TAG, "Error occurred: ${e.message}", e) + } + } + } + + private fun updateUI(release: Release?) { + } + + @RequiresApi(Build.VERSION_CODES.O) + private fun startDownload(release: Release) { + val apkFileName = "my-tv-${release.data.versionName}.apk" + + val downloadManager = + context!!.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager + val request = Request(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) + + // 注册广播接收器,监听下载完成事件 + context!!.registerReceiver( + DownloadReceiver(context!!, apkFileName, downloadReference), + IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE), Context.RECEIVER_NOT_EXPORTED + ) + } + + + 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) + + // 检查是否是我们发起的下载 + 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" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lizongying/mytv/Utils.kt b/app/src/main/java/com/lizongying/mytv/Utils.kt index 4eeb39b8..f8c70b9a 100644 --- a/app/src/main/java/com/lizongying/mytv/Utils.kt +++ b/app/src/main/java/com/lizongying/mytv/Utils.kt @@ -3,18 +3,63 @@ package com.lizongying.mytv import android.content.Context import android.content.res.Resources import android.util.TypedValue +import com.google.gson.Gson +import com.lizongying.mytv.api.TimeResponse +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext import java.io.File -import java.text.SimpleDateFormat -import java.util.* import java.io.IOException +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Locale object Utils { + private var between: Long = 0 + fun getDateFormat(format: String): String { - return SimpleDateFormat(format, Locale.CHINA).format(Date()) + return SimpleDateFormat( + format, + Locale.CHINA + ).format(Date(System.currentTimeMillis() - between)) } fun getDateTimestamp(): Long { - return Date().time / 1000 + return (System.currentTimeMillis() - between) / 1000 + } + + suspend fun init() { + var currentTimeMillis: Long = 0 + try { + currentTimeMillis = getTimestampFromServer() + } catch (e: Exception) { + println("Failed to retrieve timestamp from server: ${e.message}") + } + between = System.currentTimeMillis() - currentTimeMillis + } + + /** + * 从服务器获取时间戳 + * @return Long 时间戳 + */ + private suspend fun getTimestampFromServer(): Long { + return withContext(Dispatchers.IO) { + val client = okhttp3.OkHttpClient.Builder() + .connectTimeout(500, java.util.concurrent.TimeUnit.MILLISECONDS) + .readTimeout(1, java.util.concurrent.TimeUnit.SECONDS).build() + val request = okhttp3.Request.Builder() + .url("https://api.m.taobao.com/rest/api3.do?api=mtop.common.getTimestamp") + .build() + try { + client.newCall(request).execute().use { response -> + if (!response.isSuccessful) throw IOException("Unexpected code $response") + val string = response.body()?.string() + Gson().fromJson(string, TimeResponse::class.java).data.t.toLong() + } + } catch (e: IOException) { + // Handle network errors + throw IOException("Error during network request", e) + } + } } fun dpToPx(dp: Float): Int { diff --git a/app/src/main/java/com/lizongying/mytv/api/ApiClient.kt b/app/src/main/java/com/lizongying/mytv/api/ApiClient.kt index 076e6aab..945dcdd3 100644 --- a/app/src/main/java/com/lizongying/mytv/api/ApiClient.kt +++ b/app/src/main/java/com/lizongying/mytv/api/ApiClient.kt @@ -34,6 +34,14 @@ class ApiClient { .build().create(YSPTokenService::class.java) } + val ReleaseService by lazy { + Retrofit.Builder() + .baseUrl(myUrl) + .client(okHttpClient) + .addConverterFactory(GsonConverterFactory.create()) + .build().create(ReleaseService::class.java) + } + val yspProtoService: YSPProtoService by lazy { Retrofit.Builder() .baseUrl(protoUrl) diff --git a/app/src/main/java/com/lizongying/mytv/api/Info.kt b/app/src/main/java/com/lizongying/mytv/api/Info.kt index 7708ac8b..0f46b184 100644 --- a/app/src/main/java/com/lizongying/mytv/api/Info.kt +++ b/app/src/main/java/com/lizongying/mytv/api/Info.kt @@ -3,9 +3,30 @@ package com.lizongying.mytv.api data class Info( val code: Int?, val msg: String?, - val data: InfoData, -) + val data: Data, +) { + data class Data( + val token: String, + ) +} -data class InfoData( - val token: String, -) +data class Release( + 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( + val data: Time +) { + data class Time( + val t: String + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/lizongying/mytv/api/ReleaseService.kt b/app/src/main/java/com/lizongying/mytv/api/ReleaseService.kt new file mode 100644 index 00000000..d06eea16 --- /dev/null +++ b/app/src/main/java/com/lizongying/mytv/api/ReleaseService.kt @@ -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 +} \ No newline at end of file diff --git a/app/src/main/java/com/lizongying/mytv/requests/MyRequest.kt b/app/src/main/java/com/lizongying/mytv/requests/MyRequest.kt new file mode 100644 index 00000000..4aed67f8 --- /dev/null +++ b/app/src/main/java/com/lizongying/mytv/requests/MyRequest.kt @@ -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 { + override fun onResponse(call: Call, response: Response) { + if (response.isSuccessful) { + continuation.resume(response.body()) + } else { + continuation.resume(null) + } + } + + override fun onFailure(call: Call, t: Throwable) { + continuation.resume(null) + } + }) + } + } + + companion object { + private const val TAG = "MyRequest" + } +} \ No newline at end of file