diff --git a/HISTORY.md b/HISTORY.md index 89d6462f..a2185e69 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,17 @@ ## 更新日誌 +### v1.3.8.0-kitkat + +* 優化遠程配置 + +### v1.3.8.0 + +* 優化遠程配置 + +### v1.3.7.20 + +* 解決一個可能引起閃退的問題 + ### v1.3.7.20-kitkat * 解決EPG和proxy無法置空的問題 diff --git a/README.md b/README.md index aeef3ff1..4ce9c333 100644 --- a/README.md +++ b/README.md @@ -6,13 +6,13 @@ ## 使用 -* 遙控器左鍵/觸屏單擊打開節目列表 +* 遙控器左鍵/觸屏單擊打開視頻列表 * 遙控器右鍵/觸屏雙擊打開配置 -* 遙控器返回鍵關閉節目列表/配置 -* 打開配置頁后,選擇遠程配置,掃描二維碼可以配置視頻源地址等。 -* 如果視頻源地址已配置,並且打開了“啟動后自動更新視頻源”后,軟件啟動后自動更新視頻源 -* 在節目列表顯示的時候,右鍵收藏/取消收藏 -* 配置地址 http://0.0.0.0:34567 , +* 遙控器返回鍵關閉視頻列表/配置 +* 在聚焦視頻標題的時候,右鍵收藏/取消收藏 +* 打開配置后,選擇遠程配置,掃描二維碼可以配置視頻源等。也可以直接遠程配置地址 http://0.0.0.0:34567 +* 如果視頻源地址已配置,並且打開了“應用啟動后更新視頻源”后,應用啟動后會自動更新視頻源 +* 默認遙控器下鍵/觸屏下滑切換到下一個視頻。換台反轉打開後,邏輯相反 注意: @@ -54,7 +54,9 @@ 下載安裝 [releases](https://github.com/lizongying/my-tv-0/releases/) -更多地址 [my-tv-0](https://lyrics.run/my-tv-0.html) +注意,“*-kitkat”為安卓4.4兼容版本 + +更多下載地址 [my-tv-0](https://lyrics.run/my-tv-0.html) ![image](./screenshots/Screenshot_20240810_151748.png) ![image](./screenshots/Screenshot_20240813_232847.png) diff --git a/app/src/main/java/com/lizongying/mytv0/MainViewModel.kt b/app/src/main/java/com/lizongying/mytv0/MainViewModel.kt index 4ad5ab6b..daef57c9 100644 --- a/app/src/main/java/com/lizongying/mytv0/MainViewModel.kt +++ b/app/src/main/java/com/lizongying/mytv0/MainViewModel.kt @@ -32,7 +32,9 @@ class MainViewModel : ViewModel() { private lateinit var appDirectory: File var listModel: List = listOf() val groupModel = TVGroupModel() - + private var cacheFile: File? = null + private var cacheConfig = "" + private var initialized = false private val _channelsOk = MutableLiveData() val channelsOk: LiveData @@ -71,27 +73,43 @@ class MainViewModel : ViewModel() { } } + private fun getCache(): String { + return if (cacheFile!!.exists()) { + cacheFile!!.readText() + } else { + "" + } + } + fun init(context: Context) { groupModel.addTVListModel(TVListModel("我的收藏", 0)) groupModel.addTVListModel(TVListModel("全部頻道", 1)) appDirectory = context.filesDir - val file = File(appDirectory, FILE_NAME) - val str = if (file.exists()) { - file.readText() - } else { - context.resources.openRawResource(R.raw.channels).bufferedReader() + cacheFile = File(appDirectory, FILE_NAME) + if (!cacheFile!!.exists()) { + cacheFile!!.createNewFile() + } + + cacheConfig = getCache() + Log.i(TAG, "cacheConfig $cacheConfig") + + if (cacheConfig.isEmpty()) { + cacheConfig = context.resources.openRawResource(R.raw.channels).bufferedReader() .use { it.readText() } + Log.i(TAG, "cacheConfig $cacheConfig") } try { - str2List(str) + str2List(cacheConfig) } catch (e: Exception) { e.printStackTrace() - file.deleteOnExit() + cacheFile!!.deleteOnExit() R.string.channel_read_error.showToast() } + initialized = true + _channelsOk.value = true } @@ -128,20 +146,9 @@ class MainViewModel : ViewModel() { val response = HttpClient.okHttpClient.newCall(request).execute() if (response.isSuccessful) { - val file = File(appDirectory, FILE_NAME) - if (!file.exists()) { - file.createNewFile() - } val str = response.body()!!.string() withContext(Dispatchers.Main) { - if (str2List(str)) { - file.writeText(str) - SP.config = serverUrl - _channelsOk.value = true - R.string.channel_import_success.showToast() - } else { - R.string.channel_import_error.showToast() - } + tryStr2List(str, null, serverUrl) } } else { Log.e(TAG, "Request status ${response.code()}") @@ -186,18 +193,7 @@ class MainViewModel : ViewModel() { return } - try { - if (str2List(str)) { - SP.config = uri.toString() - R.string.channel_import_success.showToast() - } else { - R.string.channel_import_error.showToast() - } - } catch (e: Exception) { - e.printStackTrace() - file.deleteOnExit() - R.string.channel_read_error.showToast() - } + tryStr2List(str, file, uri.toString()) } else { CoroutineScope(Dispatchers.IO).launch { update(uri.toString()) @@ -205,13 +201,37 @@ class MainViewModel : ViewModel() { } } - fun str2List(str: String): Boolean { + fun tryStr2List(str: String, file: File?, url: String) { + try { + if (str2List(str)) { + cacheFile!!.writeText(str) + cacheConfig = str + SP.config = url + _channelsOk.value = true + R.string.channel_import_success.showToast() + } else { + R.string.channel_import_error.showToast() + } + } catch (e: Exception) { + e.printStackTrace() + file?.deleteOnExit() + R.string.channel_read_error.showToast() + } + } + + private fun str2List(str: String): Boolean { var string = str + if (initialized && string == cacheConfig) { + return false + } val g = Gua() if (g.verify(str)) { string = g.decode(str) } - if (string.isBlank()) { + if (string.isEmpty()) { + return false + } + if (initialized && string == cacheConfig) { return false } diff --git a/app/src/main/java/com/lizongying/mytv0/ModalFragment.kt b/app/src/main/java/com/lizongying/mytv0/ModalFragment.kt index a19c66ff..68676b5b 100644 --- a/app/src/main/java/com/lizongying/mytv0/ModalFragment.kt +++ b/app/src/main/java/com/lizongying/mytv0/ModalFragment.kt @@ -1,6 +1,8 @@ package com.lizongying.mytv0 +import android.content.Intent import android.graphics.Bitmap +import android.net.Uri import android.os.Bundle import android.os.Handler import android.os.Looper @@ -47,8 +49,14 @@ class ModalFragment : DialogFragment() { Glide.with(requireContext()) .load(bitmap) .into(binding.modalImage) - binding.modalText.text = arguments?.getString(KEY_TEXT) + val text = arguments?.getString(KEY_TEXT) + binding.modalText.text = text binding.modalText.visibility = View.VISIBLE + binding.modal.setOnClickListener { + val url = "http://$text" + val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) + startActivity(intent) + } } else { Glide.with(requireContext()) .load(arguments?.getInt(KEY_DRAWABLE_ID)) diff --git a/app/src/main/java/com/lizongying/mytv0/MyTVExceptionHandler.kt b/app/src/main/java/com/lizongying/mytv0/MyTVExceptionHandler.kt index fd809d30..830acb6c 100644 --- a/app/src/main/java/com/lizongying/mytv0/MyTVExceptionHandler.kt +++ b/app/src/main/java/com/lizongying/mytv0/MyTVExceptionHandler.kt @@ -11,7 +11,6 @@ import kotlinx.coroutines.withContext import okhttp3.MediaType import okhttp3.Request import okhttp3.RequestBody -import java.io.IOException import kotlin.system.exitProcess class MyTVExceptionHandler(private val context: Context) : Thread.UncaughtExceptionHandler { @@ -60,7 +59,6 @@ class MyTVExceptionHandler(private val context: Context) : Thread.UncaughtExcept private suspend fun saveLog(crashInfo: String) { withContext(Dispatchers.IO) { - val requestBody = RequestBody.create(MediaType.parse("text/plain"), crashInfo) val request = Request.Builder() .url("https://lyrics.run/my-tv-0/v1/log") @@ -74,7 +72,7 @@ class MyTVExceptionHandler(private val context: Context) : Thread.UncaughtExcept Log.e(TAG, "log failed: ${response.code()}") } } - } catch (e: IOException) { + } catch (e: Exception) { e.printStackTrace() } } diff --git a/app/src/main/java/com/lizongying/mytv0/SettingFragment.kt b/app/src/main/java/com/lizongying/mytv0/SettingFragment.kt index 79f80284..122b8640 100644 --- a/app/src/main/java/com/lizongying/mytv0/SettingFragment.kt +++ b/app/src/main/java/com/lizongying/mytv0/SettingFragment.kt @@ -22,6 +22,7 @@ import androidx.lifecycle.ViewModelProvider import com.lizongying.mytv0.ModalFragment.Companion.KEY_BITMAP import com.lizongying.mytv0.ModalFragment.Companion.KEY_TEXT import com.lizongying.mytv0.SimpleServer.Companion.PORT +import com.lizongying.mytv0.Utils.getDateTimestamp import com.lizongying.mytv0.databinding.SettingBinding import kotlin.math.max import kotlin.math.min @@ -120,7 +121,7 @@ class SettingFragment : Fragment() { binding.qrcode.setOnClickListener { val imageModalFragment = ModalFragment() val size = Utils.dpToPx(200) - val img = QrCodeUtil().createQRCodeBitmap(server, size, size) + val img = QrCodeUtil().createQRCodeBitmap("$server?${getDateTimestamp()}", size, size) val args = Bundle() args.putString(KEY_TEXT, server.removePrefix("http://")) args.putParcelable(KEY_BITMAP, img) @@ -293,6 +294,7 @@ class SettingFragment : Fragment() { SP.config = SP.DEFAULT_CONFIG_URL Log.i(TAG, "config url: ${SP.config}") + context.deleteFile(FILE_NAME) viewModel.reset(context) confirmConfig() @@ -300,8 +302,6 @@ class SettingFragment : Fragment() { Log.i(TAG, "default channel: ${SP.channel}") confirmChannel() -// context.deleteFile(FILE_NAME) - SP.deleteLike() Log.i(TAG, "clear like") @@ -372,7 +372,7 @@ class SettingFragment : Fragment() { override fun onHiddenChanged(hidden: Boolean) { super.onHiddenChanged(hidden) - if (!hidden) { + if (_binding != null && !hidden) { binding.qrcode.requestFocus() } } diff --git a/app/src/main/java/com/lizongying/mytv0/SimpleServer.kt b/app/src/main/java/com/lizongying/mytv0/SimpleServer.kt index 8d95c142..f835994d 100644 --- a/app/src/main/java/com/lizongying/mytv0/SimpleServer.kt +++ b/app/src/main/java/com/lizongying/mytv0/SimpleServer.kt @@ -43,8 +43,19 @@ class SimpleServer(private val context: Context, private val viewModel: MainView private fun handleSettings(): Response { val response: String try { + val file = File(context.filesDir, FILE_NAME) + var str = if (file.exists()) { + file.readText() + } else { + "" + } + if (str.isEmpty()) { + str = context.resources.openRawResource(R.raw.channels).bufferedReader() + .use { it.readText() } + } val respSettings = RespSettings( channelUri = SP.config ?: "", + channelText = str, channelDefault = SP.channel, proxy = SP.proxy ?: "", epg = SP.epg ?: "", @@ -68,13 +79,7 @@ class SimpleServer(private val context: Context, private val viewModel: MainView try { readBody(session)?.let { handler.post { - if (viewModel.str2List(it)) { - File(context.filesDir, FILE_NAME).writeText(it) - SP.config = "file://" - R.string.channel_import_success.showToast() - } else { - R.string.channel_import_error.showToast() - } + viewModel.tryStr2List(it, null, "") } } } catch (e: Exception) { diff --git a/app/src/main/java/com/lizongying/mytv0/data/RespSettings.kt b/app/src/main/java/com/lizongying/mytv0/data/RespSettings.kt index c6863890..37661f8c 100644 --- a/app/src/main/java/com/lizongying/mytv0/data/RespSettings.kt +++ b/app/src/main/java/com/lizongying/mytv0/data/RespSettings.kt @@ -2,6 +2,7 @@ package com.lizongying.mytv0.data data class RespSettings( val channelUri: String, + val channelText: String, val channelDefault: Int, val proxy: String, val epg: String, diff --git a/app/src/main/java/com/lizongying/mytv0/models/TVGroupModel.kt b/app/src/main/java/com/lizongying/mytv0/models/TVGroupModel.kt index c423e808..3524fb16 100644 --- a/app/src/main/java/com/lizongying/mytv0/models/TVGroupModel.kt +++ b/app/src/main/java/com/lizongying/mytv0/models/TVGroupModel.kt @@ -169,9 +169,6 @@ class TVGroupModel : ViewModel() { // get & set // keep: In the current list loop fun getPrev(keep: Boolean = false): TVModel? { - - Log.i(TAG, "keep $keep") - // No item if (tvGroupValue.size < 2 || tvGroupValue[1].size() == 0) { return null @@ -206,8 +203,6 @@ class TVGroupModel : ViewModel() { // get & set fun getNext(keep: Boolean = false): TVModel? { - Log.i(TAG, "keep $keep") - // No item if (tvGroupValue.size < 2 || tvGroupValue[1].size() == 0) { return null diff --git a/app/src/main/res/layout/modal.xml b/app/src/main/res/layout/modal.xml index f6a9defe..1b206cad 100644 --- a/app/src/main/res/layout/modal.xml +++ b/app/src/main/res/layout/modal.xml @@ -2,6 +2,7 @@ diff --git a/app/src/main/res/raw/index.html b/app/src/main/res/raw/index.html index 4082e3f5..7f0ab0eb 100644 --- a/app/src/main/res/raw/index.html +++ b/app/src/main/res/raw/index.html @@ -43,15 +43,12 @@ color: #EEEEEE; } - input[type="text"], textarea { - width: 20rem; - } - input, textarea { - padding: 10px; + padding: .6rem; border: none; border-radius: 4px; transition: box-shadow 0.3s ease-in-out; + vertical-align: bottom; } input:focus, textarea:focus { @@ -59,56 +56,86 @@ border-color: #66afe9; box-shadow: 0 0 10px rgba(102, 175, 233, 0.6); } + + .container { + display: flex; + flex-direction: row; + } + + input[type="text"], textarea { + flex: 1; + box-sizing: border-box; + } + + input[type="text"], input[type="file"], input[type="button"] { + height: 2.6rem; + } + + input[type="button"] { + margin-left: .6rem; + }

