-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #223 from LeGend-wLw/main
完成服务器有关所有功能,并优化相关代码
- Loading branch information
Showing
8 changed files
with
263 additions
and
95 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
} |
Oops, something went wrong.