Limit cache max-age and action to clear cache manually
This commit is contained in:
@@ -23,6 +23,7 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.SharedFlow
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
import kotlinx.coroutines.flow.asSharedFlow
|
import kotlinx.coroutines.flow.asSharedFlow
|
||||||
|
import okhttp3.Cache
|
||||||
import okhttp3.CookieJar
|
import okhttp3.CookieJar
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import org.koitharu.kotatsu.BuildConfig
|
import org.koitharu.kotatsu.BuildConfig
|
||||||
@@ -88,14 +89,19 @@ interface AppModule {
|
|||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideOkHttpClient(
|
fun provideHttpCache(
|
||||||
localStorageManager: LocalStorageManager,
|
localStorageManager: LocalStorageManager,
|
||||||
|
): Cache = localStorageManager.createHttpCache()
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun provideOkHttpClient(
|
||||||
|
cache: Cache,
|
||||||
commonHeadersInterceptor: CommonHeadersInterceptor,
|
commonHeadersInterceptor: CommonHeadersInterceptor,
|
||||||
mirrorSwitchInterceptor: MirrorSwitchInterceptor,
|
mirrorSwitchInterceptor: MirrorSwitchInterceptor,
|
||||||
cookieJar: CookieJar,
|
cookieJar: CookieJar,
|
||||||
settings: AppSettings,
|
settings: AppSettings,
|
||||||
): OkHttpClient {
|
): OkHttpClient {
|
||||||
val cache = localStorageManager.createHttpCache()
|
|
||||||
return OkHttpClient.Builder().apply {
|
return OkHttpClient.Builder().apply {
|
||||||
connectTimeout(20, TimeUnit.SECONDS)
|
connectTimeout(20, TimeUnit.SECONDS)
|
||||||
readTimeout(60, TimeUnit.SECONDS)
|
readTimeout(60, TimeUnit.SECONDS)
|
||||||
@@ -106,6 +112,7 @@ interface AppModule {
|
|||||||
bypassSSLErrors()
|
bypassSSLErrors()
|
||||||
}
|
}
|
||||||
cache(cache)
|
cache(cache)
|
||||||
|
addNetworkInterceptor(CacheLimitInterceptor())
|
||||||
addInterceptor(GZipInterceptor())
|
addInterceptor(GZipInterceptor())
|
||||||
addInterceptor(commonHeadersInterceptor)
|
addInterceptor(commonHeadersInterceptor)
|
||||||
addInterceptor(CloudFlareInterceptor())
|
addInterceptor(CloudFlareInterceptor())
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package org.koitharu.kotatsu.core.network
|
||||||
|
|
||||||
|
import okhttp3.CacheControl
|
||||||
|
import okhttp3.Interceptor
|
||||||
|
import okhttp3.Response
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
class CacheLimitInterceptor : Interceptor {
|
||||||
|
|
||||||
|
private val defaultMaxAge = TimeUnit.HOURS.toSeconds(1)
|
||||||
|
private val defaultCacheControl = CacheControl.Builder()
|
||||||
|
.maxAge(defaultMaxAge.toInt(), TimeUnit.SECONDS)
|
||||||
|
.build()
|
||||||
|
.toString()
|
||||||
|
|
||||||
|
override fun intercept(chain: Interceptor.Chain): Response {
|
||||||
|
val response = chain.proceed(chain.request())
|
||||||
|
val responseCacheControl = CacheControl.parse(response.headers)
|
||||||
|
if (responseCacheControl.noStore || responseCacheControl.maxAgeSeconds <= defaultMaxAge) {
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
return response.newBuilder()
|
||||||
|
.header(CommonHeaders.CACHE_CONTROL, defaultCacheControl)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@ object CommonHeaders {
|
|||||||
const val CONTENT_ENCODING = "Content-Encoding"
|
const val CONTENT_ENCODING = "Content-Encoding"
|
||||||
const val ACCEPT_ENCODING = "Accept-Encoding"
|
const val ACCEPT_ENCODING = "Accept-Encoding"
|
||||||
const val AUTHORIZATION = "Authorization"
|
const val AUTHORIZATION = "Authorization"
|
||||||
|
const val CACHE_CONTROL = "Cache-Control"
|
||||||
|
|
||||||
val CACHE_CONTROL_NO_STORE: CacheControl
|
val CACHE_CONTROL_NO_STORE: CacheControl
|
||||||
get() = CacheControl.Builder().noStore().build()
|
get() = CacheControl.Builder().noStore().build()
|
||||||
|
|||||||
@@ -345,6 +345,7 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
|
|||||||
const val KEY_SOURCES_HIDDEN = "sources_hidden"
|
const val KEY_SOURCES_HIDDEN = "sources_hidden"
|
||||||
const val KEY_TRAFFIC_WARNING = "traffic_warning"
|
const val KEY_TRAFFIC_WARNING = "traffic_warning"
|
||||||
const val KEY_PAGES_CACHE_CLEAR = "pages_cache_clear"
|
const val KEY_PAGES_CACHE_CLEAR = "pages_cache_clear"
|
||||||
|
const val KEY_HTTP_CACHE_CLEAR = "http_cache_clear"
|
||||||
const val KEY_COOKIES_CLEAR = "cookies_clear"
|
const val KEY_COOKIES_CLEAR = "cookies_clear"
|
||||||
const val KEY_THUMBS_CACHE_CLEAR = "thumbs_cache_clear"
|
const val KEY_THUMBS_CACHE_CLEAR = "thumbs_cache_clear"
|
||||||
const val KEY_SEARCH_HISTORY_CLEAR = "search_history_clear"
|
const val KEY_SEARCH_HISTORY_CLEAR = "search_history_clear"
|
||||||
|
|||||||
@@ -8,7 +8,10 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.runInterruptible
|
||||||
|
import okhttp3.Cache
|
||||||
import org.koitharu.kotatsu.R
|
import org.koitharu.kotatsu.R
|
||||||
import org.koitharu.kotatsu.base.ui.BasePreferenceFragment
|
import org.koitharu.kotatsu.base.ui.BasePreferenceFragment
|
||||||
import org.koitharu.kotatsu.core.network.cookies.MutableCookieJar
|
import org.koitharu.kotatsu.core.network.cookies.MutableCookieJar
|
||||||
@@ -39,6 +42,9 @@ class HistorySettingsFragment : BasePreferenceFragment(R.string.history_and_cach
|
|||||||
@Inject
|
@Inject
|
||||||
lateinit var cookieJar: MutableCookieJar
|
lateinit var cookieJar: MutableCookieJar
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var cache: Cache
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var shortcutsUpdater: ShortcutsUpdater
|
lateinit var shortcutsUpdater: ShortcutsUpdater
|
||||||
|
|
||||||
@@ -52,6 +58,7 @@ class HistorySettingsFragment : BasePreferenceFragment(R.string.history_and_cach
|
|||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
findPreference<Preference>(AppSettings.KEY_PAGES_CACHE_CLEAR)?.bindSummaryToCacheSize(CacheDir.PAGES)
|
findPreference<Preference>(AppSettings.KEY_PAGES_CACHE_CLEAR)?.bindSummaryToCacheSize(CacheDir.PAGES)
|
||||||
findPreference<Preference>(AppSettings.KEY_THUMBS_CACHE_CLEAR)?.bindSummaryToCacheSize(CacheDir.THUMBS)
|
findPreference<Preference>(AppSettings.KEY_THUMBS_CACHE_CLEAR)?.bindSummaryToCacheSize(CacheDir.THUMBS)
|
||||||
|
findPreference<Preference>(AppSettings.KEY_HTTP_CACHE_CLEAR)?.bindSummaryToHttpCacheSize()
|
||||||
findPreference<Preference>(AppSettings.KEY_SEARCH_HISTORY_CLEAR)?.let { pref ->
|
findPreference<Preference>(AppSettings.KEY_SEARCH_HISTORY_CLEAR)?.let { pref ->
|
||||||
viewLifecycleScope.launch {
|
viewLifecycleScope.launch {
|
||||||
lifecycle.awaitStateAtLeast(Lifecycle.State.RESUMED)
|
lifecycle.awaitStateAtLeast(Lifecycle.State.RESUMED)
|
||||||
@@ -90,6 +97,11 @@ class HistorySettingsFragment : BasePreferenceFragment(R.string.history_and_cach
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AppSettings.KEY_HTTP_CACHE_CLEAR -> {
|
||||||
|
clearHttpCache()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
AppSettings.KEY_UPDATES_FEED_CLEAR -> {
|
AppSettings.KEY_UPDATES_FEED_CLEAR -> {
|
||||||
viewLifecycleScope.launch {
|
viewLifecycleScope.launch {
|
||||||
trackerRepo.clearLogs()
|
trackerRepo.clearLogs()
|
||||||
@@ -131,6 +143,32 @@ class HistorySettingsFragment : BasePreferenceFragment(R.string.history_and_cach
|
|||||||
summary = FileSize.BYTES.format(context, size)
|
summary = FileSize.BYTES.format(context, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun Preference.bindSummaryToHttpCacheSize() = viewLifecycleScope.launch {
|
||||||
|
val size = runInterruptible(Dispatchers.IO) { cache.size() }
|
||||||
|
summary = FileSize.BYTES.format(context, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun clearHttpCache() {
|
||||||
|
val preference = findPreference<Preference>(AppSettings.KEY_HTTP_CACHE_CLEAR) ?: return
|
||||||
|
val ctx = preference.context.applicationContext
|
||||||
|
viewLifecycleScope.launch {
|
||||||
|
try {
|
||||||
|
preference.isEnabled = false
|
||||||
|
val size = runInterruptible(Dispatchers.IO) {
|
||||||
|
cache.evictAll()
|
||||||
|
cache.size()
|
||||||
|
}
|
||||||
|
preference.summary = FileSize.BYTES.format(ctx, size)
|
||||||
|
} catch (e: CancellationException) {
|
||||||
|
throw e
|
||||||
|
} catch (e: Exception) {
|
||||||
|
preference.summary = e.getDisplayMessage(ctx.resources)
|
||||||
|
} finally {
|
||||||
|
preference.isEnabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun clearSearchHistory(preference: Preference) {
|
private fun clearSearchHistory(preference: Preference) {
|
||||||
MaterialAlertDialogBuilder(context ?: return)
|
MaterialAlertDialogBuilder(context ?: return)
|
||||||
.setTitle(R.string.clear_search_history)
|
.setTitle(R.string.clear_search_history)
|
||||||
|
|||||||
@@ -417,4 +417,5 @@
|
|||||||
<string name="translations">Translations</string>
|
<string name="translations">Translations</string>
|
||||||
<string name="web_view_unavailable">WebView not available: check if WebView provider is installed</string>
|
<string name="web_view_unavailable">WebView not available: check if WebView provider is installed</string>
|
||||||
<string name="invalid_value_message">Invalid value</string>
|
<string name="invalid_value_message">Invalid value</string>
|
||||||
|
<string name="clear_network_cache">Clear network cache</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -39,6 +39,12 @@
|
|||||||
android:summary="@string/computing_"
|
android:summary="@string/computing_"
|
||||||
android:title="@string/clear_pages_cache" />
|
android:title="@string/clear_pages_cache" />
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
android:key="http_cache_clear"
|
||||||
|
android:persistent="false"
|
||||||
|
android:summary="@string/loading_"
|
||||||
|
android:title="@string/clear_network_cache" />
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
android:key="cookies_clear"
|
android:key="cookies_clear"
|
||||||
android:persistent="false"
|
android:persistent="false"
|
||||||
|
|||||||
Reference in New Issue
Block a user