From 03dbd86363a61fdb0d85d71c07e7c8438b274065 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Mon, 9 Nov 2020 20:33:53 +0200 Subject: [PATCH] Fix some StrictMode warnings --- .../kotatsu/core/network/NetworkModule.kt | 4 +- .../network/cookies/PersistentCookieJar.kt | 11 +++-- .../persistence/SharedPrefsCookiePersistor.kt | 15 ++----- .../kotatsu/ui/download/DownloadService.kt | 3 +- .../ui/search/MangaSuggestionsProvider.kt | 43 ++++++++++++------- .../kotatsu/ui/search/SearchActivity.kt | 2 +- .../kotatsu/ui/search/SearchHelper.kt | 2 +- .../ui/settings/HistorySettingsFragment.kt | 32 ++++++++------ .../org/koitharu/kotatsu/utils/CacheUtils.kt | 3 ++ .../org/koitharu/kotatsu/utils/UiUtils.kt | 6 ++- 10 files changed, 72 insertions(+), 49 deletions(-) diff --git a/app/src/main/java/org/koitharu/kotatsu/core/network/NetworkModule.kt b/app/src/main/java/org/koitharu/kotatsu/core/network/NetworkModule.kt index 71e997f29..91065155f 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/network/NetworkModule.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/network/NetworkModule.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.core.network import okhttp3.CookieJar import okhttp3.OkHttpClient import org.koin.android.ext.koin.androidContext +import org.koin.core.qualifier.named import org.koin.dsl.module import org.koitharu.kotatsu.core.network.cookies.PersistentCookieJar import org.koitharu.kotatsu.core.network.cookies.cache.SetCookieCache @@ -18,13 +19,14 @@ val networkModule SharedPrefsCookiePersistor(androidContext()) ) } + single(named(CacheUtils.QUALIFIER_HTTP)) { CacheUtils.createHttpCache(androidContext()) } single { OkHttpClient.Builder().apply { connectTimeout(20, TimeUnit.SECONDS) readTimeout(60, TimeUnit.SECONDS) writeTimeout(20, TimeUnit.SECONDS) cookieJar(get()) - cache(CacheUtils.createHttpCache(androidContext())) + cache(get(named(CacheUtils.QUALIFIER_HTTP))) addInterceptor(UserAgentInterceptor()) addInterceptor(CloudFlareInterceptor()) }.build() diff --git a/app/src/main/java/org/koitharu/kotatsu/core/network/cookies/PersistentCookieJar.kt b/app/src/main/java/org/koitharu/kotatsu/core/network/cookies/PersistentCookieJar.kt index 4761051ff..437a54467 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/network/cookies/PersistentCookieJar.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/network/cookies/PersistentCookieJar.kt @@ -20,24 +20,29 @@ import okhttp3.HttpUrl import org.koitharu.kotatsu.core.network.cookies.cache.CookieCache import org.koitharu.kotatsu.core.network.cookies.persistence.CookiePersistor import java.util.* +import java.util.concurrent.atomic.AtomicBoolean class PersistentCookieJar( private val cache: CookieCache, private val persistor: CookiePersistor ) : ClearableCookieJar { - init { - cache.addAll(persistor.loadAll()) - } + private var isLoaded = AtomicBoolean(false) @Synchronized override fun saveFromResponse(url: HttpUrl, cookies: List) { + if (isLoaded.compareAndSet(false, true)) { + cache.addAll(persistor.loadAll()) + } cache.addAll(cookies) persistor.saveAll(filterPersistentCookies(cookies)) } @Synchronized override fun loadForRequest(url: HttpUrl): List { + if (isLoaded.compareAndSet(false, true)) { + cache.addAll(persistor.loadAll()) + } val cookiesToRemove: MutableList = ArrayList() val validCookies: MutableList = ArrayList() val it = cache.iterator() diff --git a/app/src/main/java/org/koitharu/kotatsu/core/network/cookies/persistence/SharedPrefsCookiePersistor.kt b/app/src/main/java/org/koitharu/kotatsu/core/network/cookies/persistence/SharedPrefsCookiePersistor.kt index 2a80a19b1..73ef2b6ea 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/network/cookies/persistence/SharedPrefsCookiePersistor.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/network/cookies/persistence/SharedPrefsCookiePersistor.kt @@ -15,22 +15,15 @@ */ package org.koitharu.kotatsu.core.network.cookies.persistence -import android.annotation.SuppressLint import android.content.Context -import android.content.SharedPreferences import okhttp3.Cookie import java.util.* -@SuppressLint("CommitPrefEdits") -class SharedPrefsCookiePersistor(private val sharedPreferences: SharedPreferences) : - CookiePersistor { +class SharedPrefsCookiePersistor(context: Context) : CookiePersistor { - constructor(context: Context) : this( - context.getSharedPreferences( - "cookies", - Context.MODE_PRIVATE - ) - ) + private val sharedPreferences by lazy { + context.getSharedPreferences("cookies", Context.MODE_PRIVATE) + } override fun loadAll(): List { val cookies: MutableList = ArrayList(sharedPreferences.all.size) diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/download/DownloadService.kt b/app/src/main/java/org/koitharu/kotatsu/ui/download/DownloadService.kt index 379bc0715..5aef01327 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/download/DownloadService.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/download/DownloadService.kt @@ -16,6 +16,7 @@ import okhttp3.Request import okio.IOException import org.koin.android.ext.android.get import org.koin.android.ext.android.inject +import org.koin.core.context.GlobalContext import org.koitharu.kotatsu.BuildConfig import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.local.PagesCache @@ -231,7 +232,7 @@ class DownloadService : BaseService() { private fun confirmDataTransfer(context: Context, callback: () -> Unit) { val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager - val settings = AppSettings(context) + val settings = GlobalContext.get().get() if (cm.isActiveNetworkMetered && settings.isTrafficWarningEnabled) { CheckBoxAlertDialog.Builder(context) .setTitle(R.string.warning) diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/search/MangaSuggestionsProvider.kt b/app/src/main/java/org/koitharu/kotatsu/ui/search/MangaSuggestionsProvider.kt index 5da084743..81627a063 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/search/MangaSuggestionsProvider.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/search/MangaSuggestionsProvider.kt @@ -12,6 +12,10 @@ import android.view.View import android.view.ViewGroup import android.widget.TextView import androidx.cursoradapter.widget.CursorAdapter +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.koitharu.kotatsu.BuildConfig import org.koitharu.kotatsu.R @@ -50,27 +54,35 @@ class MangaSuggestionsProvider : SearchRecentSuggestionsProvider() { private const val AUTHORITY = "${BuildConfig.APPLICATION_ID}.MangaSuggestionsProvider" private const val MODE = DATABASE_MODE_QUERIES - @JvmStatic private val uri = Uri.Builder() .scheme(ContentResolver.SCHEME_CONTENT) .authority(AUTHORITY) .appendPath(SearchManager.SUGGEST_URI_PATH_QUERY) .build() - @JvmStatic private val projection = arrayOf("_id", SearchManager.SUGGEST_COLUMN_QUERY) - @JvmStatic - fun saveQuery(context: Context, query: String) { - SearchRecentSuggestions( - context, - AUTHORITY, - MODE - ).saveRecentQuery(query, null) + fun saveQueryAsync(context: Context, query: String) { + GlobalScope.launch(Dispatchers.IO) { + saveQuery(context, query) + } } - @JvmStatic - fun clearHistory(context: Context) { + fun saveQuery(context: Context, query: String) { + runCatching { + SearchRecentSuggestions( + context, + AUTHORITY, + MODE + ).saveRecentQuery(query, null) + }.onFailure { + if (BuildConfig.DEBUG) { + it.printStackTrace() + } + } + } + + suspend fun clearHistory(context: Context) = withContext(Dispatchers.IO) { SearchRecentSuggestions( context, AUTHORITY, @@ -78,15 +90,15 @@ class MangaSuggestionsProvider : SearchRecentSuggestionsProvider() { ).clearHistory() } - @JvmStatic - fun getItemsCount(context: Context) = getCursor(context)?.use { it.count } ?: 0 + suspend fun getItemsCount(context: Context) = withContext(Dispatchers.IO) { + getCursor(context)?.use { it.count } ?: 0 + } - @JvmStatic private fun getCursor(context: Context): Cursor? { return context.contentResolver?.query(uri, projection, null, arrayOf(""), null) } - @JvmStatic + @Deprecated("Need async implementation") fun getSuggestionAdapter(context: Context): CursorAdapter? = getCursor( context )?.let { cursor -> @@ -102,6 +114,5 @@ class MangaSuggestionsProvider : SearchRecentSuggestionsProvider() { } } } - } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/search/SearchActivity.kt b/app/src/main/java/org/koitharu/kotatsu/ui/search/SearchActivity.kt index 681aed11b..89ea79c3d 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/search/SearchActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/search/SearchActivity.kt @@ -50,7 +50,7 @@ class SearchActivity : BaseActivity(), SearchView.OnQueryTextListener { .replace(R.id.container, SearchFragment.newInstance(source, query)) .commit() searchView.clearFocus() - MangaSuggestionsProvider.saveQuery(this, query) + MangaSuggestionsProvider.saveQueryAsync(applicationContext, query) true } else false } diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/search/SearchHelper.kt b/app/src/main/java/org/koitharu/kotatsu/ui/search/SearchHelper.kt index 7ebb87dd8..e408fdc7d 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/search/SearchHelper.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/search/SearchHelper.kt @@ -30,7 +30,7 @@ object SearchHelper { override fun onQueryTextSubmit(query: String?): Boolean { return if (!query.isNullOrBlank()) { context.startActivity(GlobalSearchActivity.newIntent(context, query.trim())) - MangaSuggestionsProvider.saveQuery(context, query) + MangaSuggestionsProvider.saveQueryAsync(context.applicationContext, query) true } else false } diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/settings/HistorySettingsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/ui/settings/HistorySettingsFragment.kt index 2fc84f2ef..0343ac7e1 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/settings/HistorySettingsFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/settings/HistorySettingsFragment.kt @@ -45,14 +45,18 @@ class HistorySettingsFragment : BasePreferenceFragment(R.string.history_and_cach pref.summary = FileSizeUtils.formatBytes(pref.context, size) } } - findPreference(AppSettings.KEY_SEARCH_HISTORY_CLEAR)?.let { p -> - val items = MangaSuggestionsProvider.getItemsCount(p.context) - p.summary = p.context.resources.getQuantityString(R.plurals.items, items, items) + findPreference(AppSettings.KEY_SEARCH_HISTORY_CLEAR)?.let { pref -> + viewLifecycleScope.launchWhenResumed { + val items = MangaSuggestionsProvider.getItemsCount(pref.context) + pref.summary = + pref.context.resources.getQuantityString(R.plurals.items, items, items) + } } - findPreference(AppSettings.KEY_UPDATES_FEED_CLEAR)?.let { p -> + findPreference(AppSettings.KEY_UPDATES_FEED_CLEAR)?.let { pref -> viewLifecycleScope.launchWhenResumed { val items = trackerRepo.count() - p.summary = p.context.resources.getQuantityString(R.plurals.items, items, items) + pref.summary = + pref.context.resources.getQuantityString(R.plurals.items, items, items) } } } @@ -68,14 +72,16 @@ class HistorySettingsFragment : BasePreferenceFragment(R.string.history_and_cach true } AppSettings.KEY_SEARCH_HISTORY_CLEAR -> { - MangaSuggestionsProvider.clearHistory(preference.context) - preference.summary = preference.context.resources - .getQuantityString(R.plurals.items, 0, 0) - Snackbar.make( - view ?: return true, - R.string.search_history_cleared, - Snackbar.LENGTH_SHORT - ).show() + viewLifecycleScope.launch { + MangaSuggestionsProvider.clearHistory(preference.context) + preference.summary = preference.context.resources + .getQuantityString(R.plurals.items, 0, 0) + Snackbar.make( + view ?: return@launch, + R.string.search_history_cleared, + Snackbar.LENGTH_SHORT + ).show() + } true } AppSettings.KEY_UPDATES_FEED_CLEAR -> { diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/CacheUtils.kt b/app/src/main/java/org/koitharu/kotatsu/utils/CacheUtils.kt index a1cd681b8..0c8848086 100644 --- a/app/src/main/java/org/koitharu/kotatsu/utils/CacheUtils.kt +++ b/app/src/main/java/org/koitharu/kotatsu/utils/CacheUtils.kt @@ -12,6 +12,8 @@ import java.io.File object CacheUtils { + const val QUALIFIER_HTTP = "cache_http" + val CONTROL_DISABLED = CacheControl.Builder() .noStore() .build() @@ -30,6 +32,7 @@ object CacheUtils { .map { it.sub(name) } .forEach { it.deleteRecursively() } + // FIXME need async implementation fun createHttpCache(context: Context): Cache { val directory = (context.externalCacheDir ?: context.cacheDir).sub("http") directory.mkdirs() diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/UiUtils.kt b/app/src/main/java/org/koitharu/kotatsu/utils/UiUtils.kt index d2c93ba5a..adf8ab4e3 100644 --- a/app/src/main/java/org/koitharu/kotatsu/utils/UiUtils.kt +++ b/app/src/main/java/org/koitharu/kotatsu/utils/UiUtils.kt @@ -4,16 +4,18 @@ import android.content.Context import android.view.View import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView +import org.koin.core.component.KoinComponent +import org.koin.core.component.get import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.utils.ext.measureWidth import kotlin.math.abs import kotlin.math.roundToInt -object UiUtils { +object UiUtils : KoinComponent { fun resolveGridSpanCount(context: Context, width: Int = 0): Int { - val scaleFactor = AppSettings(context).gridSize / 100f + val scaleFactor = get().gridSize / 100f val cellWidth = context.resources.getDimension(R.dimen.preferred_grid_width) * scaleFactor val screenWidth = (if (width <= 0) { context.resources.displayMetrics.widthPixels