我的電視·〇

- +

視頻源可以设置地址/文本/文件其中之一


- - +
+ + +

- - +
+ + +

- - + +
+ +

- - - + +
+ + +

- - +
+ + +

- - +
+ + +

@@ -119,8 +146,8 @@

我的電視·〇

appName: '我的電視·〇', uriLabel: '視頻源地址', plainTextLabel: '視頻源文本', - uploadFileLabel: '選擇視頻源文件後會自動上傳保存', - channelLabel: '默認播放頻道,從1開始。0代表沒有設置', + uploadFileLabel: '視頻源文件,選擇後會自動上傳保存', + channelLabel: '默認播放頻道,從1開始。0代表默认設置', epgLabel: 'EPG', proxyLabel: '代理地址', confirm: '確認', @@ -129,8 +156,8 @@

我的電視·〇

appName: '我的电视·〇', uriLabel: '视频源地址', plainTextLabel: '视频源文本', - uploadFileLabel: '选择视频源文件后会自动上传保存', - channelLabel: '默认播放频道,从1开始。0代表沒有设置', + uploadFileLabel: '视频源文件,选择后会自动上传保存', + channelLabel: '默认播放频道,从1开始。0代表默認设置', epgLabel: 'EPG', proxyLabel: '代理地址', confirm: '确认', @@ -223,6 +250,7 @@

