Skip to content

Commit

Permalink
Merge pull request #223 from LeGend-wLw/main
Browse files Browse the repository at this point in the history
完成服务器有关所有功能,并优化相关代码
  • Loading branch information
lizongying authored Feb 6, 2024
2 parents 4fb27ba + d8342e8 commit 0a7837c
Show file tree
Hide file tree
Showing 8 changed files with 263 additions and 95 deletions.
157 changes: 157 additions & 0 deletions app/src/main/java/com/lizongying/mytv/ChannelUtils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package com.lizongying.mytv

import android.content.Context
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.File

/**
*@author LeGend
*@date 2024/2/4 22:42
*/
object ChannelUtils {
/**
* 获取服务器channel版本
*
* @param context Context
*
* @return 服务器channel版本
*/
suspend fun getServerVersion(context: Context): Int {
return withContext(Dispatchers.IO) {
val client = okhttp3.OkHttpClient.Builder().connectTimeout(500, java.util.concurrent.TimeUnit.MILLISECONDS)
.readTimeout(1, java.util.concurrent.TimeUnit.SECONDS).build()
client.newCall(okhttp3.Request.Builder().url(getServerVersionUrl(context)).build()).execute()
.use { response ->
if (!response.isSuccessful) throw java.io.IOException("Unexpected code $response")
val body = response.body()
body?.string()?.toInt() ?: 0
}
}
}

/**
* 获取服务器channel
*
* @param url String 服务器地址
*
* @return Array<TV> 服务器channel
*
* @throws java.io.IOException 网络请求失败
*/
suspend fun getServerChannel(url: String): List<TV> {
val result = 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(url).build()
client.newCall(request).execute().use { response ->
if (!response.isSuccessful) throw java.io.IOException("Unexpected code $response")
val body = response.body()
body?.string() ?: ""
}
}
return withContext(Dispatchers.Default) {
val type = object : com.google.gson.reflect.TypeToken<List<TV>>() {}.type
com.google.gson.Gson().fromJson(result, type)
}
}

/**
* 获取服务器地址
*
* @param context Context
*
* @return 服务器地址
*/
fun getServerUrl(context: Context): String {
return context.resources.getString(R.string.server_url)
}

/**
* 获取serverVersion的URL
*
* @param context Context
*
* @return serverVersionURL 服务器版本地址
*/
suspend fun getServerVersionUrl(context: Context): String {
return withContext(Dispatchers.IO) {
context.resources.getString(R.string.server_version_url)
}
}


/**
* 获取本地channel版本
*
* @param context Context
*
* @return 本地channel
*/
suspend fun getLocalVersion(context: Context): Int {
return withContext(Dispatchers.IO) {
val file = File(getAppDirectory(context), "channels")
//检查本地是否已经有保存的channels.json,若无保存的Channel.json则从读取assert中文件
val savedVersion =
context.getSharedPreferences("saved_version", Context.MODE_PRIVATE).getInt("version", Integer.MIN_VALUE)
if (!file.exists() || savedVersion == Integer.MIN_VALUE) {
context.resources.getInteger(R.integer.local_channel_version)
} else {
savedVersion
}
}
}

/**
* 获取本地可读取的目录
* @param context Context
*
* @return 可读取的目录
*/
private fun getAppDirectory(context: Context): File {
return context.filesDir
}

/**
* 获取本地channel
*
* @param context Context
*
* @return Array<TV> 本地channel
*/
suspend fun getLocalChannel(context: Context): List<TV> {
val str = withContext(Dispatchers.IO) {
if (File(getAppDirectory(context), "channels").exists()) {
File(getAppDirectory(context), "channels").readText()
} else {
context.resources.openRawResource(R.raw.channels).bufferedReader().use { it.readText() }
}
}
return withContext(Dispatchers.Default) {
val type = object : com.google.gson.reflect.TypeToken<List<TV>>() {}.type
com.google.gson.Gson().fromJson(str, type)
}
}

