fix: 图片和语音改用 HttpURLConnection 下载
OkHttp 在该设备连 OSS 的 HTTPS/HTTP 都被 reset。 改用系统 HttpURLConnection(走系统 TLS 栈,可能支持不同 cipher suite)。 图片和语音统一使用原始 HTTPS URL。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -19,8 +19,6 @@ import com.xiaoqu.watch.databinding.FragmentTaskListBinding
|
|||||||
import com.xiaoqu.watch.network.ApiResult
|
import com.xiaoqu.watch.network.ApiResult
|
||||||
import com.xiaoqu.watch.network.api.TaskApi
|
import com.xiaoqu.watch.network.api.TaskApi
|
||||||
import com.xiaoqu.watch.network.safeApiCall
|
import com.xiaoqu.watch.network.safeApiCall
|
||||||
import okhttp3.OkHttpClient
|
|
||||||
import okhttp3.Request
|
|
||||||
import com.xiaoqu.watch.service.manager.NfcTaskManager
|
import com.xiaoqu.watch.service.manager.NfcTaskManager
|
||||||
import com.xiaoqu.watch.ui.common.BaseFragment
|
import com.xiaoqu.watch.ui.common.BaseFragment
|
||||||
import com.xiaoqu.watch.ui.widget.QuTipDialog
|
import com.xiaoqu.watch.ui.widget.QuTipDialog
|
||||||
@@ -45,8 +43,6 @@ class TaskListFragment : BaseFragment<FragmentTaskListBinding>() {
|
|||||||
@Inject lateinit var taskApi: TaskApi
|
@Inject lateinit var taskApi: TaskApi
|
||||||
@Inject lateinit var nfcTaskManager: NfcTaskManager
|
@Inject lateinit var nfcTaskManager: NfcTaskManager
|
||||||
|
|
||||||
/** 下载用的 OkHttp(不带签名拦截器,走 HTTP 避免 TLS 问题) */
|
|
||||||
private val downloadClient by lazy { OkHttpClient.Builder().build() }
|
|
||||||
|
|
||||||
/** 任务 ID 列表(queryTaskIds 返回) */
|
/** 任务 ID 列表(queryTaskIds 返回) */
|
||||||
private var taskList: List<TaskItem> = emptyList()
|
private var taskList: List<TaskItem> = emptyList()
|
||||||
@@ -438,10 +434,9 @@ class TaskListFragment : BaseFragment<FragmentTaskListBinding>() {
|
|||||||
scaleType = android.widget.ImageView.ScaleType.CENTER_CROP
|
scaleType = android.widget.ImageView.ScaleType.CENTER_CROP
|
||||||
setBackgroundColor(requireContext().getColor(R.color.card_background))
|
setBackgroundColor(requireContext().getColor(R.color.card_background))
|
||||||
}
|
}
|
||||||
val httpUrl = pic.url.replace("https://", "http://")
|
loadImage(imageView, pic.url)
|
||||||
loadImage(imageView, httpUrl)
|
|
||||||
// 点击放大全屏查看
|
// 点击放大全屏查看
|
||||||
imageView.setOnClickListener { showFullImage(httpUrl) }
|
imageView.setOnClickListener { showFullImage(pic.url) }
|
||||||
binding.picContainer.addView(imageView)
|
binding.picContainer.addView(imageView)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -471,18 +466,21 @@ class TaskListFragment : BaseFragment<FragmentTaskListBinding>() {
|
|||||||
loadImage(imageView, url)
|
loadImage(imageView, url)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 异步加载图片到 ImageView */
|
/** 异步加载图片到 ImageView(使用系统 HttpURLConnection) */
|
||||||
private fun loadImage(imageView: android.widget.ImageView, url: String) {
|
private fun loadImage(imageView: android.widget.ImageView, url: String) {
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
try {
|
try {
|
||||||
val bitmap = kotlinx.coroutines.withContext(kotlinx.coroutines.Dispatchers.IO) {
|
val bitmap = kotlinx.coroutines.withContext(kotlinx.coroutines.Dispatchers.IO) {
|
||||||
val request = Request.Builder().url(url).build()
|
val conn = java.net.URL(url).openConnection() as java.net.HttpURLConnection
|
||||||
val response = downloadClient.newCall(request).execute()
|
conn.connectTimeout = 10000
|
||||||
if (response.isSuccessful) {
|
conn.readTimeout = 10000
|
||||||
response.body?.byteStream()?.let {
|
conn.connect()
|
||||||
android.graphics.BitmapFactory.decodeStream(it)
|
if (conn.responseCode == 200) {
|
||||||
}
|
conn.inputStream.use { android.graphics.BitmapFactory.decodeStream(it) }
|
||||||
} else null
|
} else {
|
||||||
|
Timber.w("图片HTTP错误: ${conn.responseCode} $url")
|
||||||
|
null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
bitmap?.let { imageView.setImageBitmap(it) }
|
bitmap?.let { imageView.setImageBitmap(it) }
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@@ -549,21 +547,21 @@ class TaskListFragment : BaseFragment<FragmentTaskListBinding>() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 用 OkHttp 下载音频文件到缓存目录 */
|
/** 下载音频文件到缓存目录(使用系统 HttpURLConnection) */
|
||||||
private fun downloadToCache(url: String): java.io.File? {
|
private fun downloadToCache(url: String): java.io.File? {
|
||||||
return try {
|
return try {
|
||||||
// OSS 的 HTTPS 在 Android 8.1 设备 TLS 握手失败,降级为 HTTP
|
val conn = java.net.URL(url).openConnection() as java.net.HttpURLConnection
|
||||||
val httpUrl = url.replace("https://", "http://")
|
conn.connectTimeout = 15000
|
||||||
val request = Request.Builder().url(httpUrl).build()
|
conn.readTimeout = 15000
|
||||||
val response = downloadClient.newCall(request).execute()
|
conn.connect()
|
||||||
if (!response.isSuccessful) {
|
if (conn.responseCode != 200) {
|
||||||
Timber.w("语音下载HTTP错误: ${response.code}")
|
Timber.w("语音下载HTTP错误: ${conn.responseCode}")
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
// 写入缓存文件
|
// 写入缓存文件
|
||||||
val fileName = "voice_${url.hashCode()}.mp3"
|
val fileName = "voice_${url.hashCode()}.mp3"
|
||||||
val cacheFile = java.io.File(requireContext().cacheDir, fileName)
|
val cacheFile = java.io.File(requireContext().cacheDir, fileName)
|
||||||
response.body?.byteStream()?.use { input ->
|
conn.inputStream.use { input ->
|
||||||
cacheFile.outputStream().use { output ->
|
cacheFile.outputStream().use { output ->
|
||||||
input.copyTo(output)
|
input.copyTo(output)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user