Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5436c65b76 | ||
|
|
c590813a1a | ||
|
|
1cf36e1b41 | ||
|
|
5895a20af1 | ||
|
|
fd5fd43b72 |
@@ -15,8 +15,8 @@ android {
|
|||||||
applicationId 'org.koitharu.kotatsu'
|
applicationId 'org.koitharu.kotatsu'
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 33
|
targetSdkVersion 33
|
||||||
versionCode 524
|
versionCode 525
|
||||||
versionName '4.4.8'
|
versionName '4.4.9'
|
||||||
generatedDensities = []
|
generatedDensities = []
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ afterEvaluate {
|
|||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
//noinspection GradleDependency
|
//noinspection GradleDependency
|
||||||
implementation('com.github.KotatsuApp:kotatsu-parsers:2340100999') {
|
implementation('com.github.KotatsuApp:kotatsu-parsers:1b6d1456f3') {
|
||||||
exclude group: 'org.json', module: 'json'
|
exclude group: 'org.json', module: 'json'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import kotlinx.coroutines.flow.asStateFlow
|
|||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
|
import org.json.JSONArray
|
||||||
|
import org.json.JSONObject
|
||||||
import org.koitharu.kotatsu.BuildConfig
|
import org.koitharu.kotatsu.BuildConfig
|
||||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
import org.koitharu.kotatsu.parsers.util.await
|
import org.koitharu.kotatsu.parsers.util.await
|
||||||
@@ -28,6 +30,7 @@ import javax.inject.Inject
|
|||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
private const val CERT_SHA1 = "2C:19:C7:E8:07:61:2B:8E:94:51:1B:FD:72:67:07:64:5D:C2:58:AE"
|
private const val CERT_SHA1 = "2C:19:C7:E8:07:61:2B:8E:94:51:1B:FD:72:67:07:64:5D:C2:58:AE"
|
||||||
|
private const val CONTENT_TYPE_APK = "application/vnd.android.package-archive"
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
class AppUpdateRepository @Inject constructor(
|
class AppUpdateRepository @Inject constructor(
|
||||||
@@ -46,7 +49,9 @@ class AppUpdateRepository @Inject constructor(
|
|||||||
.url("https://api.github.com/repos/KotatsuApp/Kotatsu/releases?page=1&per_page=10")
|
.url("https://api.github.com/repos/KotatsuApp/Kotatsu/releases?page=1&per_page=10")
|
||||||
val jsonArray = okHttp.newCall(request.build()).await().parseJsonArray()
|
val jsonArray = okHttp.newCall(request.build()).await().parseJsonArray()
|
||||||
return jsonArray.mapJSONNotNull { json ->
|
return jsonArray.mapJSONNotNull { json ->
|
||||||
val asset = json.optJSONArray("assets")?.optJSONObject(0) ?: return@mapJSONNotNull null
|
val asset = json.optJSONArray("assets")?.find { jo ->
|
||||||
|
jo.optString("content_type") == CONTENT_TYPE_APK
|
||||||
|
} ?: return@mapJSONNotNull null
|
||||||
AppVersion(
|
AppVersion(
|
||||||
id = json.getLong("id"),
|
id = json.getLong("id"),
|
||||||
url = json.getString("html_url"),
|
url = json.getString("html_url"),
|
||||||
@@ -103,4 +108,15 @@ class AppUpdateRepository @Inject constructor(
|
|||||||
}.onFailure { error ->
|
}.onFailure { error ->
|
||||||
error.printStackTraceDebug()
|
error.printStackTraceDebug()
|
||||||
}.getOrNull()
|
}.getOrNull()
|
||||||
|
|
||||||
|
private inline fun JSONArray.find(predicate: (JSONObject) -> Boolean): JSONObject? {
|
||||||
|
val size = length()
|
||||||
|
for (i in 0 until size) {
|
||||||
|
val jo = getJSONObject(i)
|
||||||
|
if (predicate(jo)) {
|
||||||
|
return jo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerUser
|
|||||||
import org.koitharu.kotatsu.utils.ext.toRequestBody
|
import org.koitharu.kotatsu.utils.ext.toRequestBody
|
||||||
|
|
||||||
private const val REDIRECT_URI = "kotatsu://shikimori-auth"
|
private const val REDIRECT_URI = "kotatsu://shikimori-auth"
|
||||||
private const val BASE_URL = "https://shikimori.one/"
|
private const val BASE_URL = "https://shikimori.me/"
|
||||||
private const val MANGA_PAGE_SIZE = 10
|
private const val MANGA_PAGE_SIZE = 10
|
||||||
|
|
||||||
class ShikimoriRepository(
|
class ShikimoriRepository(
|
||||||
@@ -195,15 +195,15 @@ class ShikimoriRepository(
|
|||||||
id = json.getLong("id"),
|
id = json.getLong("id"),
|
||||||
name = json.getString("name"),
|
name = json.getString("name"),
|
||||||
altName = json.getStringOrNull("russian"),
|
altName = json.getStringOrNull("russian"),
|
||||||
cover = json.getJSONObject("image").getString("preview").toAbsoluteUrl("shikimori.one"),
|
cover = json.getJSONObject("image").getString("preview").toAbsoluteUrl("shikimori.me"),
|
||||||
url = json.getString("url").toAbsoluteUrl("shikimori.one"),
|
url = json.getString("url").toAbsoluteUrl("shikimori.me"),
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun ScrobblerMangaInfo(json: JSONObject) = ScrobblerMangaInfo(
|
private fun ScrobblerMangaInfo(json: JSONObject) = ScrobblerMangaInfo(
|
||||||
id = json.getLong("id"),
|
id = json.getLong("id"),
|
||||||
name = json.getString("name"),
|
name = json.getString("name"),
|
||||||
cover = json.getJSONObject("image").getString("preview").toAbsoluteUrl("shikimori.one"),
|
cover = json.getJSONObject("image").getString("preview").toAbsoluteUrl("shikimori.me"),
|
||||||
url = json.getString("url").toAbsoluteUrl("shikimori.one"),
|
url = json.getString("url").toAbsoluteUrl("shikimori.me"),
|
||||||
descriptionHtml = json.getString("description_html"),
|
descriptionHtml = json.getString("description_html"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
package org.koitharu.kotatsu.settings
|
package org.koitharu.kotatsu.settings
|
||||||
|
|
||||||
|
import okhttp3.HttpUrl
|
||||||
import okhttp3.internal.toCanonicalHost
|
import okhttp3.internal.toCanonicalHost
|
||||||
import org.koitharu.kotatsu.R
|
import org.koitharu.kotatsu.R
|
||||||
import org.koitharu.kotatsu.utils.EditTextValidator
|
import org.koitharu.kotatsu.utils.EditTextValidator
|
||||||
|
|
||||||
class DomainValidator : EditTextValidator() {
|
class DomainValidator : EditTextValidator() {
|
||||||
|
|
||||||
|
private val urlBuilder = HttpUrl.Builder()
|
||||||
|
|
||||||
override fun validate(text: String): ValidationResult {
|
override fun validate(text: String): ValidationResult {
|
||||||
val trimmed = text.trim()
|
val trimmed = text.trim()
|
||||||
if (trimmed.isEmpty()) {
|
if (trimmed.isEmpty()) {
|
||||||
@@ -18,13 +21,7 @@ class DomainValidator : EditTextValidator() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkCharacters(value: String): Boolean {
|
private fun checkCharacters(value: String): Boolean = runCatching {
|
||||||
for (i in value.indices) {
|
urlBuilder.host(value)
|
||||||
val c = value[i]
|
}.isSuccess
|
||||||
if (c !in '\u0020'..'\u007e') {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ class AboutSettingsFragment : BasePreferenceFragment(R.string.about) {
|
|||||||
}
|
}
|
||||||
findPreference<SwitchPreferenceCompat>(AppSettings.KEY_UPDATES_UNSTABLE)?.run {
|
findPreference<SwitchPreferenceCompat>(AppSettings.KEY_UPDATES_UNSTABLE)?.run {
|
||||||
isEnabled = VersionId(BuildConfig.VERSION_NAME).isStable
|
isEnabled = VersionId(BuildConfig.VERSION_NAME).isStable
|
||||||
|
if (!isEnabled) isChecked = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,8 +31,7 @@ class AboutLinksPreference @JvmOverloads constructor(
|
|||||||
binding.btn4pda,
|
binding.btn4pda,
|
||||||
binding.btnDiscord,
|
binding.btnDiscord,
|
||||||
binding.btnGithub,
|
binding.btnGithub,
|
||||||
binding.btnReddit,
|
binding.btnTelegram,
|
||||||
binding.btnTwitter,
|
|
||||||
).forEach { button ->
|
).forEach { button ->
|
||||||
TooltipCompat.setTooltipText(button, button.contentDescription)
|
TooltipCompat.setTooltipText(button, button.contentDescription)
|
||||||
button.setOnClickListener(this)
|
button.setOnClickListener(this)
|
||||||
@@ -43,8 +42,7 @@ class AboutLinksPreference @JvmOverloads constructor(
|
|||||||
val urlResId = when (v.id) {
|
val urlResId = when (v.id) {
|
||||||
R.id.btn_4pda -> R.string.url_forpda
|
R.id.btn_4pda -> R.string.url_forpda
|
||||||
R.id.btn_discord -> R.string.url_discord
|
R.id.btn_discord -> R.string.url_discord
|
||||||
R.id.btn_twitter -> R.string.url_twitter
|
R.id.btn_telegram -> R.string.url_telegram
|
||||||
R.id.btn_reddit -> R.string.url_reddit
|
|
||||||
R.id.btn_github -> R.string.url_github
|
R.id.btn_github -> R.string.url_github
|
||||||
else -> return
|
else -> return
|
||||||
}
|
}
|
||||||
@@ -59,9 +57,9 @@ class AboutLinksPreference @JvmOverloads constructor(
|
|||||||
Intent.createChooser(intent, title)
|
Intent.createChooser(intent, title)
|
||||||
} else {
|
} else {
|
||||||
intent
|
intent
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
} catch (_: ActivityNotFoundException) {
|
} catch (_: ActivityNotFoundException) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
<vector
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:tint="?attr/colorControlNormal"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24">
|
|
||||||
<path
|
|
||||||
android:fillColor="#FF000000"
|
|
||||||
android:pathData="M14.5 15.41C14.58 15.5 14.58 15.69 14.5 15.8C13.77 16.5 12.41 16.56 12 16.56C11.61 16.56 10.25 16.5 9.54 15.8C9.44 15.69 9.44 15.5 9.54 15.41C9.65 15.31 9.82 15.31 9.92 15.41C10.38 15.87 11.33 16 12 16C12.69 16 13.66 15.87 14.1 15.41C14.21 15.31 14.38 15.31 14.5 15.41M10.75 13.04C10.75 12.47 10.28 12 9.71 12C9.14 12 8.67 12.47 8.67 13.04C8.67 13.61 9.14 14.09 9.71 14.08C10.28 14.08 10.75 13.61 10.75 13.04M14.29 12C13.72 12 13.25 12.5 13.25 13.05S13.72 14.09 14.29 14.09C14.86 14.09 15.33 13.61 15.33 13.05C15.33 12.5 14.86 12 14.29 12M22 12C22 17.5 17.5 22 12 22S2 17.5 2 12C2 6.5 6.5 2 12 2S22 6.5 22 12M18.67 12C18.67 11.19 18 10.54 17.22 10.54C16.82 10.54 16.46 10.7 16.2 10.95C15.2 10.23 13.83 9.77 12.3 9.71L12.97 6.58L15.14 7.05C15.16 7.6 15.62 8.04 16.18 8.04C16.75 8.04 17.22 7.57 17.22 7C17.22 6.43 16.75 5.96 16.18 5.96C15.77 5.96 15.41 6.2 15.25 6.55L12.82 6.03C12.75 6 12.68 6.03 12.63 6.07C12.57 6.11 12.54 6.17 12.53 6.24L11.79 9.72C10.24 9.77 8.84 10.23 7.82 10.96C7.56 10.71 7.2 10.56 6.81 10.56C6 10.56 5.35 11.21 5.35 12C5.35 12.61 5.71 13.11 6.21 13.34C6.19 13.5 6.18 13.62 6.18 13.78C6.18 16 8.79 17.85 12 17.85C15.23 17.85 17.85 16.03 17.85 13.78C17.85 13.64 17.84 13.5 17.81 13.34C18.31 13.11 18.67 12.6 18.67 12Z" />
|
|
||||||
</vector>
|
|
||||||
10
app/src/main/res/drawable/ic_telegram.xml
Normal file
10
app/src/main/res/drawable/ic_telegram.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="512"
|
||||||
|
android:viewportHeight="512">
|
||||||
|
<path
|
||||||
|
android:pathData="m196.03,328.68 l162,118.8c16.8,10.8 31.2,4.8 36,-16.8l66,-309.6c6,-26.4 -10.8,-38.4 -28.8,-30L46.03,239.88c-25.2,9.6 -25.2,25.2 -4.8,31.2l99.6,31.2 228,-145.2c10.8,-6 20.4,-3.6 13.2,4.8"
|
||||||
|
android:strokeWidth="1.2"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
</vector>
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
<vector
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:tint="?attr/colorControlNormal"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24">
|
|
||||||
<path
|
|
||||||
android:fillColor="@android:color/white"
|
|
||||||
android:pathData="M22.46,6C21.69,6.35 20.86,6.58 20,6.69C20.88,6.16 21.56,5.32 21.88,4.31C21.05,4.81 20.13,5.16 19.16,5.36C18.37,4.5 17.26,4 16,4C13.65,4 11.73,5.92 11.73,8.29C11.73,8.63 11.77,8.96 11.84,9.27C8.28,9.09 5.11,7.38 3,4.79C2.63,5.42 2.42,6.16 2.42,6.94C2.42,8.43 3.17,9.75 4.33,10.5C3.62,10.5 2.96,10.3 2.38,10C2.38,10 2.38,10 2.38,10.03C2.38,12.11 3.86,13.85 5.82,14.24C5.46,14.34 5.08,14.39 4.69,14.39C4.42,14.39 4.15,14.36 3.89,14.31C4.43,16 6,17.26 7.89,17.29C6.43,18.45 4.58,19.13 2.56,19.13C2.22,19.13 1.88,19.11 1.54,19.07C3.44,20.29 5.7,21 8.12,21C16,21 20.33,14.46 20.33,8.79C20.33,8.6 20.33,8.42 20.32,8.23C21.16,7.63 21.88,6.87 22.46,6Z" />
|
|
||||||
</vector>
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -7,13 +8,13 @@
|
|||||||
android:gravity="center">
|
android:gravity="center">
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/btn_4pda"
|
android:id="@+id/btn_github"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
android:contentDescription="4PDA"
|
android:contentDescription="GitHub"
|
||||||
android:padding="16dp"
|
android:padding="16dp"
|
||||||
android:src="@drawable/ic_4pda"
|
android:src="@drawable/ic_github"
|
||||||
app:tint="?attr/colorAccent"
|
app:tint="?attr/colorAccent"
|
||||||
tools:ignore="HardcodedText" />
|
tools:ignore="HardcodedText" />
|
||||||
|
|
||||||
@@ -29,36 +30,25 @@
|
|||||||
tools:ignore="HardcodedText" />
|
tools:ignore="HardcodedText" />
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/btn_twitter"
|
android:id="@+id/btn_telegram"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
android:contentDescription="Twitter"
|
android:contentDescription="Telegram"
|
||||||
android:padding="16dp"
|
android:padding="16dp"
|
||||||
android:src="@drawable/ic_twitter"
|
android:src="@drawable/ic_telegram"
|
||||||
app:tint="?attr/colorAccent"
|
app:tint="?attr/colorAccent"
|
||||||
tools:ignore="HardcodedText" />
|
tools:ignore="HardcodedText" />
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/btn_reddit"
|
android:id="@+id/btn_4pda"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
android:contentDescription="Reddit"
|
android:contentDescription="4PDA"
|
||||||
android:padding="16dp"
|
android:padding="16dp"
|
||||||
android:src="@drawable/ic_reddit"
|
android:src="@drawable/ic_4pda"
|
||||||
app:tint="?attr/colorAccent"
|
app:tint="?attr/colorAccent"
|
||||||
tools:ignore="HardcodedText" />
|
tools:ignore="HardcodedText" />
|
||||||
|
|
||||||
<ImageButton
|
</LinearLayout>
|
||||||
android:id="@+id/btn_github"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
|
||||||
android:contentDescription="GitHub"
|
|
||||||
android:padding="16dp"
|
|
||||||
android:src="@drawable/ic_github"
|
|
||||||
app:tint="?attr/colorAccent"
|
|
||||||
tools:ignore="HardcodedText" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|||||||
@@ -3,8 +3,7 @@
|
|||||||
<string name="url_github" translatable="false">https://github.com/KotatsuApp/Kotatsu</string>
|
<string name="url_github" translatable="false">https://github.com/KotatsuApp/Kotatsu</string>
|
||||||
<string name="url_discord" translatable="false">https://discord.gg/NNJ5RgVBC5</string>
|
<string name="url_discord" translatable="false">https://discord.gg/NNJ5RgVBC5</string>
|
||||||
<string name="url_forpda" translatable="false">https://4pda.to/forum/index.php?showtopic=697669</string>
|
<string name="url_forpda" translatable="false">https://4pda.to/forum/index.php?showtopic=697669</string>
|
||||||
<string name="url_twitter" translatable="false">https://twitter.com/kotatsuapp</string>
|
<string name="url_telegram" translatable="false">https://t.me/kotatsuapp</string>
|
||||||
<string name="url_reddit" translatable="false">https://reddit.com/user/kotatsuapp</string>
|
|
||||||
<string name="url_weblate" translatable="false">https://hosted.weblate.org/engage/kotatsu</string>
|
<string name="url_weblate" translatable="false">https://hosted.weblate.org/engage/kotatsu</string>
|
||||||
<string name="url_error_report" translatable="false">https://acra.kotatsu.app/report</string>
|
<string name="url_error_report" translatable="false">https://acra.kotatsu.app/report</string>
|
||||||
<string name="account_type_sync" translatable="false">org.kotatsu.sync</string>
|
<string name="account_type_sync" translatable="false">org.kotatsu.sync</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user