Merge branch 'master' into devel
This commit is contained in:
@@ -16,8 +16,8 @@ android {
|
|||||||
applicationId 'org.koitharu.kotatsu'
|
applicationId 'org.koitharu.kotatsu'
|
||||||
minSdk = 21
|
minSdk = 21
|
||||||
targetSdk = 35
|
targetSdk = 35
|
||||||
versionCode = 658
|
versionCode = 659
|
||||||
versionName = '7.4.1'
|
versionName = '7.4.2'
|
||||||
generatedDensities = []
|
generatedDensities = []
|
||||||
testInstrumentationRunner 'org.koitharu.kotatsu.HiltTestRunner'
|
testInstrumentationRunner 'org.koitharu.kotatsu.HiltTestRunner'
|
||||||
ksp {
|
ksp {
|
||||||
@@ -82,7 +82,7 @@ afterEvaluate {
|
|||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
//noinspection GradleDependency
|
//noinspection GradleDependency
|
||||||
implementation('com.github.KotatsuApp:kotatsu-parsers:3b5a018f8c') {
|
implementation('com.github.KotatsuApp:kotatsu-parsers:ca212ca692') {
|
||||||
exclude group: 'org.json', module: 'json'
|
exclude group: 'org.json', module: 'json'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +95,7 @@ dependencies {
|
|||||||
implementation 'androidx.activity:activity-ktx:1.9.1'
|
implementation 'androidx.activity:activity-ktx:1.9.1'
|
||||||
implementation 'androidx.fragment:fragment-ktx:1.8.2'
|
implementation 'androidx.fragment:fragment-ktx:1.8.2'
|
||||||
implementation 'androidx.transition:transition-ktx:1.5.1'
|
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-viewmodel-ktx:2.8.4'
|
||||||
implementation 'androidx.lifecycle:lifecycle-service:2.8.4'
|
implementation 'androidx.lifecycle:lifecycle-service:2.8.4'
|
||||||
implementation 'androidx.lifecycle:lifecycle-process: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.lifecycle:lifecycle-common-java8:2.8.4'
|
||||||
implementation 'androidx.webkit:webkit:1.11.0'
|
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
|
//noinspection GradleDependency
|
||||||
implementation('com.google.guava:guava:32.0.1-android') {
|
implementation('com.google.guava:guava:32.0.1-android') {
|
||||||
exclude group: 'com.google.guava', module: 'failureaccess'
|
exclude group: 'com.google.guava', module: 'failureaccess'
|
||||||
|
|||||||
@@ -704,6 +704,7 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
|
|||||||
const val KEY_LOGS_SHARE = "logs_share"
|
const val KEY_LOGS_SHARE = "logs_share"
|
||||||
const val KEY_APP_UPDATE = "app_update"
|
const val KEY_APP_UPDATE = "app_update"
|
||||||
const val KEY_APP_TRANSLATION = "about_app_translation"
|
const val KEY_APP_TRANSLATION = "about_app_translation"
|
||||||
|
const val PROXY_TEST = "proxy_test"
|
||||||
|
|
||||||
// old keys are for migration only
|
// old keys are for migration only
|
||||||
private const val KEY_IMAGES_PROXY_OLD = "images_proxy"
|
private const val KEY_IMAGES_PROXY_OLD = "images_proxy"
|
||||||
|
|||||||
@@ -61,7 +61,13 @@ class MangaSourcesRepository @Inject constructor(
|
|||||||
suspend fun getEnabledSources(): List<MangaSource> {
|
suspend fun getEnabledSources(): List<MangaSource> {
|
||||||
assimilateNewSources()
|
assimilateNewSources()
|
||||||
val order = settings.sourcesSortOrder
|
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<MangaSourceInfo>(enabled.size + external.size)
|
||||||
|
external.mapTo(list) { MangaSourceInfo(it, isEnabled = true, isPinned = true) }
|
||||||
|
list.addAll(enabled)
|
||||||
|
list
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getPinnedSources(): Set<MangaSource> {
|
suspend fun getPinnedSources(): Set<MangaSource> {
|
||||||
@@ -308,8 +314,6 @@ class MangaSourcesRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun observeExternalSources(): Flow<List<ExternalMangaSource>> {
|
private fun observeExternalSources(): Flow<List<ExternalMangaSource>> {
|
||||||
val intent = Intent("app.kotatsu.parser.PROVIDE_MANGA")
|
|
||||||
val pm = context.packageManager
|
|
||||||
return callbackFlow {
|
return callbackFlow {
|
||||||
val receiver = object : BroadcastReceiver() {
|
val receiver = object : BroadcastReceiver() {
|
||||||
override fun onReceive(context: Context?, intent: Intent?) {
|
override fun onReceive(context: Context?, intent: Intent?) {
|
||||||
@@ -333,15 +337,19 @@ class MangaSourcesRepository @Inject constructor(
|
|||||||
}.onStart {
|
}.onStart {
|
||||||
emit(null)
|
emit(null)
|
||||||
}.map {
|
}.map {
|
||||||
pm.queryIntentContentProviders(intent, 0).map { resolveInfo ->
|
getExternalSources()
|
||||||
ExternalMangaSource(
|
|
||||||
packageName = resolveInfo.providerInfo.packageName,
|
|
||||||
authority = resolveInfo.providerInfo.authority,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}.distinctUntilChanged()
|
}.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<MangaSourceEntity>.toSources(
|
private fun List<MangaSourceEntity>.toSources(
|
||||||
skipNsfwSources: Boolean,
|
skipNsfwSources: Boolean,
|
||||||
sortOrder: SourcesSortOrder?,
|
sortOrder: SourcesSortOrder?,
|
||||||
|
|||||||
@@ -176,8 +176,10 @@ class WebtoonScalingFrame @JvmOverloads constructor(
|
|||||||
val targetChild = findTargetChild()
|
val targetChild = findTargetChild()
|
||||||
adjustBounds()
|
adjustBounds()
|
||||||
targetChild.run {
|
targetChild.run {
|
||||||
scaleX = scale
|
if (!scale.isNaN()) {
|
||||||
scaleY = scale
|
scaleX = scale
|
||||||
|
scaleY = scale
|
||||||
|
}
|
||||||
translationX = transX
|
translationX = transX
|
||||||
translationY = transY
|
translationY = transY
|
||||||
if (pendingScroll != 0) {
|
if (pendingScroll != 0) {
|
||||||
@@ -298,7 +300,7 @@ class WebtoonScalingFrame @JvmOverloads constructor(
|
|||||||
distanceX: Float,
|
distanceX: Float,
|
||||||
distanceY: Float,
|
distanceY: Float,
|
||||||
): Boolean {
|
): Boolean {
|
||||||
if (scale <= 1f) return false
|
if (scale <= 1f || scale.isNaN()) return false
|
||||||
transformMatrix.postTranslate(-distanceX, -distanceY)
|
transformMatrix.postTranslate(-distanceX, -distanceY)
|
||||||
invalidateTarget()
|
invalidateTarget()
|
||||||
return true
|
return true
|
||||||
@@ -323,7 +325,7 @@ class WebtoonScalingFrame @JvmOverloads constructor(
|
|||||||
velocityX: Float,
|
velocityX: Float,
|
||||||
velocityY: Float,
|
velocityY: Float,
|
||||||
): Boolean {
|
): Boolean {
|
||||||
if (scale <= 1) return false
|
if (scale <= 1 || scale.isNaN()) return false
|
||||||
|
|
||||||
prevPos.set(transX.toInt(), transY.toInt())
|
prevPos.set(transX.toInt(), transY.toInt())
|
||||||
overScroller.fling(
|
overScroller.fling(
|
||||||
|
|||||||
@@ -7,20 +7,40 @@ import android.view.inputmethod.EditorInfo
|
|||||||
import androidx.preference.EditTextPreference
|
import androidx.preference.EditTextPreference
|
||||||
import androidx.preference.Preference
|
import androidx.preference.Preference
|
||||||
import androidx.preference.PreferenceCategory
|
import androidx.preference.PreferenceCategory
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
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.R
|
||||||
|
import org.koitharu.kotatsu.core.network.BaseHttpClient
|
||||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
import org.koitharu.kotatsu.core.ui.BasePreferenceFragment
|
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.EditTextBindListener
|
||||||
import org.koitharu.kotatsu.settings.utils.PasswordSummaryProvider
|
import org.koitharu.kotatsu.settings.utils.PasswordSummaryProvider
|
||||||
import org.koitharu.kotatsu.settings.utils.validation.DomainValidator
|
import org.koitharu.kotatsu.settings.utils.validation.DomainValidator
|
||||||
import org.koitharu.kotatsu.settings.utils.validation.PortNumberValidator
|
import org.koitharu.kotatsu.settings.utils.validation.PortNumberValidator
|
||||||
import java.net.Proxy
|
import java.net.Proxy
|
||||||
|
import javax.inject.Inject
|
||||||
|
import kotlin.coroutines.cancellation.CancellationException
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class ProxySettingsFragment : BasePreferenceFragment(R.string.proxy),
|
class ProxySettingsFragment : BasePreferenceFragment(R.string.proxy),
|
||||||
SharedPreferences.OnSharedPreferenceChangeListener {
|
SharedPreferences.OnSharedPreferenceChangeListener {
|
||||||
|
|
||||||
|
private var testJob: Job? = null
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
@BaseHttpClient
|
||||||
|
lateinit var okHttpClient: OkHttpClient
|
||||||
|
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
addPreferencesFromResource(R.xml.pref_proxy)
|
addPreferencesFromResource(R.xml.pref_proxy)
|
||||||
findPreference<EditTextPreference>(AppSettings.KEY_PROXY_ADDRESS)?.setOnBindEditTextListener(
|
findPreference<EditTextPreference>(AppSettings.KEY_PROXY_ADDRESS)?.setOnBindEditTextListener(
|
||||||
@@ -60,6 +80,15 @@ class ProxySettingsFragment : BasePreferenceFragment(R.string.proxy),
|
|||||||
super.onDestroyView()
|
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?) {
|
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
|
||||||
when (key) {
|
when (key) {
|
||||||
AppSettings.KEY_PROXY_TYPE -> updateDependencies()
|
AppSettings.KEY_PROXY_TYPE -> updateDependencies()
|
||||||
@@ -73,5 +102,47 @@ class ProxySettingsFragment : BasePreferenceFragment(R.string.proxy),
|
|||||||
findPreference<PreferenceCategory>(AppSettings.KEY_PROXY_AUTH)?.isEnabled = isProxyEnabled
|
findPreference<PreferenceCategory>(AppSettings.KEY_PROXY_AUTH)?.isEnabled = isProxyEnabled
|
||||||
findPreference<Preference>(AppSettings.KEY_PROXY_LOGIN)?.isEnabled = isProxyEnabled
|
findPreference<Preference>(AppSettings.KEY_PROXY_LOGIN)?.isEnabled = isProxyEnabled
|
||||||
findPreference<Preference>(AppSettings.KEY_PROXY_PASSWORD)?.isEnabled = isProxyEnabled
|
findPreference<Preference>(AppSettings.KEY_PROXY_PASSWORD)?.isEnabled = isProxyEnabled
|
||||||
|
findPreference<Preference>(AppSettings.PROXY_TEST)?.isEnabled = isProxyEnabled && testJob?.isActive != true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun testConnection() {
|
||||||
|
testJob?.cancel()
|
||||||
|
testJob = viewLifecycleScope.launch {
|
||||||
|
val pref = findPreference<Preference>(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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ class UpdatesFragment : MangaListFragment() {
|
|||||||
return when (item.itemId) {
|
return when (item.itemId) {
|
||||||
R.id.action_remove -> {
|
R.id.action_remove -> {
|
||||||
viewModel.remove(controller.snapshot())
|
viewModel.remove(controller.snapshot())
|
||||||
|
mode.finish()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -671,4 +671,5 @@
|
|||||||
<string name="chapters_left">Chapters left</string>
|
<string name="chapters_left">Chapters left</string>
|
||||||
<string name="external_source">External/plugin</string>
|
<string name="external_source">External/plugin</string>
|
||||||
<string name="plugin_incompatible">Incompatible plugin or internal error. Make sure you are using the latest version of the plugin and Kotatsu</string>
|
<string name="plugin_incompatible">Incompatible plugin or internal error. Make sure you are using the latest version of the plugin and Kotatsu</string>
|
||||||
|
<string name="connection_ok">Connection is OK</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -36,4 +36,10 @@
|
|||||||
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
android:key="proxy_test"
|
||||||
|
android:persistent="false"
|
||||||
|
android:title="Test connection"
|
||||||
|
app:allowDividerAbove="true" />
|
||||||
|
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
|
|||||||
Reference in New Issue
Block a user