Compare commits

..
3 Commits
Author SHA1 Message Date
Li ZongYing 188431a29f fix 4.0 crash 2024-03-14 22:08:28 +08:00
Li ZongYing 5c0152e36c optimize fetch token 2024-03-13 12:11:51 +08:00
Li ZongYing a63463559f phoenix retry 2024-03-13 11:31:37 +08:00
8 changed files with 176 additions and 39 deletions
+1
View File
@@ -230,6 +230,7 @@ adb install my-tv.apk
* 节目增加预告
* 频道列表优化
* epg更新后菜单更新
* 防止菜单第一个获取焦点
## 赞赏
Binary file not shown.
Binary file not shown.
@@ -17,15 +17,15 @@ import com.lizongying.mytv.models.TVViewModel
class CardAdapter(
private val recyclerView: RecyclerView,
private val owner: LifecycleOwner,
private val mainFragment: MainFragment,
private var tvListViewModel: TVListViewModel,
) :
RecyclerView.Adapter<CardAdapter.ViewHolder>() {
private var listener: ItemListener? = null
private var focused: View? = null
private var defaultFocused = false
var defaultFocus: Int = -1
var visiable = false
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val cardView = object :
@@ -57,19 +57,23 @@ class CardAdapter(
val tvViewModel = item as TVViewModel
val cardView = viewHolder.view as ImageCardView
cardView.tag = tvViewModel
if (!defaultFocused && item.id.value == defaultFocus) {
cardView.requestFocus()
defaultFocused = true
}
val onFocusChangeListener = View.OnFocusChangeListener { _, hasFocus ->
listener?.onItemFocusChange(item, hasFocus)
// if (hasFocus && defaultFocus == item.id.value) {
val onFocusChangeListener = View.OnFocusChangeListener { view, hasFocus ->
if (hasFocus) {
listener?.onItemHasFocus(item)
focused = cardView
startScaleAnimation(cardView, 0.9f, 1.0f, 200)
//
// if (mainFragment.shouldHasFocus(view.tag as TVViewModel)) {
// }
// if (visiable) {
// startScaleAnimation(cardView, 0.9f, 1.0f, 200)
// } else {
// visiable = true
// }
}
}
@@ -110,8 +114,16 @@ class CardAdapter(
val view = itemView
}
fun toPosition(position: Int) {
recyclerView.post {
recyclerView.scrollToPosition(position)
recyclerView.getChildAt(position)?.isSelected
recyclerView.getChildAt(position)?.requestFocus()
}
}
interface ItemListener {
fun onItemFocusChange(tvViewModel: TVViewModel, hasFocus: Boolean)
fun onItemHasFocus(tvViewModel: TVViewModel)
fun onItemClicked(tvViewModel: TVViewModel)
fun onKey(keyCode: Int): Boolean
}
@@ -72,7 +72,7 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
val adapter =
CardAdapter(
itemBinding.rowItems,
viewLifecycleOwner,
this,
tvListViewModelCurrent,
)
rowList.add(itemBinding.rowItems)
@@ -111,14 +111,6 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
itemPosition = 0
}
val tv = tvListViewModel.getTVViewModel(itemPosition)
for (i in rowList) {
if (i.tag as Int == tv?.getRowPosition()) {
((i as RecyclerView).adapter as CardAdapter).defaultFocus = itemPosition
break
}
}
tvListViewModel.setItemPosition(itemPosition)
tvListViewModel.tvListViewModel.value?.forEach { tvViewModel ->
tvViewModel.errInfo.observe(viewLifecycleOwner) { _ ->
@@ -191,20 +183,18 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
return false
}
override fun onItemFocusChange(tvViewModel: TVViewModel, hasFocus: Boolean) {
if (hasFocus) {
tvListViewModel.setItemPositionCurrent(tvViewModel.id.value!!)
override fun onItemHasFocus(tvViewModel: TVViewModel) {
tvListViewModel.setItemPositionCurrent(tvViewModel.id.value!!)
val row = tvViewModel.getRowPosition()
val row = tvViewModel.getRowPosition()
for (i in rowList) {
if (i.tag as Int != row) {
((i as RecyclerView).adapter as CardAdapter).clear()
}
for (i in rowList) {
if (i.tag as Int != row) {
((i as RecyclerView).adapter as CardAdapter).clear()
}
(activity as MainActivity).mainActive()
}
(activity as MainActivity).mainActive()
}
override fun onItemClicked(tvViewModel: TVViewModel) {
@@ -237,8 +227,6 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
itemPosition
)?.requestFocus()
}
((rowList[rowPosition] as RecyclerView).adapter as CardAdapter).defaultFocus = itemPosition
}
fun check(tvViewModel: TVViewModel): Boolean {
@@ -304,6 +292,32 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
}
}
fun shouldHasFocus(tvModel: TVViewModel):Boolean {
return tvModel == tvListViewModel.getTVViewModel(itemPosition)
}
override fun onHiddenChanged(hidden: Boolean) {
super.onHiddenChanged(hidden)
if (!hidden) {
val tvModel = tvListViewModel.getTVViewModel(itemPosition)
val rowPosition = tvModel?.getRowPosition()
val itemPosition = tvModel?.getItemPosition()
Log.i(TAG, "toPosition $rowPosition $itemPosition")
for (i in rowList) {
if (i.tag as Int == rowPosition) {
((i as RecyclerView).adapter as CardAdapter).toPosition(itemPosition!!)
break
}
}
} else {
view?.post {
// for (i in rowList) {
// ((i as RecyclerView).adapter as CardAdapter).visiable = false
// }
}
}
}
override fun onResume() {
Log.i(TAG, "onResume")
super.onResume()
@@ -147,6 +147,7 @@ object Request {
}
fun fetchVideo(tvModel: TVViewModel, cookie: String) {
Log.i(TAG, "fetchVideo with cookie")
call?.cancel()
if (::btraceRunnable.isInitialized) {
handler.removeCallbacks(btraceRunnable)
@@ -399,9 +400,9 @@ object Request {
// Log.d(TAG, "$title url $url")
tvModel.addVideoUrl(url)
tvModel.allReady()
tvModel.retryTimes = 0
tvModel.tokenRetryTimes = 0
} else {
Log.e(TAG, "auth status error")
Log.e(TAG, "auth status error ${response.code()}")
if (tvModel.tokenRetryTimes < tvModel.tokenRetryMaxTimes) {
tvModel.tokenRetryTimes++
fetchFAuth(tvModel)
@@ -420,6 +421,7 @@ object Request {
}
fun fetchData(tvModel: TVViewModel) {
Log.i(TAG, "fetchData")
if (tvModel.getTV().channel == "港澳台") {
fetchFAuth(tvModel)
return
@@ -445,7 +447,6 @@ object Request {
class TokenRunnable : Runnable {
override fun run() {
fetchToken()
handler.postDelayed(this, 600000)
}
}
@@ -1,7 +1,11 @@
package com.lizongying.mytv.api
import android.os.Build
import android.util.Log
import okhttp3.ConnectionSpec
import okhttp3.OkHttpClient
import okhttp3.TlsVersion
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.converter.protobuf.ProtoConverterFactory
@@ -68,6 +72,36 @@ class ApiClient {
.build().create(FAuthService::class.java)
}
private fun enableTls12OnPreLollipop(client: OkHttpClient.Builder): OkHttpClient.Builder {
if (Build.VERSION.SDK_INT >= 16 && Build.VERSION.SDK_INT < 22) {
try {
val sc = SSLContext.getInstance("TLSv1.2")
sc.init(null, null, null)
// a more robust version is to pass a custom X509TrustManager
// as the second parameter and make checkServerTrusted to accept your server.
// Credits: https://github.com/square/okhttp/issues/2372#issuecomment-1774955225
client.sslSocketFactory(Tls12SocketFactory(sc.socketFactory))
val cs = ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_2)
.build()
val specs: MutableList<ConnectionSpec> = ArrayList()
specs.add(cs)
specs.add(ConnectionSpec.COMPATIBLE_TLS)
specs.add(ConnectionSpec.CLEARTEXT)
client.connectionSpecs(specs)
} catch (exc: java.lang.Exception) {
Log.e("OkHttpTLSCompat", "Error while setting TLS 1.2", exc)
}
}
return client
}
private fun getUnsafeOkHttpClient(): OkHttpClient {
try {
val trustAllCerts: Array<TrustManager> = arrayOf(
@@ -93,11 +127,12 @@ class ApiClient {
val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, trustAllCerts, java.security.SecureRandom())
return OkHttpClient.Builder()
val builder = OkHttpClient.Builder()
.sslSocketFactory(sslContext.socketFactory, trustAllCerts[0] as X509TrustManager)
.hostnameVerifier { _, _ -> true }
.dns(DnsCache())
.build()
return enableTls12OnPreLollipop(builder).build()
} catch (e: Exception) {
throw RuntimeException(e)
@@ -0,0 +1,74 @@
package com.lizongying.mytv.api
import java.io.IOException
import java.net.InetAddress
import java.net.Socket
import java.net.UnknownHostException
import javax.net.ssl.SSLSocket
import javax.net.ssl.SSLSocketFactory
/**
* Enables TLS v1.2 when creating SSLSockets.
*
*
* For some reason, android supports TLS v1.2 from API 16, but enables it by
* default only from API 20.
* @link https://developer.android.com/reference/javax/net/ssl/SSLSocket.html
* @see SSLSocketFactory
*/
class Tls12SocketFactory(val delegate: SSLSocketFactory) : SSLSocketFactory() {
override fun getDefaultCipherSuites(): Array<String> {
return delegate.defaultCipherSuites
}
override fun getSupportedCipherSuites(): Array<String> {
return delegate.supportedCipherSuites
}
@Throws(IOException::class)
override fun createSocket(s: Socket, host: String, port: Int, autoClose: Boolean): Socket {
return patch(delegate.createSocket(s, host, port, autoClose))
}
@Throws(IOException::class, UnknownHostException::class)
override fun createSocket(host: String, port: Int): Socket {
return patch(delegate.createSocket(host, port))
}
@Throws(IOException::class, UnknownHostException::class)
override fun createSocket(
host: String,
port: Int,
localHost: InetAddress,
localPort: Int
): Socket {
return patch(delegate.createSocket(host, port, localHost, localPort))
}
@Throws(IOException::class)
override fun createSocket(host: InetAddress, port: Int): Socket {
return patch(delegate.createSocket(host, port))
}
@Throws(IOException::class)
override fun createSocket(
address: InetAddress,
port: Int,
localAddress: InetAddress,
localPort: Int
): Socket {
return patch(delegate.createSocket(address, port, localAddress, localPort))
}
private fun patch(s: Socket): Socket {
if (s is SSLSocket) {
s.enabledProtocols = TLS_V12_ONLY
}
return s
}
companion object {
private val TLS_V12_ONLY = arrayOf("TLSv1.2")
}
}