/**
* 更新channels.json
*
* @param context Context
*
* @return 无
*
* @throws java.io.IOException 写入失败
*/
suspend fun updateLocalChannel(context: Context, version: Int, channels: List<TV>) {
withContext(Dispatchers.IO) {
val file = File(getAppDirectory(context), "channels")
if (!file.exists()) {
file.createNewFile()
}
file.writeText(com.google.gson.Gson().toJson(channels))
context.getSharedPreferences("saved_version", Context.MODE_PRIVATE).edit().putInt(
"version", version
).apply()
}
}
}
1 change: 1 addition & 0 deletions app/src/main/java/com/lizongying/mytv/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class MainActivity : FragmentActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
Log.i(TAG, "onCreate")
TVList.init(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

Expand Down
3 changes: 1 addition & 2 deletions app/src/main/java/com/lizongying/mytv/MainFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,7 @@ class MainFragment : BrowseSupportFragment() {
val cardPresenter = CardPresenter(viewLifecycleOwner)

var idx: Long = 0
context?.let { TVList.init(it) }
for ((k, v) in TVList.list) {
for ((k, v) in TVList.list!!) {
val listRowAdapter = ArrayObjectAdapter(cardPresenter)
for ((idx2, v1) in v.withIndex()) {
val tvViewModel = TVViewModel(v1)
Expand Down
4 changes: 1 addition & 3 deletions app/src/main/java/com/lizongying/mytv/Request.kt
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,6 @@ class Request {

fun initYSP(context: Context) {
ysp = YSP(context)
//TODO 不确定在哪里初始化
TVList.init(context)
}

var call: Call<LiveInfo>? = null
Expand Down Expand Up @@ -374,7 +372,7 @@ class Request {
continue
}
val tv =
TVList.list[channelType]?.find { it.title == mapping[item.channelName] }
TVList.list?.get(channelType)?.find { it.title == mapping[item.channelName] }
if (tv != null) {
tv.logo = item.tvLogo
tv.pid = item.pid
Expand Down
105 changes: 66 additions & 39 deletions app/src/main/java/com/lizongying/mytv/TVList.kt
Original file line number Diff line number Diff line change
@@ -1,54 +1,81 @@
package com.lizongying.mytv

import android.content.Context
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import java.io.File
import android.util.Log
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.io.IOException
import kotlin.math.log

object TVList {
lateinit var list: Map<String, List<TV>>
private val channels = "channels.json"

fun init(context: Context) {
if (::list.isInitialized) {
return
}
synchronized(this) {
if (::list.isInitialized) {
return
@Volatile
var list: Map<String, List<TV>>? = null
get():Map<String, List<TV>>? {
//等待初始化完成
while (this.list === null) {
Thread.sleep(10)
}
list = setupTV(context)
return this.list
}
}


private fun setupTV(context: Context): Map<String, List<TV>> {
val map: MutableMap<String, MutableList<TV>> = mutableMapOf()
val appDirectory = Utils.getAppDirectory(context)

//检查当前目录下是否存在channels.json
val file = File(appDirectory, channels)
if (!file.exists()) {
//不存在则从assets中拷贝
file.createNewFile()
context.resources.openRawResource(R.raw.channels).use { input ->
file.outputStream().use { output ->
input.copyTo(output)
}
/**
* 初始化
*
* @param context Context
*/
fun init(context: Context) {
CoroutineScope(Dispatchers.Default).launch {
//获取本地版本号
val localVersion = ChannelUtils.getLocalVersion(context)
//获取服务器版本号
val serverVersion = try {
ChannelUtils.getServerVersion(context)
} catch (e: IOException) {
Log.e("TVList", "无法从服务器获取版本信息", e)
Integer.MIN_VALUE
}
}

//读取channels.json,并转换为Map<String,LIst<TV>>
val json = file.readText()
//防止类型擦除
val type = object : TypeToken<Array<TV>>() {}.type
Gson().fromJson<Array<TV>>(json, type)?.forEach {
if (map.containsKey(it.channel)) {
map[it.channel]?.add(it)
//频道列表
val channelTVMap: MutableMap<String, MutableList<TV>> = mutableMapOf()
//是否从服务器更新
var updateFromServer = false
//获取频道列表
val tvList: List<TV> = if (localVersion < serverVersion) {
//获取服务器地址
val url = ChannelUtils.getServerUrl(context)
//是否从服务器更新
updateFromServer = true
Log.i("TVList", "从服务器获取频道信息")
try {
ChannelUtils.getServerChannel(url)
} catch (e: IOException) {
Log.e("TVList", "无法从服务器获取频道信息", e)
updateFromServer = false
ChannelUtils.getLocalChannel(context)
}
} else {
map[it.channel] = mutableListOf(it)
Log.i("TVList", "从本地获取频道信息")
//获取本地频道
ChannelUtils.getLocalChannel(context)
}
//按频道分类
for (tv in tvList) {
val key = tv.channel
if (channelTVMap.containsKey(key)) {
val list = channelTVMap[key]!!
list.add(tv)
channelTVMap[key] = list
} else {
channelTVMap[key] = mutableListOf(tv)
}
}
//保存频道列表
list = channelTVMap
//保存版本号
if (updateFromServer) {
ChannelUtils.updateLocalChannel(context, serverVersion, tvList)
}
}
return map
}
}
Loading

0 comments on commit 0a7837c

Please sign in to comment.