diff --git a/app/build.gradle b/app/build.gradle index f850ab3fb..63d79d558 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -16,8 +16,8 @@ android { applicationId 'org.koitharu.kotatsu' minSdk = 21 targetSdk = 35 - versionCode = 658 - versionName = '7.4.1' + versionCode = 659 + versionName = '7.4.2' generatedDensities = [] testInstrumentationRunner 'org.koitharu.kotatsu.HiltTestRunner' ksp { @@ -82,7 +82,7 @@ afterEvaluate { } dependencies { //noinspection GradleDependency - implementation('com.github.KotatsuApp:kotatsu-parsers:3b5a018f8c') { + implementation('com.github.KotatsuApp:kotatsu-parsers:ca212ca692') { exclude group: 'org.json', module: 'json' } @@ -95,7 +95,7 @@ dependencies { implementation 'androidx.activity:activity-ktx:1.9.1' implementation 'androidx.fragment:fragment-ktx:1.8.2' implementation 'androidx.transition:transition-ktx:1.5.1' - implementation 'androidx.collection:collection-ktx:1.4.2' + implementation 'androidx.collection:collection-ktx:1.4.3' implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.4' implementation 'androidx.lifecycle:lifecycle-service:2.8.4' implementation 'androidx.lifecycle:lifecycle-process:2.8.4' @@ -109,7 +109,7 @@ dependencies { implementation 'androidx.lifecycle:lifecycle-common-java8:2.8.4' implementation 'androidx.webkit:webkit:1.11.0' - implementation 'androidx.work:work-runtime:2.9.0' + implementation 'androidx.work:work-runtime:2.9.1' //noinspection GradleDependency implementation('com.google.guava:guava:32.0.1-android') { exclude group: 'com.google.guava', module: 'failureaccess' diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/prefs/AppSettings.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/prefs/AppSettings.kt index 23df27186..7ca1c78f3 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/prefs/AppSettings.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/prefs/AppSettings.kt @@ -704,6 +704,7 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) { const val KEY_LOGS_SHARE = "logs_share" const val KEY_APP_UPDATE = "app_update" const val KEY_APP_TRANSLATION = "about_app_translation" + const val PROXY_TEST = "proxy_test" // old keys are for migration only private const val KEY_IMAGES_PROXY_OLD = "images_proxy" diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/explore/data/MangaSourcesRepository.kt b/app/src/main/kotlin/org/koitharu/kotatsu/explore/data/MangaSourcesRepository.kt index e2bc04cc2..5965308b8 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/explore/data/MangaSourcesRepository.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/explore/data/MangaSourcesRepository.kt @@ -61,7 +61,13 @@ class MangaSourcesRepository @Inject constructor( suspend fun getEnabledSources(): List { assimilateNewSources() val order = settings.sourcesSortOrder - return dao.findAllEnabled(order).toSources(settings.isNsfwContentDisabled, order) + return dao.findAllEnabled(order).toSources(settings.isNsfwContentDisabled, order).let { enabled -> + val external = getExternalSources() + val list = ArrayList(enabled.size + external.size) + external.mapTo(list) { MangaSourceInfo(it, isEnabled = true, isPinned = true) } + list.addAll(enabled) + list + } } suspend fun getPinnedSources(): Set { @@ -308,8 +314,6 @@ class MangaSourcesRepository @Inject constructor( } private fun observeExternalSources(): Flow> { - val intent = Intent("app.kotatsu.parser.PROVIDE_MANGA") - val pm = context.packageManager return callbackFlow { val receiver = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { @@ -333,15 +337,19 @@ class MangaSourcesRepository @Inject constructor( }.onStart { emit(null) }.map { - pm.queryIntentContentProviders(intent, 0).map { resolveInfo -> - ExternalMangaSource( - packageName = resolveInfo.providerInfo.packageName, - authority = resolveInfo.providerInfo.authority, - ) - } + getExternalSources() }.distinctUntilChanged() } + private fun getExternalSources() = context.packageManager.queryIntentContentProviders( + Intent("app.kotatsu.parser.PROVIDE_MANGA"), 0, + ).map { resolveInfo -> + ExternalMangaSource( + packageName = resolveInfo.providerInfo.packageName, + authority = resolveInfo.providerInfo.authority, + ) + } + private fun List.toSources( skipNsfwSources: Boolean, sortOrder: SourcesSortOrder?, diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonScalingFrame.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonScalingFrame.kt index 8393aacaf..4ea9f134a 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonScalingFrame.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonScalingFrame.kt @@ -176,8 +176,10 @@ class WebtoonScalingFrame @JvmOverloads constructor( val targetChild = findTargetChild() adjustBounds() targetChild.run { - scaleX = scale - scaleY = scale + if (!scale.isNaN()) { + scaleX = scale + scaleY = scale + } translationX = transX translationY = transY if (pendingScroll != 0) { @@ -298,7 +300,7 @@ class WebtoonScalingFrame @JvmOverloads constructor( distanceX: Float, distanceY: Float, ): Boolean { - if (scale <= 1f) return false + if (scale <= 1f || scale.isNaN()) return false transformMatrix.postTranslate(-distanceX, -distanceY) invalidateTarget() return true @@ -323,7 +325,7 @@ class WebtoonScalingFrame @JvmOverloads constructor( velocityX: Float, velocityY: Float, ): Boolean { - if (scale <= 1) return false + if (scale <= 1 || scale.isNaN()) return false prevPos.set(transX.toInt(), transY.toInt()) overScroller.fling( diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/ProxySettingsFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/ProxySettingsFragment.kt index 4125d46e2..188b9f28c 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/ProxySettingsFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/ProxySettingsFragment.kt @@ -7,20 +7,40 @@ import android.view.inputmethod.EditorInfo import androidx.preference.EditTextPreference import androidx.preference.Preference import androidx.preference.PreferenceCategory +import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import okhttp3.OkHttpClient +import okhttp3.Request import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.network.BaseHttpClient import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.ui.BasePreferenceFragment +import org.koitharu.kotatsu.core.util.ext.getDisplayMessage +import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug +import org.koitharu.kotatsu.core.util.ext.viewLifecycleScope +import org.koitharu.kotatsu.parsers.util.await import org.koitharu.kotatsu.settings.utils.EditTextBindListener import org.koitharu.kotatsu.settings.utils.PasswordSummaryProvider import org.koitharu.kotatsu.settings.utils.validation.DomainValidator import org.koitharu.kotatsu.settings.utils.validation.PortNumberValidator import java.net.Proxy +import javax.inject.Inject +import kotlin.coroutines.cancellation.CancellationException @AndroidEntryPoint class ProxySettingsFragment : BasePreferenceFragment(R.string.proxy), SharedPreferences.OnSharedPreferenceChangeListener { + private var testJob: Job? = null + + @Inject + @BaseHttpClient + lateinit var okHttpClient: OkHttpClient + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { addPreferencesFromResource(R.xml.pref_proxy) findPreference(AppSettings.KEY_PROXY_ADDRESS)?.setOnBindEditTextListener( @@ -60,6 +80,15 @@ class ProxySettingsFragment : BasePreferenceFragment(R.string.proxy), super.onDestroyView() } + override fun onPreferenceTreeClick(preference: Preference): Boolean = when (preference.key) { + AppSettings.PROXY_TEST -> { + testConnection() + true + } + + else -> super.onPreferenceTreeClick(preference) + } + override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { when (key) { AppSettings.KEY_PROXY_TYPE -> updateDependencies() @@ -73,5 +102,47 @@ class ProxySettingsFragment : BasePreferenceFragment(R.string.proxy), findPreference(AppSettings.KEY_PROXY_AUTH)?.isEnabled = isProxyEnabled findPreference(AppSettings.KEY_PROXY_LOGIN)?.isEnabled = isProxyEnabled findPreference(AppSettings.KEY_PROXY_PASSWORD)?.isEnabled = isProxyEnabled + findPreference(AppSettings.PROXY_TEST)?.isEnabled = isProxyEnabled && testJob?.isActive != true + } + + private fun testConnection() { + testJob?.cancel() + testJob = viewLifecycleScope.launch { + val pref = findPreference(AppSettings.PROXY_TEST) + pref?.run { + setSummary(R.string.loading_) + isEnabled = false + } + try { + withContext(Dispatchers.Default) { + val request = Request.Builder() + .get() + .url("http://neverssl.com") + .build() + val response = okHttpClient.newCall(request).await() + check(response.isSuccessful) { response.message } + } + showTestResult(null) + } catch (e: CancellationException) { + throw e + } catch (e: Throwable) { + e.printStackTraceDebug() + showTestResult(e) + } finally { + pref?.run { + isEnabled = true + summary = null + } + } + } + } + + private fun showTestResult(error: Throwable?) { + MaterialAlertDialogBuilder(requireContext()) + .setTitle(R.string.proxy) + .setMessage(error?.getDisplayMessage(resources) ?: getString(R.string.connection_ok)) + .setPositiveButton(android.R.string.ok, null) + .setCancelable(true) + .show() } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/updates/UpdatesFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/updates/UpdatesFragment.kt index 73f562648..8fe56b9a3 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/updates/UpdatesFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/updates/UpdatesFragment.kt @@ -26,6 +26,7 @@ class UpdatesFragment : MangaListFragment() { return when (item.itemId) { R.id.action_remove -> { viewModel.remove(controller.snapshot()) + mode.finish() true } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1ddbc9e5b..52e423fcf 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -671,4 +671,5 @@ Chapters left External/plugin Incompatible plugin or internal error. Make sure you are using the latest version of the plugin and Kotatsu + Connection is OK diff --git a/app/src/main/res/xml/pref_proxy.xml b/app/src/main/res/xml/pref_proxy.xml index 4b0447277..75f530990 100644 --- a/app/src/main/res/xml/pref_proxy.xml +++ b/app/src/main/res/xml/pref_proxy.xml @@ -36,4 +36,10 @@ + +