diff --git a/HISTORY.md b/HISTORY.md index ff1fac2..c6c95cd 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,12 @@ ## 更新日志 +### v1.3.3 + +* 一些樣式優化 +* 不同組頻道不合併 +* 防止頻道文件被覆蓋 +* 修復不顯示全部頻道的問題 + ### v1.3.2 * 固定遠程配置端口為34567 diff --git a/README.md b/README.md index 7434be6..dacdf95 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,7 @@ adb install my-tv-0.apk * 詳細EPG * 淺色菜單 * 無效的頻道? +* 导入文件后,修改配置地址,防止二次覆盖。判断文件是否被修改 ## 讚賞 diff --git a/app/build.gradle.kts b/app/build.gradle.kts index f211eb5..4bc2ec2 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -74,6 +74,7 @@ fun getVersionName(): String { } dependencies { + implementation(libs.appcompat) // For AGP 7.4+ coreLibraryDesugaring(libs.desugar.jdk.libs) @@ -96,7 +97,6 @@ dependencies { implementation(libs.core.ktx) implementation(libs.coroutines) - implementation(libs.leanback) implementation(libs.multidex) implementation(libs.constraintlayout) diff --git a/app/src/main/java/com/lizongying/mytv0/GroupAdapter.kt b/app/src/main/java/com/lizongying/mytv0/GroupAdapter.kt index ca833c2..5e7d358 100644 --- a/app/src/main/java/com/lizongying/mytv0/GroupAdapter.kt +++ b/app/src/main/java/com/lizongying/mytv0/GroupAdapter.kt @@ -102,9 +102,9 @@ class GroupAdapter( val oldLikeMode = tvGroupModel.isInLikeMode; tvGroupModel.isInLikeMode = position == 0 if (tvGroupModel.isInLikeMode) { - R.string.favorite_mode.showToast() +// R.string.favorite_mode.showToast() } else if (oldLikeMode) { - R.string.standard_mode.showToast() +// R.string.standard_mode.showToast() } }, 500) } @@ -152,7 +152,11 @@ class GroupAdapter( class ViewHolder(private val context: Context, private val binding: GroupItemBinding) : RecyclerView.ViewHolder(binding.root) { fun bindTitle(text: String) { - binding.title.text = text + binding.title.text = when (text) { + "我的收藏" -> context.getString(R.string.my_favorites) + "全部頻道" -> context.getString(R.string.all_channels) + else -> text + } } fun focus(hasFocus: Boolean) { diff --git a/app/src/main/java/com/lizongying/mytv0/MainActivity.kt b/app/src/main/java/com/lizongying/mytv0/MainActivity.kt index fda828a..4ddaf47 100644 --- a/app/src/main/java/com/lizongying/mytv0/MainActivity.kt +++ b/app/src/main/java/com/lizongying/mytv0/MainActivity.kt @@ -14,16 +14,16 @@ import android.view.MotionEvent import android.view.View import android.view.WindowManager import android.widget.Toast +import androidx.appcompat.app.AppCompatActivity import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsControllerCompat import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentActivity import com.lizongying.mytv0.models.TVList import java.util.Locale -class MainActivity : FragmentActivity() { +class MainActivity : AppCompatActivity() { private var ok = 0 private var playerFragment = PlayerFragment() @@ -144,26 +144,26 @@ class MainActivity : FragmentActivity() { if (SP.channel > 0) { if (SP.channel < TVList.listModel.size) { TVList.setPosition(SP.channel - 1) - R.string.play_default_channel.showToast() +// R.string.play_default_channel.showToast() } else { SP.channel = 0 TVList.setPosition(0) - R.string.default_channel_out_of_range.showToast() +// R.string.default_channel_out_of_range.showToast() } } else { if (!TVList.setPosition(SP.position)) { TVList.setPosition(0) - R.string.last_channel_out_of_range.showToast() +// R.string.last_channel_out_of_range.showToast() } else { - R.string.play_last_channel.showToast() +// R.string.play_last_channel.showToast() } } TVList.groupModel.isInLikeMode = SP.defaultLike; if (TVList.groupModel.isInLikeMode) { - R.string.favorite_mode.showToast() +// R.string.favorite_mode.showToast() } else { - R.string.standard_mode.showToast() +// R.string.standard_mode.showToast() } // TODO group position diff --git a/app/src/main/java/com/lizongying/mytv0/SettingFragment.kt b/app/src/main/java/com/lizongying/mytv0/SettingFragment.kt index 8917f1d..4b6fda6 100644 --- a/app/src/main/java/com/lizongying/mytv0/SettingFragment.kt +++ b/app/src/main/java/com/lizongying/mytv0/SettingFragment.kt @@ -2,6 +2,7 @@ package com.lizongying.mytv0 import android.Manifest import android.content.pm.PackageManager +import android.graphics.drawable.ColorDrawable import android.net.Uri import android.os.Build import android.os.Bundle @@ -188,69 +189,77 @@ class SettingFragment : Fragment() { val txtTextSize = application.px2PxFont(binding.versionName.textSize) - binding.checkVersion.layoutParams.width = btnWidth - binding.checkVersion.layoutParams.height = btnHeight - binding.checkVersion.textSize = btnTextSize - binding.checkVersion.layoutParams = btnLayoutParams - binding.versionName.layoutParams.width = txtWidth binding.versionName.textSize = txtTextSize - binding.qrcode.layoutParams.width = btnWidth - binding.qrcode.layoutParams.height = btnHeight - binding.qrcode.textSize = btnTextSize - binding.qrcode.layoutParams = btnLayoutParams - - binding.confirmConfig.layoutParams.width = btnWidth - binding.confirmConfig.layoutParams.height = btnHeight - binding.confirmConfig.textSize = btnTextSize - binding.confirmConfig.layoutParams = btnLayoutParams - - binding.clear.layoutParams.width = btnWidth - binding.clear.layoutParams.height = btnHeight - binding.clear.textSize = btnTextSize - binding.clear.layoutParams = btnLayoutParams - - binding.appreciate.layoutParams.width = btnWidth - binding.appreciate.layoutParams.height = btnHeight - binding.appreciate.textSize = btnTextSize - binding.appreciate.layoutParams = btnLayoutParams - - binding.exit.layoutParams.width = btnWidth - binding.exit.layoutParams.height = btnHeight - binding.exit.textSize = btnTextSize - binding.exit.layoutParams = btnLayoutParams + for (i in listOf( + binding.qrcode, + binding.confirmConfig, + binding.clear, + binding.checkVersion, + binding.exit, + binding.appreciate, + )) { + i.layoutParams.width = btnWidth + i.layoutParams.height = btnHeight + i.textSize = btnTextSize + i.layoutParams = btnLayoutParams + i.setOnFocusChangeListener { _, hasFocus -> + if (hasFocus) { + i.background = ColorDrawable( + ContextCompat.getColor( + context, + R.color.focus + ) + ) + } else { + i.background = ColorDrawable( + ContextCompat.getColor( + context, + R.color.description_blur + ) + ) + } + } + } - val textSize = application.px2PxFont(binding.switchChannelReversal.textSize) + val textSizeSwitch = application.px2PxFont(binding.switchChannelReversal.textSize) - val layoutParamsChannelReversal = + val layoutParamsSwitch = binding.switchChannelReversal.layoutParams as ViewGroup.MarginLayoutParams - layoutParamsChannelReversal.topMargin = + layoutParamsSwitch.topMargin = application.px2Px(binding.switchChannelReversal.marginTop) - binding.switchChannelReversal.textSize = textSize - binding.switchChannelReversal.layoutParams = layoutParamsChannelReversal - - binding.switchChannelNum.textSize = textSize - binding.switchChannelNum.layoutParams = layoutParamsChannelReversal - - binding.switchTime.textSize = textSize - binding.switchTime.layoutParams = layoutParamsChannelReversal - - binding.switchBootStartup.textSize = textSize - binding.switchBootStartup.layoutParams = layoutParamsChannelReversal - - binding.switchRepeatInfo.textSize = textSize - binding.switchRepeatInfo.layoutParams = layoutParamsChannelReversal - - binding.switchConfigAutoLoad.textSize = textSize - binding.switchConfigAutoLoad.layoutParams = layoutParamsChannelReversal - - binding.switchDefaultLike.textSize = textSize - binding.switchDefaultLike.layoutParams = layoutParamsChannelReversal - - binding.switchShowAllChannels.textSize = textSize - binding.switchShowAllChannels.layoutParams = layoutParamsChannelReversal + for (i in listOf( + binding.switchChannelReversal, + binding.switchChannelNum, + binding.switchTime, + binding.switchBootStartup, + binding.switchRepeatInfo, + binding.switchConfigAutoLoad, + binding.switchDefaultLike, + binding.switchShowAllChannels, + )) { + i.textSize = textSizeSwitch + i.layoutParams = layoutParamsSwitch + i.setOnFocusChangeListener { _, hasFocus -> + if (hasFocus) { + i.setTextColor( + ContextCompat.getColor( + context, + R.color.focus + ) + ) + } else { + i.setTextColor( + ContextCompat.getColor( + context, + R.color.title_blur + ) + ) + } + } + } updateManager = UpdateManager(context, context.appVersionCode) diff --git a/app/src/main/java/com/lizongying/mytv0/SimpleServer.kt b/app/src/main/java/com/lizongying/mytv0/SimpleServer.kt index 08c252d..7354f5f 100644 --- a/app/src/main/java/com/lizongying/mytv0/SimpleServer.kt +++ b/app/src/main/java/com/lizongying/mytv0/SimpleServer.kt @@ -31,16 +31,18 @@ class SimpleServer(private val context: Context) : NanoHTTPD(PORT) { override fun serve(session: IHTTPSession): Response { return when (session.uri) { - "/api/channels" -> handleChannelsRequest(session) - "/api/uri" -> handleUriRequest(session) - "/api/channel" -> handleChannel(session) + "/api/channels" -> handleChannelsFromFile(session) + "/api/uri" -> handleChannelsFromUri(session) + "/api/channel" -> handleDefaultChannel(session) "/api/proxy" -> handleProxy(session) "/api/settings" -> handleSettings() else -> handleStaticContent(session) } } - private fun handleChannelsRequest(session: IHTTPSession): Response { + private fun handleChannelsFromFile(session: IHTTPSession): Response { + R.string.start_config_channel.showToast() + val response = "" try { val map = HashMap() session.parseBody(map) @@ -48,6 +50,7 @@ class SimpleServer(private val context: Context) : NanoHTTPD(PORT) { handler.post { if (TVList.str2List(it)) { File(context.filesDir, TVList.FILE_NAME).writeText(it) + SP.config = "file://" R.string.channel_import_success.showToast() } else { R.string.channel_import_error.showToast() @@ -62,7 +65,6 @@ class SimpleServer(private val context: Context) : NanoHTTPD(PORT) { e.message ) } - val response = "" return newFixedLengthResponse(Response.Status.OK, "text/plain", response) } @@ -84,7 +86,9 @@ class SimpleServer(private val context: Context) : NanoHTTPD(PORT) { var uri: String = "", ) - private fun handleUriRequest(session: IHTTPSession): Response { + private fun handleChannelsFromUri(session: IHTTPSession): Response { + R.string.start_config_channel.showToast() + val response = "" try { val map = HashMap() session.parseBody(map) @@ -103,7 +107,6 @@ class SimpleServer(private val context: Context) : NanoHTTPD(PORT) { "SERVER INTERNAL ERROR: IOException: " + e.message ) } - val response = "频道读取中" return newFixedLengthResponse(Response.Status.OK, "text/plain", response) } @@ -111,7 +114,9 @@ class SimpleServer(private val context: Context) : NanoHTTPD(PORT) { val channel: Int, ) - private fun handleChannel(session: IHTTPSession): Response { + private fun handleDefaultChannel(session: IHTTPSession): Response { + R.string.start_set_default_channel.showToast() + val response = "" try { val map = HashMap() session.parseBody(map) @@ -134,7 +139,6 @@ class SimpleServer(private val context: Context) : NanoHTTPD(PORT) { e.message ) } - val response = "" return newFixedLengthResponse(Response.Status.OK, "text/plain", response) } diff --git a/app/src/main/java/com/lizongying/mytv0/Utils.kt b/app/src/main/java/com/lizongying/mytv0/Utils.kt index 95e823d..0dfa712 100644 --- a/app/src/main/java/com/lizongying/mytv0/Utils.kt +++ b/app/src/main/java/com/lizongying/mytv0/Utils.kt @@ -4,16 +4,53 @@ import android.content.res.Resources import android.os.Build import android.util.TypedValue import com.google.gson.Gson +import com.lizongying.mytv0.ISP.CHINA_MOBILE +import com.lizongying.mytv0.ISP.CHINA_TELECOM +import com.lizongying.mytv0.ISP.CHINA_UNICOM +import com.lizongying.mytv0.ISP.UNKNOWN +import com.lizongying.mytv0.models.TVList import com.lizongying.mytv0.requests.TimeResponse import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import java.io.IOException import java.text.SimpleDateFormat import java.util.Date import java.util.Locale +enum class ISP { + UNKNOWN, + CHINA_MOBILE, + CHINA_UNICOM, + CHINA_TELECOM; + + fun fromName(name: String): ISP { + val isp = when (name) { + "ChinaMobile" -> CHINA_MOBILE + "ChinaUnicom" -> CHINA_UNICOM + "ChinaTelecom" -> CHINA_TELECOM + else -> UNKNOWN + } + return isp + } +} + +data class IpInfo( + val ip: String, + val location: Location +) + +data class Location( + val city_name: String, + val country_name: String, + val isp_domain: String, + val latitude: String, + val longitude: String, + val owner_domain: String, + val region_name: String, +) + + object Utils { private var between: Long = 0 @@ -35,8 +72,15 @@ object Utils { between = System.currentTimeMillis() - currentTimeMillis } } catch (e: Exception) { - println("Failed to retrieve timestamp from server: ${e.message}") + e.printStackTrace() } +// +// try { +// val isp = getISP() +// TVList.setISP(isp) +// } catch (e: Exception) { +// e.printStackTrace() +// } } init { @@ -57,13 +101,38 @@ object Utils { .build() try { client.newCall(request).execute().use { response -> - if (!response.isSuccessful) throw IOException("Unexpected code $response") + if (!response.isSuccessful) return@withContext 0 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) + } catch (e: Exception) { + e.printStackTrace() + 0 + } + } + } + + suspend fun getISP(): ISP { + return withContext(Dispatchers.IO) { + val client = okhttp3.OkHttpClient.Builder().build() + val request = okhttp3.Request.Builder() + .url("https://api.myip.la/json") + .build() + try { + client.newCall(request).execute().use { response -> + if (!response.isSuccessful) return@withContext UNKNOWN + val string = response.body?.string() + val isp = Gson().fromJson(string, IpInfo::class.java).location.isp_domain + when (isp) { + "ChinaMobile" -> CHINA_MOBILE + "ChinaUnicom" -> CHINA_UNICOM + "ChinaTelecom" -> CHINA_TELECOM + else -> UNKNOWN + } + } + } catch (e: Exception) { + e.printStackTrace() + UNKNOWN } } } 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 e029c79..b2e28f8 100644 --- a/app/src/main/java/com/lizongying/mytv0/models/TVGroupModel.kt +++ b/app/src/main/java/com/lizongying/mytv0/models/TVGroupModel.kt @@ -42,6 +42,15 @@ class TVGroupModel : ViewModel() { _tvGroupModel.value = newList } + fun clearNotFilter() { + _tvGroupModel.value = mutableListOf( + (_tvGroupModel.value as List)[0], + (_tvGroupModel.value as List)[1] + ) + setPosition(0) + (_tvGroupModel.value as List)[1].clear() + } + fun clear() { if (SP.showAllChannels) { _tvGroupModel.value = mutableListOf(getTVListModel(0)!!, getTVListModel(1)!!) @@ -81,4 +90,8 @@ class TVGroupModel : ViewModel() { } return _tvGroupModel.value!!.filter { it.getName() != "全部頻道" }.size } + + companion object { + const val TAG = "TVGroupModel" + } } \ No newline at end of file diff --git a/app/src/main/java/com/lizongying/mytv0/models/TVList.kt b/app/src/main/java/com/lizongying/mytv0/models/TVList.kt index 8b90489..d9e4696 100644 --- a/app/src/main/java/com/lizongying/mytv0/models/TVList.kt +++ b/app/src/main/java/com/lizongying/mytv0/models/TVList.kt @@ -7,6 +7,7 @@ import androidx.core.net.toFile import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import com.google.gson.JsonSyntaxException +import com.lizongying.mytv0.ISP import com.lizongying.mytv0.R import com.lizongying.mytv0.SP import com.lizongying.mytv0.requests.HttpClient @@ -28,10 +29,16 @@ object TVList { val groupModel = TVGroupModel() private var epg = SP.epg + private var isp = ISP.UNKNOWN + private val _position = MutableLiveData() val position: LiveData get() = _position + fun setISP(isp: ISP) { + this.isp = isp + } + fun init(context: Context) { _position.value = 0 @@ -59,7 +66,9 @@ object TVList { if (SP.configAutoLoad) { SP.config?.let { - update(it) + if (it.startsWith("http")) { + update(it) + } } } else if (!epg.isNullOrEmpty()) { CoroutineScope(Dispatchers.IO).launch { @@ -214,7 +223,8 @@ object TVList { val title = info.last().trim() var name = nameRegex.find(info.first())?.groupValues?.get(1)?.trim() name = name ?: title - val group = groupRegex.find(info.first())?.groupValues?.get(1)?.trim() + var group = groupRegex.find(info.first())?.groupValues?.get(1)?.trim() + group = group ?: "" val logo = logRegex.find(info.first())?.groupValues?.get(1)?.trim() val uris = if (index + 1 < lines.size) listOf(lines[index + 1].trim()) else emptyList() @@ -227,15 +237,15 @@ object TVList { "", uris, mapOf(), - group ?: "", + group, SourceType.UNKNOWN, listOf(), ) - if (!tvMap.containsKey(name)) { - tvMap[name] = listOf() + if (!tvMap.containsKey(group + name)) { + tvMap[group + name] = listOf() } - tvMap[name] = tvMap[name]!! + tv + tvMap[group + name] = tvMap[group + name]!! + tv } } for ((_, tv) in tvMap) { @@ -275,10 +285,10 @@ object TVList { val title = arr.first().trim() val uris = arr.drop(1) - if (!tvMap.containsKey(title)) { - tvMap[title] = listOf() + if (!tvMap.containsKey(group + title)) { + tvMap[group + title] = listOf() } - tvMap[title] = tvMap[title]!! + uris + tvMap[group + title] = tvMap[group + title]!! + uris } } } @@ -286,7 +296,7 @@ object TVList { val tv = TV( -1, "", - title, + title.removePrefix(group), "", "", "", @@ -304,7 +314,7 @@ object TVList { } } - groupModel.clear() + groupModel.clearNotFilter() val map: MutableMap> = mutableMapOf() for (v in list) { @@ -335,9 +345,8 @@ object TVList { listModel = listModelNew // 全部频道 - groupModel.getTVListModel(1)?.setTVListModel(listModel) + (groupModel.tvGroupModel.value as List)[1].setTVListModel(listModel) - Log.i(TAG, "groupModel ${groupModel.size()}") groupModel.setChange() return true diff --git a/app/src/main/res/color/switch_thumb_color.xml b/app/src/main/res/color/switch_thumb_color.xml new file mode 100644 index 0000000..1d32c19 --- /dev/null +++ b/app/src/main/res/color/switch_thumb_color.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/color/switch_track_color.xml b/app/src/main/res/color/switch_track_color.xml new file mode 100644 index 0000000..7d2edc3 --- /dev/null +++ b/app/src/main/res/color/switch_track_color.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/layout/info.xml b/app/src/main/res/layout/info.xml index 3a6a2b1..bf3c281 100644 --- a/app/src/main/res/layout/info.xml +++ b/app/src/main/res/layout/info.xml @@ -22,7 +22,7 @@ android:id="@+id/logo" android:layout_width="100dp" android:layout_height="match_parent" - android:background="@drawable/rounded_white_left" + android:background="@drawable/rounded_dark_left" android:padding="10dp" /> diff --git a/app/src/main/res/layout/setting.xml b/app/src/main/res/layout/setting.xml index feb7812..cc610f3 100644 --- a/app/src/main/res/layout/setting.xml +++ b/app/src/main/res/layout/setting.xml @@ -1,5 +1,6 @@ -