我的電視·〇

console.log(json); document.querySelector('#channel').value = json.channelDefault; document.querySelector('#uri').value = json.channelUri; + document.querySelector('#plain-text').value = json.channelText; document.querySelector('#epg').value = json.epg; document.querySelector('#proxy').value = json.proxy; })() diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 30eca113..3e9c8851 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -5,11 +5,11 @@ 更新应用 开机自启 赞赏作者 - 应用配置 + 更新配置 默认频道 循环播放时显示视频信息 显示时间 - 应用启动后自动更新视频源 + 应用启动后更新视频源 退出应用 默认频道号 恢复默认 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index a30a128c..06bbce58 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -5,11 +5,11 @@ 更新應用 開機自啟 讚賞作者 - 應用配置 + 更新配置 默認頻道 循環播放時顯示視頻信息 顯示時間 - 應用啟動後自動更新視頻源 + 應用啟動後更新視頻源 退出應用 默認頻道號 恢復默認 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b2446e7e..97bb5c24 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -5,11 +5,11 @@ Check for Updates Boot Startup Appreciate Author - Apply Configuration + Update Configuration Default Channel Show Video Info During Loop Playback Show Time - Auto Update Video Source on App Startup + Update Video Source on App Startup Exit Application Default Channel Number Restore Default diff --git a/version.json b/version.json index d754bfa4..af55a991 100644 --- a/version.json +++ b/version.json @@ -1 +1 @@ -{"version_code": 16975636, "version_name": "v1.3.7.20-kitkat"} +{"version_code": 16975872, "version_name": "v1.3.8.0-kitkat"}