diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index c7cc0c855..9af821d54 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -2,4 +2,4 @@ blank_issues_enabled: false contact_links: - name: ⚠️ Source issue url: https://github.com/KotatsuApp/kotatsu-parsers/issues/new - about: Issues and requests for sources should be opened in the kotatsu-parsers repository instead \ No newline at end of file + about: If you have troubles with a manga parser or want to propose new manga source, please open an issue in the kotatsu-parsers repository instead \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/report_bug.yml b/.github/ISSUE_TEMPLATE/report_bug.yml new file mode 100644 index 000000000..261f51945 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/report_bug.yml @@ -0,0 +1,64 @@ +name: 🐞 Bug report +description: Report a bug in Kotatsu +labels: [bug] +body: + + - type: textarea + id: summary + attributes: + label: Brief summary + description: Please describe, what went wrong + validations: + required: true + + - type: textarea + id: reproduce-steps + attributes: + label: Steps to reproduce + description: Please provide a way to reproduce this issue. Screenshots or videos can be very helpful + placeholder: | + Example: + 1. First step + 2. Second step + 3. Issue here + validations: + required: false + + + - type: input + id: kotatsu-version + attributes: + label: Kotatsu version + description: You can find your Kotatsu version in **Settings → About**. + placeholder: | + Example: "3.3" + validations: + required: true + + - type: input + id: android-version + attributes: + label: Android version + description: You can find this somewhere in your Android settings. + placeholder: | + Example: "12.0" + validations: + required: true + + - type: input + id: device + attributes: + label: Device + description: List your device and model. + placeholder: | + Example: "LG Nexus 5X" + validations: + required: false + + - type: checkboxes + id: acknowledgements + attributes: + label: Acknowledgements + options: + - label: I have searched the existing issues and this is a new ticket, **NOT** a duplicate or related to another open issue. + required: true \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/report_issue.yml b/.github/ISSUE_TEMPLATE/report_issue.yml deleted file mode 100644 index 4bc2d2de9..000000000 --- a/.github/ISSUE_TEMPLATE/report_issue.yml +++ /dev/null @@ -1,91 +0,0 @@ -name: 🐞 Issue report -description: Report an issue in Kotatsu -labels: [bug] -body: - - - type: textarea - id: reproduce-steps - attributes: - label: Steps to reproduce - description: Provide an example of the issue. - placeholder: | - Example: - 1. First step - 2. Second step - 3. Issue here - validations: - required: true - - - type: textarea - id: expected-behavior - attributes: - label: Expected behavior - description: Explain what you should expect to happen. - placeholder: | - Example: - "This should happen..." - validations: - required: true - - - type: textarea - id: actual-behavior - attributes: - label: Actual behavior - description: Explain what actually happens. - placeholder: | - Example: - "This happened instead..." - validations: - required: true - - - type: input - id: kotatsu-version - attributes: - label: Kotatsu version - description: You can find your Kotatsu version in **Settings → About**. - placeholder: | - Example: "3.3" - validations: - required: true - - - type: input - id: android-version - attributes: - label: Android version - description: You can find this somewhere in your Android settings. - placeholder: | - Example: "Android 12" - validations: - required: true - - - type: input - id: device - attributes: - label: Device - description: List your device and model. - placeholder: | - Example: "LG Nexus 5X" - validations: - required: true - - - type: textarea - id: other-details - attributes: - label: Other details - placeholder: | - Additional details and attachments. - - - type: checkboxes - id: acknowledgements - attributes: - label: Acknowledgements - description: Read this carefully, we will close and ignore your issue if you skimmed through this. - options: - - label: I have searched the existing issues and this is a new ticket, **NOT** a duplicate or related to another open issue. - required: true - - label: I have written a short but informative title. - required: true - - label: If this is an issue with a source, I should be opening an issue in the [parsers repository](https://github.com/KotatsuApp/kotatsu-parsers/issues/new). - required: true - - label: I will fill out all of the requested information in this form. - required: true \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/request_feature.yml b/.github/ISSUE_TEMPLATE/request_feature.yml index b49ba479b..a8539d394 100644 --- a/.github/ISSUE_TEMPLATE/request_feature.yml +++ b/.github/ISSUE_TEMPLATE/request_feature.yml @@ -1,5 +1,5 @@ name: ⭐ Feature request -description: Suggest a feature to improve Kotatsu +description: Suggest a new idea how to improve Kotatsu labels: [feature request] body: @@ -14,23 +14,6 @@ body: validations: required: true - - type: textarea - id: other-details - attributes: - label: Other details - placeholder: | - Additional details and attachments. - - - type: input - id: kotatsu-version - attributes: - label: Kotatsu version - description: You can find your Kotatsu version in **Settings → About**. - placeholder: | - Example: "3.3" - validations: - required: true - - type: checkboxes id: acknowledgements attributes: @@ -38,10 +21,4 @@ body: description: Read this carefully, we will close and ignore your issue if you skimmed through this. options: - label: I have searched the existing issues and this is a new ticket, **NOT** a duplicate or related to another open issue. - required: true - - label: I have written a short but informative title. - required: true - - label: If this is an issue with a source, I should be opening an issue in the [parsers repository](https://github.com/KotatsuApp/kotatsu-parsers/issues/new). - required: true - - label: I will fill out all of the requested information in this form. required: true \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index f02ef258b..3c11ffd80 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,8 +14,8 @@ android { applicationId 'org.koitharu.kotatsu' minSdkVersion 21 targetSdkVersion 32 - versionCode 414 - versionName '3.4.2' + versionCode 415 + versionName '3.4.3' generatedDensities = [] testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/app/src/main/java/org/koitharu/kotatsu/core/network/CloudFlareInterceptor.kt b/app/src/main/java/org/koitharu/kotatsu/core/network/CloudFlareInterceptor.kt index d9c8281d7..a32a94c83 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/network/CloudFlareInterceptor.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/network/CloudFlareInterceptor.kt @@ -17,7 +17,7 @@ class CloudFlareInterceptor : Interceptor { if (response.code == HTTP_FORBIDDEN || response.code == HTTP_UNAVAILABLE) { if (response.header(HEADER_SERVER)?.startsWith(SERVER_CLOUDFLARE) == true) { response.closeQuietly() - throw CloudFlareProtectedException(chain.request().url.toString()) + throw CloudFlareProtectedException(response.request.url.toString()) } } return response diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderActivity.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderActivity.kt index 3afceafd6..98ca6c988 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderActivity.kt @@ -24,7 +24,6 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import org.acra.ktx.sendWithAcra import org.koin.android.ext.android.get import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.core.parameter.parametersOf @@ -47,10 +46,7 @@ import org.koitharu.kotatsu.settings.SettingsActivity import org.koitharu.kotatsu.utils.GridTouchHelper import org.koitharu.kotatsu.utils.ScreenOrientationHelper import org.koitharu.kotatsu.utils.ShareHelper -import org.koitharu.kotatsu.utils.ext.getDisplayMessage -import org.koitharu.kotatsu.utils.ext.hasGlobalPoint -import org.koitharu.kotatsu.utils.ext.observeWithPrevious -import org.koitharu.kotatsu.utils.ext.postDelayed +import org.koitharu.kotatsu.utils.ext.* import java.util.concurrent.TimeUnit class ReaderActivity : @@ -375,7 +371,7 @@ class ReaderActivity : if (ExceptionResolver.canResolve(exception)) { tryResolve(exception) } else { - exception.sendWithAcra() + exception.report("ReaderActivity::onError") } } else { onCancel(dialog) diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/DomainValidator.kt b/app/src/main/java/org/koitharu/kotatsu/settings/DomainValidator.kt new file mode 100644 index 000000000..6fc5adab3 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/settings/DomainValidator.kt @@ -0,0 +1,20 @@ +package org.koitharu.kotatsu.settings + +import okhttp3.internal.toCanonicalHost +import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.utils.EditTextValidator + +class DomainValidator : EditTextValidator() { + + override fun validate(text: String): ValidationResult { + if (text.isBlank()) { + return ValidationResult.Success + } + val host = text.trim().toCanonicalHost() + return if (host == null) { + ValidationResult.Failed(context.getString(R.string.invalid_domain_message)) + } else { + ValidationResult.Success + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/SourceSettingsExt.kt b/app/src/main/java/org/koitharu/kotatsu/settings/SourceSettingsExt.kt index 3a7c8843c..4776223d7 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/SourceSettingsExt.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/SourceSettingsExt.kt @@ -28,6 +28,7 @@ fun PreferenceFragmentCompat.addPreferencesFromRepository(repository: RemoteMang EditTextBindListener( inputType = EditorInfo.TYPE_CLASS_TEXT or EditorInfo.TYPE_TEXT_VARIATION_URI, hint = key.defaultValue, + validator = DomainValidator(), ) ) setTitle(R.string.domain) diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/utils/EditTextBindListener.kt b/app/src/main/java/org/koitharu/kotatsu/settings/utils/EditTextBindListener.kt index 0390bceaa..50e93f24b 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/utils/EditTextBindListener.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/utils/EditTextBindListener.kt @@ -2,14 +2,17 @@ package org.koitharu.kotatsu.settings.utils import android.widget.EditText import androidx.preference.EditTextPreference +import org.koitharu.kotatsu.utils.EditTextValidator class EditTextBindListener( private val inputType: Int, - private val hint: String + private val hint: String, + private val validator: EditTextValidator?, ) : EditTextPreference.OnBindEditTextListener { override fun onBindEditText(editText: EditText) { editText.inputType = inputType editText.hint = hint + validator?.attachToEditText(editText) } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/CompositeMutex.kt b/app/src/main/java/org/koitharu/kotatsu/utils/CompositeMutex.kt index e66355588..6433ca44e 100644 --- a/app/src/main/java/org/koitharu/kotatsu/utils/CompositeMutex.kt +++ b/app/src/main/java/org/koitharu/kotatsu/utils/CompositeMutex.kt @@ -1,6 +1,8 @@ package org.koitharu.kotatsu.utils import kotlinx.coroutines.CancellableContinuation +import kotlinx.coroutines.currentCoroutineContext +import kotlinx.coroutines.isActive import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock @@ -32,11 +34,13 @@ class CompositeMutex : Set { } suspend fun lock(element: T) { - waitForRemoval(element) - mutex.withLock { - val lastValue = data.put(element, LinkedList>()) - check(lastValue == null) { - "CompositeMutex is double-locked for $element" + while (currentCoroutineContext().isActive) { + waitForRemoval(element) + mutex.withLock { + if (data[element] == null) { + data[element] = LinkedList>() + return + } } } } diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/EditTextValidator.kt b/app/src/main/java/org/koitharu/kotatsu/utils/EditTextValidator.kt new file mode 100644 index 000000000..37ca77618 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/utils/EditTextValidator.kt @@ -0,0 +1,54 @@ +package org.koitharu.kotatsu.utils + +import android.content.Context +import android.text.Editable +import android.text.TextWatcher +import android.widget.EditText +import androidx.annotation.CallSuper +import org.koitharu.kotatsu.utils.ext.getDisplayMessage +import java.lang.ref.WeakReference + +abstract class EditTextValidator : TextWatcher { + + private var editTextRef: WeakReference? = null + + protected val context: Context + get() = checkNotNull(editTextRef?.get()?.context) { + "EditTextValidator is not attached to EditText" + } + + override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit + + override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) = Unit + + @CallSuper + override fun afterTextChanged(s: Editable?) { + val editText = editTextRef?.get() ?: return + val newText = s?.toString().orEmpty() + val result = runCatching { + validate(newText) + }.getOrElse { e -> + ValidationResult.Failed(e.getDisplayMessage(editText.resources)) + } + editText.error = when (result) { + is ValidationResult.Failed -> result.message + ValidationResult.Success -> null + } + } + + fun attachToEditText(editText: EditText) { + editTextRef = WeakReference(editText) + editText.removeTextChangedListener(this) + editText.addTextChangedListener(this) + afterTextChanged(editText.text) + } + + abstract fun validate(text: String): ValidationResult + + sealed class ValidationResult { + + object Success : ValidationResult() + + class Failed(val message: CharSequence) : ValidationResult() + } +} \ No newline at end of file diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 1bf64ec78..6491afd0a 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -302,4 +302,19 @@ Mangas de tus favoritos Sus mangas recientemente leídos Cerrar sesión + Planeado + Lectura + Abandonado + En espera + Informar + Seguimiento + Ayuda con las comprobaciones de las actualizaciones en segundo plano + Relectura + Completado + Mostrar indicadores de progreso en la lectura + Eliminación de datos + Mostrar porcentaje de lectura en el historial y en los favoritos + El manga marcado como NSFW nunca se añadirá al historial y no se guardará tu progreso + Puede ayudar en caso de algunos problemas. Todas las autorizaciones serán invalidadas + Mostrar todo \ No newline at end of file diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index ccc436176..a58e97abf 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -31,7 +31,7 @@ 清除cookies 检查新的章节: %1$d/%2$d 你必须输入一个名称 - 有了新的漫画来源 + 新的漫画来源 根据你的喜好推荐漫画 所有的数据都在这个设备上进行本地分析. 您的个人数据不会被转移到任何服务机构 从不 @@ -284,7 +284,7 @@ 删除书签 书签 删除书签 - 加入书签 + 添加书签 撤销 从历史中删除 DNS over HTTPS @@ -295,4 +295,22 @@ 帮助进行背景更新检查 出了点问题. 请向开发人员提交一份错误报告以帮助我们修复它. 发送 + 全部禁用 + 计划 + 暂停 + 报告 + 追踪 + 注销 + 阅读 + 重读 + 完成 + 使用指纹 + 你喜欢的漫画 + 您最近阅读的漫画 + 在历史和收藏夹中显示阅读百分比 + 显示阅读进度指标 + 数据删除 + 标记为NSFW的漫画将永远不会被添加到历史中你的进度也不会被保存 + 可以在出现一些问题时提供帮助. 所有授权将被视为无效 + 显示全部 \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ba59c26e8..eb78d4db9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -320,6 +320,7 @@ Manga marked as NSFW will never added to the history and your progress will not be saved Can help in case of some issues. All authorizations will be invalidated Show all + Invalid domain Clear all history Last 2 hours History cleared diff --git a/app/src/test/java/org/koitharu/kotatsu/utils/CompositeMutexTest.kt b/app/src/test/java/org/koitharu/kotatsu/utils/CompositeMutexTest.kt new file mode 100644 index 000000000..d6f1e87ef --- /dev/null +++ b/app/src/test/java/org/koitharu/kotatsu/utils/CompositeMutexTest.kt @@ -0,0 +1,39 @@ +package org.koitharu.kotatsu.utils + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.test.runTest +import kotlinx.coroutines.withTimeoutOrNull +import kotlinx.coroutines.yield +import org.junit.Assert.assertNull +import org.junit.Test + +class CompositeMutexTest { + + @Test + fun testSingleLock() = runTest { + val mutex = CompositeMutex() + mutex.lock(1) + mutex.lock(2) + mutex.unlock(1) + assert(mutex.size == 1) + mutex.unlock(2) + assert(mutex.isEmpty()) + } + + @Test + fun testDoubleLock() = runTest { + val mutex = CompositeMutex() + repeat(2) { + launch(Dispatchers.Default) { + mutex.lock(1) + } + } + yield() + mutex.unlock(1) + val tryLock = withTimeoutOrNull(1000) { + mutex.lock(1) + } + assertNull(tryLock) + } +} \ No newline at end of file