Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -95,11 +95,17 @@ interface BaseSource : JsExtensions {
|
|||||||
* 保存登录头部信息,map格式,访问时自动添加
|
* 保存登录头部信息,map格式,访问时自动添加
|
||||||
*/
|
*/
|
||||||
fun putLoginHeader(header: String) {
|
fun putLoginHeader(header: String) {
|
||||||
|
val headerMap = GSON.fromJsonObject<Map<String, String>>(header).getOrNull()
|
||||||
|
val cookie = headerMap?.get("Cookie") ?: headerMap?.get("cookie")
|
||||||
|
cookie?.let {
|
||||||
|
CookieStore.replaceCookie(getKey(), it)
|
||||||
|
}
|
||||||
CacheManager.put("loginHeader_${getKey()}", header)
|
CacheManager.put("loginHeader_${getKey()}", header)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeLoginHeader() {
|
fun removeLoginHeader() {
|
||||||
CacheManager.delete("loginHeader_${getKey()}")
|
CacheManager.delete("loginHeader_${getKey()}")
|
||||||
|
CookieStore.removeCookie(getKey())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import splitties.init.appCtx
|
|||||||
object CacheManager {
|
object CacheManager {
|
||||||
|
|
||||||
private val queryTTFMap = hashMapOf<String, Pair<Long, QueryTTF>>()
|
private val queryTTFMap = hashMapOf<String, Pair<Long, QueryTTF>>()
|
||||||
private val memoryLruCache = object : LruCache<String, Cache>(100) {}
|
private val memoryLruCache = object : LruCache<String, String>(100) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* saveTime 单位为秒
|
* saveTime 单位为秒
|
||||||
@@ -25,15 +25,23 @@ object CacheManager {
|
|||||||
is ByteArray -> ACache.get(appCtx).put(key, value, saveTime)
|
is ByteArray -> ACache.get(appCtx).put(key, value, saveTime)
|
||||||
else -> {
|
else -> {
|
||||||
val cache = Cache(key, value.toString(), deadline)
|
val cache = Cache(key, value.toString(), deadline)
|
||||||
memoryLruCache.put(key, cache)
|
putMemory(key, value.toString())
|
||||||
appDb.cacheDao.insert(cache)
|
appDb.cacheDao.insert(cache)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun putMemory(key: String, value: Any) {
|
fun putMemory(key: String, value: String) {
|
||||||
val cache = Cache(key, value.toString(), 0)
|
memoryLruCache.put(key, value)
|
||||||
memoryLruCache.put(key, cache)
|
}
|
||||||
|
|
||||||
|
//从内存中获取数据 使用lruCache
|
||||||
|
fun getFromMemory(key: String): String? {
|
||||||
|
return memoryLruCache.get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deleteMemory(key: String) {
|
||||||
|
memoryLruCache.remove(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun get(key: String): String? {
|
fun get(key: String): String? {
|
||||||
@@ -42,24 +50,12 @@ object CacheManager {
|
|||||||
}
|
}
|
||||||
val cache = appDb.cacheDao.get(key)
|
val cache = appDb.cacheDao.get(key)
|
||||||
if (cache != null && (cache.deadline == 0L || cache.deadline > System.currentTimeMillis())) {
|
if (cache != null && (cache.deadline == 0L || cache.deadline > System.currentTimeMillis())) {
|
||||||
memoryLruCache.put(key, cache)
|
putMemory(key, cache.value ?: "")
|
||||||
return cache.value
|
return cache.value
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
//从内存中获取数据 使用lruCache 支持过期功能
|
|
||||||
private fun getFromMemory(key: String): String? {
|
|
||||||
val cache = memoryLruCache.get(key) ?: return null
|
|
||||||
val deadline = cache.deadline
|
|
||||||
return if (deadline == 0L || deadline > System.currentTimeMillis()) {
|
|
||||||
cache.value
|
|
||||||
} else {
|
|
||||||
memoryLruCache.remove(key)
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getInt(key: String): Int? {
|
fun getInt(key: String): Int? {
|
||||||
return get(key)?.toIntOrNull()
|
return get(key)?.toIntOrNull()
|
||||||
}
|
}
|
||||||
@@ -100,7 +96,7 @@ object CacheManager {
|
|||||||
|
|
||||||
fun delete(key: String) {
|
fun delete(key: String) {
|
||||||
appDb.cacheDao.delete(key)
|
appDb.cacheDao.delete(key)
|
||||||
memoryLruCache.remove(key)
|
deleteMemory(key)
|
||||||
ACache.get(appCtx).remove(key)
|
ACache.get(appCtx).remove(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8,9 +8,11 @@ import com.bumptech.glide.load.data.DataFetcher
|
|||||||
import com.bumptech.glide.load.model.GlideUrl
|
import com.bumptech.glide.load.model.GlideUrl
|
||||||
import com.bumptech.glide.util.ContentLengthInputStream
|
import com.bumptech.glide.util.ContentLengthInputStream
|
||||||
import com.bumptech.glide.util.Preconditions
|
import com.bumptech.glide.util.Preconditions
|
||||||
|
import io.legado.app.constant.AppConst
|
||||||
import io.legado.app.data.appDb
|
import io.legado.app.data.appDb
|
||||||
import io.legado.app.exception.NoStackTraceException
|
import io.legado.app.exception.NoStackTraceException
|
||||||
import io.legado.app.help.http.okHttpClient
|
import io.legado.app.help.http.okHttpClient
|
||||||
|
import io.legado.app.help.http.addHeaders
|
||||||
import io.legado.app.utils.isWifiConnect
|
import io.legado.app.utils.isWifiConnect
|
||||||
import okhttp3.Call
|
import okhttp3.Call
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
@@ -37,16 +39,19 @@ class OkHttpStreamFetcher(private val url: GlideUrl, private val options: Option
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
val requestBuilder: Request.Builder = Request.Builder().url(url.toStringUrl())
|
val requestBuilder: Request.Builder = Request.Builder().url(url.toStringUrl())
|
||||||
|
val headerMap = HashMap<String, String>()
|
||||||
|
|
||||||
options.get(OkHttpModelLoader.sourceOriginOption)?.let { sourceUrl ->
|
options.get(OkHttpModelLoader.sourceOriginOption)?.let { sourceUrl ->
|
||||||
val source = appDb.bookSourceDao.getBookSource(sourceUrl)
|
val source = appDb.bookSourceDao.getBookSource(sourceUrl)
|
||||||
?: appDb.rssSourceDao.getByKey(sourceUrl)
|
?: appDb.rssSourceDao.getByKey(sourceUrl)
|
||||||
source?.getHeaderMap(true)?.forEach {
|
source?.getHeaderMap(true)?.let {
|
||||||
requestBuilder.addHeader(it.key, it.value)
|
headerMap.putAll(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for ((key, value) in url.headers.entries) {
|
for ((key, value) in url.headers.entries) {
|
||||||
requestBuilder.addHeader(key, value)
|
headerMap.put(key, value)
|
||||||
}
|
}
|
||||||
|
requestBuilder.addHeaders(headerMap)
|
||||||
val request: Request = requestBuilder.build()
|
val request: Request = requestBuilder.build()
|
||||||
this.callback = callback
|
this.callback = callback
|
||||||
call = okHttpClient.newCall(request)
|
call = okHttpClient.newCall(request)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import android.text.TextUtils
|
|||||||
import io.legado.app.data.appDb
|
import io.legado.app.data.appDb
|
||||||
import io.legado.app.data.entities.Cookie
|
import io.legado.app.data.entities.Cookie
|
||||||
import io.legado.app.help.http.api.CookieManager
|
import io.legado.app.help.http.api.CookieManager
|
||||||
|
import io.legado.app.help.CacheManager
|
||||||
import io.legado.app.utils.NetworkUtils
|
import io.legado.app.utils.NetworkUtils
|
||||||
|
|
||||||
object CookieStore : CookieManager {
|
object CookieStore : CookieManager {
|
||||||
@@ -14,7 +15,9 @@ object CookieStore : CookieManager {
|
|||||||
*保存cookie到数据库,会自动识别url的二级域名
|
*保存cookie到数据库,会自动识别url的二级域名
|
||||||
*/
|
*/
|
||||||
override fun setCookie(url: String, cookie: String?) {
|
override fun setCookie(url: String, cookie: String?) {
|
||||||
val cookieBean = Cookie(NetworkUtils.getSubDomain(url), cookie ?: "")
|
val domain = NetworkUtils.getSubDomain(url)
|
||||||
|
CacheManager.putMemory("${domain}_cookie", cookie ?: "")
|
||||||
|
val cookieBean = Cookie(domain, cookie ?: "")
|
||||||
appDb.cookieDao.insert(cookieBean)
|
appDb.cookieDao.insert(cookieBean)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,8 +40,12 @@ object CookieStore : CookieManager {
|
|||||||
*获取url所属的二级域名的cookie
|
*获取url所属的二级域名的cookie
|
||||||
*/
|
*/
|
||||||
override fun getCookie(url: String): String {
|
override fun getCookie(url: String): String {
|
||||||
val cookieBean = appDb.cookieDao.get(NetworkUtils.getSubDomain(url))
|
val domain = NetworkUtils.getSubDomain(url)
|
||||||
return cookieBean?.cookie ?: ""
|
CacheManager.getFromMemory("${domain}_cookie")?.let { return it }
|
||||||
|
val cookieBean = appDb.cookieDao.get(domain)
|
||||||
|
val cookie = cookieBean?.cookie ?: ""
|
||||||
|
CacheManager.putMemory(url, cookie ?: "")
|
||||||
|
return cookie
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getKey(url: String, key: String): String {
|
fun getKey(url: String, key: String): String {
|
||||||
@@ -48,7 +55,9 @@ object CookieStore : CookieManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun removeCookie(url: String) {
|
override fun removeCookie(url: String) {
|
||||||
appDb.cookieDao.delete(NetworkUtils.getSubDomain(url))
|
val domain = NetworkUtils.getSubDomain(url)
|
||||||
|
CacheManager.deleteMemory("${domain}_cookie")
|
||||||
|
appDb.cookieDao.delete(domain)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun cookieToMap(cookie: String): MutableMap<String, String> {
|
override fun cookieToMap(cookie: String): MutableMap<String, String> {
|
||||||
|
|||||||
@@ -4,10 +4,7 @@ import io.legado.app.constant.AppConst
|
|||||||
import io.legado.app.help.config.AppConfig
|
import io.legado.app.help.config.AppConfig
|
||||||
import io.legado.app.help.http.cronet.CronetInterceptor
|
import io.legado.app.help.http.cronet.CronetInterceptor
|
||||||
import io.legado.app.help.http.cronet.CronetLoader
|
import io.legado.app.help.http.cronet.CronetLoader
|
||||||
import okhttp3.ConnectionSpec
|
import okhttp3.*
|
||||||
import okhttp3.Credentials
|
|
||||||
import okhttp3.Interceptor
|
|
||||||
import okhttp3.OkHttpClient
|
|
||||||
import java.net.InetSocketAddress
|
import java.net.InetSocketAddress
|
||||||
import java.net.Proxy
|
import java.net.Proxy
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
@@ -17,6 +14,22 @@ private val proxyClientCache: ConcurrentHashMap<String, OkHttpClient> by lazy {
|
|||||||
ConcurrentHashMap()
|
ConcurrentHashMap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val cookieJar by lazy {
|
||||||
|
object : CookieJar {
|
||||||
|
|
||||||
|
override fun loadForRequest(url: HttpUrl): List<Cookie> {
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
|
||||||
|
cookies.forEach {
|
||||||
|
CookieStore.replaceCookie(url.toString(), "${it.name}=${it.value}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val okHttpClient: OkHttpClient by lazy {
|
val okHttpClient: OkHttpClient by lazy {
|
||||||
val specs = arrayListOf(
|
val specs = arrayListOf(
|
||||||
ConnectionSpec.MODERN_TLS,
|
ConnectionSpec.MODERN_TLS,
|
||||||
@@ -29,6 +42,7 @@ val okHttpClient: OkHttpClient by lazy {
|
|||||||
.writeTimeout(15, TimeUnit.SECONDS)
|
.writeTimeout(15, TimeUnit.SECONDS)
|
||||||
.readTimeout(15, TimeUnit.SECONDS)
|
.readTimeout(15, TimeUnit.SECONDS)
|
||||||
.callTimeout(60, TimeUnit.SECONDS)
|
.callTimeout(60, TimeUnit.SECONDS)
|
||||||
|
.cookieJar(cookieJar = cookieJar)
|
||||||
.sslSocketFactory(SSLHelper.unsafeSSLSocketFactory, SSLHelper.unsafeTrustManager)
|
.sslSocketFactory(SSLHelper.unsafeSSLSocketFactory, SSLHelper.unsafeTrustManager)
|
||||||
.retryOnConnectionFailure(true)
|
.retryOnConnectionFailure(true)
|
||||||
.hostnameVerifier(SSLHelper.unsafeHostnameVerifier)
|
.hostnameVerifier(SSLHelper.unsafeHostnameVerifier)
|
||||||
@@ -47,7 +61,7 @@ val okHttpClient: OkHttpClient by lazy {
|
|||||||
chain.proceed(builder.build())
|
chain.proceed(builder.build())
|
||||||
})
|
})
|
||||||
if (!AppConfig.isGooglePlay && AppConfig.isCronet && CronetLoader.install()) {
|
if (!AppConfig.isGooglePlay && AppConfig.isCronet && CronetLoader.install()) {
|
||||||
builder.addInterceptor(CronetInterceptor(null))
|
builder.addInterceptor(CronetInterceptor(cookieJar = cookieJar))
|
||||||
}
|
}
|
||||||
builder.build()
|
builder.build()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
package io.legado.app.help.http.cronet
|
package io.legado.app.help.http.cronet
|
||||||
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import io.legado.app.help.http.CookieStore
|
|
||||||
import io.legado.app.utils.printOnDebug
|
import io.legado.app.utils.printOnDebug
|
||||||
import okhttp3.*
|
import okhttp3.*
|
||||||
|
import okhttp3.internal.http.receiveHeaders
|
||||||
|
|
||||||
|
|
||||||
class CronetInterceptor(private val cookieJar: CookieJar?) : Interceptor {
|
class CronetInterceptor(private val cookieJar: CookieJar = CookieJar.NO_COOKIES) : Interceptor {
|
||||||
|
|
||||||
override fun intercept(chain: Interceptor.Chain): Response {
|
override fun intercept(chain: Interceptor.Chain): Response {
|
||||||
val original: Request = chain.request()
|
val original: Request = chain.request()
|
||||||
@@ -18,15 +18,18 @@ class CronetInterceptor(private val cookieJar: CookieJar?) : Interceptor {
|
|||||||
//移除Keep-Alive,手动设置会导致400 BadRequest
|
//移除Keep-Alive,手动设置会导致400 BadRequest
|
||||||
builder.removeHeader("Keep-Alive")
|
builder.removeHeader("Keep-Alive")
|
||||||
builder.removeHeader("Accept-Encoding")
|
builder.removeHeader("Accept-Encoding")
|
||||||
val cookieStr = getCookie(original.url)
|
if (cookieJar != CookieJar.NO_COOKIES) {
|
||||||
//设置Cookie
|
val cookieStr = getCookie(original.url)
|
||||||
if (cookieStr.length > 3) {
|
//设置Cookie
|
||||||
builder.header("Cookie", cookieStr)
|
if (cookieStr.length > 3) {
|
||||||
|
builder.addHeader("Cookie", cookieStr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val new = builder.build()
|
|
||||||
proceedWithCronet(new, chain.call())?.let { response ->
|
val newReq = builder.build()
|
||||||
|
proceedWithCronet(newReq, chain.call())?.let { response ->
|
||||||
//从Response 中保存Cookie到CookieJar
|
//从Response 中保存Cookie到CookieJar
|
||||||
cookieJar?.saveFromResponse(new.url, Cookie.parseAll(new.url, response.headers))
|
cookieJar.receiveHeaders(newReq.url, response.headers)
|
||||||
response
|
response
|
||||||
} ?: chain.proceed(original)
|
} ?: chain.proceed(original)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@@ -54,21 +57,14 @@ class CronetInterceptor(private val cookieJar: CookieJar?) : Interceptor {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getCookie(url: HttpUrl): String {
|
|
||||||
val sb = StringBuilder()
|
/** Returns a 'Cookie' HTTP request header with all cookies, like `a=b; c=d`. */
|
||||||
//处理从 Cookiejar 获取到的Cookies
|
private fun getCookie(url: HttpUrl): String = buildString {
|
||||||
if (cookieJar != null) {
|
val cookies = cookieJar.loadForRequest(url)
|
||||||
val cookies = cookieJar.loadForRequest(url)
|
cookies.forEachIndexed { index, cookie ->
|
||||||
for (cookie in cookies) {
|
if (index > 0) append("; ")
|
||||||
sb.append(cookie.name).append("=").append(cookie.value).append("; ")
|
append(cookie.name).append('=').append(cookie.value)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
//处理自定义的Cookie
|
|
||||||
val cookie = CookieStore.getCookie(url.toString())
|
|
||||||
if (cookie.length > 3) {
|
|
||||||
sb.append(cookie)
|
|
||||||
}
|
|
||||||
return sb.toString()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -192,9 +192,6 @@ class AnalyzeUrl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
headerMap[UA_NAME] ?: let {
|
|
||||||
headerMap[UA_NAME] = AppConfig.userAgent
|
|
||||||
}
|
|
||||||
urlNoQuery = url
|
urlNoQuery = url
|
||||||
when (method) {
|
when (method) {
|
||||||
RequestMethod.GET -> {
|
RequestMethod.GET -> {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import android.view.ViewGroup
|
|||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import androidx.appcompat.widget.Toolbar
|
import androidx.appcompat.widget.Toolbar
|
||||||
import com.bumptech.glide.request.RequestOptions
|
import com.bumptech.glide.request.RequestOptions
|
||||||
|
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
import io.legado.app.base.BaseDialogFragment
|
import io.legado.app.base.BaseDialogFragment
|
||||||
import io.legado.app.databinding.DialogVerificationCodeViewBinding
|
import io.legado.app.databinding.DialogVerificationCodeViewBinding
|
||||||
@@ -46,7 +47,6 @@ class VerificationCodeDialog() : BaseDialogFragment(R.layout.dialog_verification
|
|||||||
binding.run {
|
binding.run {
|
||||||
toolBar.setBackgroundColor(primaryColor)
|
toolBar.setBackgroundColor(primaryColor)
|
||||||
val sourceOrigin = arguments?.getString("sourceOrigin")
|
val sourceOrigin = arguments?.getString("sourceOrigin")
|
||||||
val key = "${sourceOrigin}_verificationResult"
|
|
||||||
arguments?.getString("imageUrl")?.let { imageUrl ->
|
arguments?.getString("imageUrl")?.let { imageUrl ->
|
||||||
ImageLoader.load(requireContext(), imageUrl).apply {
|
ImageLoader.load(requireContext(), imageUrl).apply {
|
||||||
sourceOrigin?.let {
|
sourceOrigin?.let {
|
||||||
@@ -58,8 +58,10 @@ class VerificationCodeDialog() : BaseDialogFragment(R.layout.dialog_verification
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}.error(R.drawable.image_loading_error)
|
}.error(R.drawable.image_loading_error)
|
||||||
.into(ivImage)
|
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||||
ivImage.setOnClickListener {
|
.skipMemoryCache(true)
|
||||||
|
.into(verificationCodeImageView)
|
||||||
|
verificationCodeImageView.setOnClickListener {
|
||||||
showDialogFragment(PhotoDialog(imageUrl, sourceOrigin))
|
showDialogFragment(PhotoDialog(imageUrl, sourceOrigin))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,11 +18,10 @@
|
|||||||
app:titleTextAppearance="@style/ToolbarTitle" />
|
app:titleTextAppearance="@style/ToolbarTitle" />
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageView
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
android:id="@+id/iv_image"
|
android:id="@+id/verification_code_image_view"
|
||||||
android:scaleType="fitXY"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="50dp"
|
android:layout_height="100dp"
|
||||||
android:padding="3dp"
|
android:scaleType="centerCrop"
|
||||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||||
tools:ignore="UnusedAttribute" />
|
tools:ignore="UnusedAttribute" />
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user