Merge branch 'devel' of github.com:nv95/Kotatsu into devel
This commit is contained in:
@@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 Google LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.koitharu.kotatsu.base.ui.widgets
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.widget.Button
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.core.view.postDelayed
|
||||||
|
import org.koitharu.kotatsu.R
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A custom snackbar implementation allowing more control over placement and entry/exit animations.
|
||||||
|
*
|
||||||
|
* Xtimms: Well, my sufferings over the Snackbar in [DetailsActivity] will go away forever... Thanks, Google.
|
||||||
|
*
|
||||||
|
* https://github.com/google/iosched/blob/main/mobile/src/main/java/com/google/samples/apps/iosched/widget/FadingSnackbar.kt
|
||||||
|
*/
|
||||||
|
class FadingSnackbar @JvmOverloads constructor(
|
||||||
|
context: Context,
|
||||||
|
attrs: AttributeSet? = null,
|
||||||
|
defStyleAttr: Int = 0
|
||||||
|
) : FrameLayout(context, attrs, defStyleAttr) {
|
||||||
|
|
||||||
|
private val message: TextView
|
||||||
|
private val action: Button
|
||||||
|
|
||||||
|
init {
|
||||||
|
val view = LayoutInflater.from(context).inflate(R.layout.fading_snackbar_layout, this, true)
|
||||||
|
message = view.findViewById(R.id.snackbar_text)
|
||||||
|
action = view.findViewById(R.id.snackbar_action)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun dismiss() {
|
||||||
|
if (visibility == VISIBLE && alpha == 1f) {
|
||||||
|
animate()
|
||||||
|
.alpha(0f)
|
||||||
|
.withEndAction { visibility = GONE }
|
||||||
|
.duration = EXIT_DURATION
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun show(
|
||||||
|
messageText: CharSequence? = null,
|
||||||
|
@StringRes actionId: Int? = null,
|
||||||
|
longDuration: Boolean = true,
|
||||||
|
actionClick: () -> Unit = { dismiss() },
|
||||||
|
dismissListener: () -> Unit = { }
|
||||||
|
) {
|
||||||
|
message.text = messageText
|
||||||
|
if (actionId != null) {
|
||||||
|
action.run {
|
||||||
|
visibility = VISIBLE
|
||||||
|
text = context.getString(actionId)
|
||||||
|
setOnClickListener {
|
||||||
|
actionClick()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
action.visibility = GONE
|
||||||
|
}
|
||||||
|
alpha = 0f
|
||||||
|
visibility = VISIBLE
|
||||||
|
animate()
|
||||||
|
.alpha(1f)
|
||||||
|
.duration = ENTER_DURATION
|
||||||
|
val showDuration = ENTER_DURATION + if (longDuration) LONG_DURATION else SHORT_DURATION
|
||||||
|
postDelayed(showDuration) {
|
||||||
|
dismiss()
|
||||||
|
dismissListener()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val ENTER_DURATION = 300L
|
||||||
|
private const val EXIT_DURATION = 200L
|
||||||
|
private const val SHORT_DURATION = 1_500L
|
||||||
|
private const val LONG_DURATION = 2_750L
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,8 @@ data class MangaChapter(
|
|||||||
val name: String,
|
val name: String,
|
||||||
val number: Int,
|
val number: Int,
|
||||||
val url: String,
|
val url: String,
|
||||||
|
val scanlator: String? = null,
|
||||||
|
val uploadDate: Long,
|
||||||
val branch: String? = null,
|
val branch: String? = null,
|
||||||
val source: MangaSource
|
val source: MangaSource
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
@@ -84,9 +84,10 @@ class AnibelRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepositor
|
|||||||
?.toRelativeUrl(getDomain()) ?: return@mapIndexedNotNull null
|
?.toRelativeUrl(getDomain()) ?: return@mapIndexedNotNull null
|
||||||
MangaChapter(
|
MangaChapter(
|
||||||
id = generateUid(href),
|
id = generateUid(href),
|
||||||
name = a.selectFirst("a")?.text().orEmpty(),
|
name = "Глава " + a.selectFirst("a")?.text().orEmpty(),
|
||||||
number = i + 1,
|
number = i + 1,
|
||||||
url = href,
|
url = href,
|
||||||
|
uploadDate = 0L,
|
||||||
source = source
|
source = source
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import org.koitharu.kotatsu.core.exceptions.ParseException
|
|||||||
import org.koitharu.kotatsu.core.model.*
|
import org.koitharu.kotatsu.core.model.*
|
||||||
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
|
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
|
||||||
import org.koitharu.kotatsu.utils.ext.*
|
import org.koitharu.kotatsu.utils.ext.*
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
abstract class ChanRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepository(
|
abstract class ChanRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepository(
|
||||||
@@ -79,15 +80,14 @@ abstract class ChanRepository(loaderContext: MangaLoaderContext) : RemoteMangaRe
|
|||||||
return manga.copy(
|
return manga.copy(
|
||||||
description = root.getElementById("description")?.html()?.substringBeforeLast("<div"),
|
description = root.getElementById("description")?.html()?.substringBeforeLast("<div"),
|
||||||
largeCoverUrl = root.getElementById("cover")?.absUrl("src"),
|
largeCoverUrl = root.getElementById("cover")?.absUrl("src"),
|
||||||
chapters = root.select("table.table_cha").flatMap { table ->
|
chapters = root.select("table.table_cha tr:gt(1)").reversed().mapIndexedNotNull { i, tr ->
|
||||||
table.select("div.manga2")
|
val href = tr?.selectFirst("a")?.relUrl("href") ?: return@mapIndexedNotNull null
|
||||||
}.map { it.selectFirst("a") }.reversed().mapIndexedNotNull { i, a ->
|
|
||||||
val href = a?.relUrl("href") ?: return@mapIndexedNotNull null
|
|
||||||
MangaChapter(
|
MangaChapter(
|
||||||
id = generateUid(href),
|
id = generateUid(href),
|
||||||
name = a.text().trim(),
|
name = tr.selectFirst("a")?.text().orEmpty(),
|
||||||
number = i + 1,
|
number = i + 1,
|
||||||
url = href,
|
url = href,
|
||||||
|
uploadDate = parseChapterDate(tr.selectFirst("div.date")?.text().orEmpty()),
|
||||||
source = source
|
source = source
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -154,4 +154,9 @@ abstract class ChanRepository(loaderContext: MangaLoaderContext) : RemoteMangaRe
|
|||||||
SortOrder.NEWEST -> "datedesc"
|
SortOrder.NEWEST -> "datedesc"
|
||||||
else -> "favdesc"
|
else -> "favdesc"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun parseChapterDate(string: String): Long {
|
||||||
|
return SimpleDateFormat("yyyy-MM-dd", Locale.US).tryParse(string)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -93,11 +93,14 @@ class DesuMeRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepositor
|
|||||||
description = json.getString("description"),
|
description = json.getString("description"),
|
||||||
chapters = chaptersList.mapIndexed { i, it ->
|
chapters = chaptersList.mapIndexed { i, it ->
|
||||||
val chid = it.getLong("id")
|
val chid = it.getLong("id")
|
||||||
|
val volChap = "Том " + it.getString("vol") + ". " + "Глава " + it.getString("ch")
|
||||||
|
val title = if (it.getString("title") == "null") "" else it.getString("title")
|
||||||
MangaChapter(
|
MangaChapter(
|
||||||
id = generateUid(chid),
|
id = generateUid(chid),
|
||||||
source = manga.source,
|
source = manga.source,
|
||||||
url = "$baseChapterUrl$chid",
|
url = "$baseChapterUrl$chid",
|
||||||
name = it.getStringOrNull("title") ?: "${manga.title} #${it.getDouble("ch")}",
|
uploadDate = it.getLong("date") * 1000,
|
||||||
|
name = if (title.isEmpty()) volChap else "$volChap: $title",
|
||||||
number = totalChapters - i
|
number = totalChapters - i
|
||||||
)
|
)
|
||||||
}.reversed()
|
}.reversed()
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ class ExHentaiRepository(
|
|||||||
name = "${manga.title} #$i",
|
name = "${manga.title} #$i",
|
||||||
number = i,
|
number = i,
|
||||||
url = url,
|
url = url,
|
||||||
branch = null,
|
uploadDate = 0L,
|
||||||
source = source,
|
source = source,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import org.koitharu.kotatsu.core.exceptions.ParseException
|
|||||||
import org.koitharu.kotatsu.core.model.*
|
import org.koitharu.kotatsu.core.model.*
|
||||||
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
|
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
|
||||||
import org.koitharu.kotatsu.utils.ext.*
|
import org.koitharu.kotatsu.utils.ext.*
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
abstract class GroupleRepository(loaderContext: MangaLoaderContext) :
|
abstract class GroupleRepository(loaderContext: MangaLoaderContext) :
|
||||||
@@ -123,13 +124,23 @@ abstract class GroupleRepository(loaderContext: MangaLoaderContext) :
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
chapters = root.selectFirst("div.chapters-link")?.selectFirst("table")
|
chapters = root.selectFirst("div.chapters-link")?.selectFirst("table")
|
||||||
?.select("a")?.asReversed()?.mapIndexed { i, a ->
|
?.select("tr:has(td > a)")?.asReversed()?.mapIndexedNotNull { i, tr ->
|
||||||
|
val a = tr.selectFirst("a") ?: return@mapIndexedNotNull null
|
||||||
val href = a.relUrl("href")
|
val href = a.relUrl("href")
|
||||||
|
var translators = ""
|
||||||
|
val translatorElement = a.attr("title")
|
||||||
|
if (!translatorElement.isNullOrBlank()) {
|
||||||
|
translators = translatorElement
|
||||||
|
.replace("(Переводчик),", "&")
|
||||||
|
.removeSuffix(" (Переводчик)")
|
||||||
|
}
|
||||||
MangaChapter(
|
MangaChapter(
|
||||||
id = generateUid(href),
|
id = generateUid(href),
|
||||||
name = a.ownText().removePrefix(manga.title).trim(),
|
name = tr.selectFirst("a")?.text().orEmpty().removePrefix(manga.title).trim(),
|
||||||
number = i + 1,
|
number = i + 1,
|
||||||
url = href,
|
url = href,
|
||||||
|
uploadDate = parseChapterDate(tr.select("td.d-none").text()),
|
||||||
|
scanlator = translators,
|
||||||
source = source
|
source = source
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -223,11 +234,15 @@ abstract class GroupleRepository(loaderContext: MangaLoaderContext) :
|
|||||||
return loaderContext.httpPost(url, payload)
|
return loaderContext.httpPost(url, payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun parseChapterDate(string: String): Long {
|
||||||
|
return SimpleDateFormat("dd.MM.yy", Locale.US).tryParse(string)
|
||||||
|
}
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
|
|
||||||
private const val PAGE_SIZE = 70
|
private const val PAGE_SIZE = 70
|
||||||
private const val PAGE_SIZE_SEARCH = 50
|
private const val PAGE_SIZE_SEARCH = 50
|
||||||
val HEADER = Headers.Builder()
|
private val HEADER = Headers.Builder()
|
||||||
.add("User-Agent", "readmangafun")
|
.add("User-Agent", "readmangafun")
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ class HenChanRepository(loaderContext: MangaLoaderContext) : ChanRepository(load
|
|||||||
url = readLink,
|
url = readLink,
|
||||||
source = source,
|
source = source,
|
||||||
number = 1,
|
number = 1,
|
||||||
|
uploadDate = 0L,
|
||||||
name = manga.title
|
name = manga.title
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import org.koitharu.kotatsu.core.exceptions.ParseException
|
|||||||
import org.koitharu.kotatsu.core.model.*
|
import org.koitharu.kotatsu.core.model.*
|
||||||
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
|
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
|
||||||
import org.koitharu.kotatsu.utils.ext.*
|
import org.koitharu.kotatsu.utils.ext.*
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
open class MangaLibRepository(loaderContext: MangaLoaderContext) :
|
open class MangaLibRepository(loaderContext: MangaLoaderContext) :
|
||||||
@@ -91,7 +92,7 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) :
|
|||||||
for (i in 0 until total) {
|
for (i in 0 until total) {
|
||||||
val item = list.getJSONObject(i)
|
val item = list.getJSONObject(i)
|
||||||
val chapterId = item.getLong("chapter_id")
|
val chapterId = item.getLong("chapter_id")
|
||||||
val branchName = item.getStringOrNull("username")
|
val scanlator = item.getStringOrNull("username")
|
||||||
val url = buildString {
|
val url = buildString {
|
||||||
append(manga.url)
|
append(manga.url)
|
||||||
append("/v")
|
append("/v")
|
||||||
@@ -102,19 +103,19 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) :
|
|||||||
append('/')
|
append('/')
|
||||||
append(item.optString("chapter_string"))
|
append(item.optString("chapter_string"))
|
||||||
}
|
}
|
||||||
var name = item.getStringOrNull("chapter_name")
|
val nameChapter = item.getStringOrNull("chapter_name")
|
||||||
if (name.isNullOrBlank() || name == "null") {
|
val volume = item.getInt("chapter_volume")
|
||||||
name = "Том " + item.getInt("chapter_volume") +
|
val number = item.getString("chapter_number")
|
||||||
" Глава " + item.getString("chapter_number")
|
val fullNameChapter = "Том $volume. Глава $number"
|
||||||
}
|
|
||||||
chapters.add(
|
chapters.add(
|
||||||
MangaChapter(
|
MangaChapter(
|
||||||
id = generateUid(chapterId),
|
id = generateUid(chapterId),
|
||||||
url = url,
|
url = url,
|
||||||
source = source,
|
source = source,
|
||||||
branch = branchName,
|
|
||||||
number = total - i,
|
number = total - i,
|
||||||
name = name
|
uploadDate = parseChapterDate(item.getString("chapter_created_at").substringBefore(" ")),
|
||||||
|
scanlator = scanlator,
|
||||||
|
name = if (nameChapter.isNullOrBlank()) fullNameChapter else "$fullNameChapter - $nameChapter"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -235,9 +236,14 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) :
|
|||||||
.toFloatOrNull()?.div(5f) ?: Manga.NO_RATING,
|
.toFloatOrNull()?.div(5f) ?: Manga.NO_RATING,
|
||||||
state = null,
|
state = null,
|
||||||
source = source,
|
source = source,
|
||||||
coverUrl = "https://$domain${covers.getString("thumbnail")}",
|
coverUrl = covers.getString("thumbnail"),
|
||||||
largeCoverUrl = "https://$domain${covers.getString("default")}"
|
largeCoverUrl = covers.getString("default")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun parseChapterDate(string: String): Long {
|
||||||
|
return SimpleDateFormat("yyy-MM-dd", Locale.US).tryParse(string)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,7 @@ import org.koitharu.kotatsu.core.exceptions.ParseException
|
|||||||
import org.koitharu.kotatsu.core.model.*
|
import org.koitharu.kotatsu.core.model.*
|
||||||
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
|
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
|
||||||
import org.koitharu.kotatsu.utils.ext.*
|
import org.koitharu.kotatsu.utils.ext.*
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class MangaOwlRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepository(loaderContext) {
|
class MangaOwlRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepository(loaderContext) {
|
||||||
@@ -99,6 +100,7 @@ class MangaOwlRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposit
|
|||||||
name = a.select("label").text(),
|
name = a.select("label").text(),
|
||||||
number = i + 1,
|
number = i + 1,
|
||||||
url = href,
|
url = href,
|
||||||
|
uploadDate = parseChapterDate(li.select("small:last-of-type").text()),
|
||||||
source = MangaSource.MANGAOWL
|
source = MangaSource.MANGAOWL
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -156,4 +158,8 @@ class MangaOwlRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposit
|
|||||||
else -> "3"
|
else -> "3"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun parseChapterDate(string: String): Long {
|
||||||
|
return SimpleDateFormat("MM/dd/yyyy", Locale.US).tryParse(string)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -7,6 +7,7 @@ import org.koitharu.kotatsu.core.model.*
|
|||||||
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
|
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
|
||||||
import org.koitharu.kotatsu.core.prefs.SourceSettings
|
import org.koitharu.kotatsu.core.prefs.SourceSettings
|
||||||
import org.koitharu.kotatsu.utils.ext.*
|
import org.koitharu.kotatsu.utils.ext.*
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class MangaTownRepository(loaderContext: MangaLoaderContext) :
|
class MangaTownRepository(loaderContext: MangaLoaderContext) :
|
||||||
@@ -117,6 +118,7 @@ class MangaTownRepository(loaderContext: MangaLoaderContext) :
|
|||||||
url = href,
|
url = href,
|
||||||
source = MangaSource.MANGATOWN,
|
source = MangaSource.MANGATOWN,
|
||||||
number = i + 1,
|
number = i + 1,
|
||||||
|
uploadDate = parseChapterDate(li.selectFirst("span.time")?.text().orEmpty()),
|
||||||
name = name.ifEmpty { "${manga.title} - ${i + 1}" }
|
name = name.ifEmpty { "${manga.title} - ${i + 1}" }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -167,6 +169,14 @@ class MangaTownRepository(loaderContext: MangaLoaderContext) :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun parseChapterDate(date: String): Long {
|
||||||
|
return when {
|
||||||
|
date.contains("Today") -> Calendar.getInstance().timeInMillis
|
||||||
|
date.contains("Yesterday") -> Calendar.getInstance().apply { add(Calendar.DAY_OF_MONTH, -1) }.timeInMillis
|
||||||
|
else -> SimpleDateFormat("MMM dd,yyyy", Locale.US).tryParse(date)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreatePreferences(map: MutableMap<String, Any>) {
|
override fun onCreatePreferences(map: MutableMap<String, Any>) {
|
||||||
super.onCreatePreferences(map)
|
super.onCreatePreferences(map)
|
||||||
map[SourceSettings.KEY_USE_SSL] = true
|
map[SourceSettings.KEY_USE_SSL] = true
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ import org.koitharu.kotatsu.base.domain.MangaLoaderContext
|
|||||||
import org.koitharu.kotatsu.core.exceptions.ParseException
|
import org.koitharu.kotatsu.core.exceptions.ParseException
|
||||||
import org.koitharu.kotatsu.core.model.*
|
import org.koitharu.kotatsu.core.model.*
|
||||||
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
|
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
|
||||||
|
import org.koitharu.kotatsu.utils.WordSet
|
||||||
import org.koitharu.kotatsu.utils.ext.*
|
import org.koitharu.kotatsu.utils.ext.*
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class MangareadRepository(
|
class MangareadRepository(
|
||||||
@@ -138,6 +140,7 @@ class MangareadRepository(
|
|||||||
name = a!!.ownText(),
|
name = a!!.ownText(),
|
||||||
number = i + 1,
|
number = i + 1,
|
||||||
url = href,
|
url = href,
|
||||||
|
uploadDate = parseChapterDate(doc2.selectFirst("span.chapter-release-date i")?.text()),
|
||||||
source = MangaSource.MANGAREAD
|
source = MangaSource.MANGAREAD
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -162,6 +165,70 @@ class MangareadRepository(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun parseChapterDate(date: String?): Long {
|
||||||
|
date ?: return 0
|
||||||
|
return when {
|
||||||
|
date.endsWith(" ago", ignoreCase = true) -> {
|
||||||
|
parseRelativeDate(date)
|
||||||
|
}
|
||||||
|
// Handle translated 'ago' in Portuguese.
|
||||||
|
date.endsWith(" atrás", ignoreCase = true) -> {
|
||||||
|
parseRelativeDate(date)
|
||||||
|
}
|
||||||
|
// Handle translated 'ago' in Turkish.
|
||||||
|
date.endsWith(" önce", ignoreCase = true) -> {
|
||||||
|
parseRelativeDate(date)
|
||||||
|
}
|
||||||
|
// Handle 'yesterday' and 'today', using midnight
|
||||||
|
date.startsWith("year", ignoreCase = true) -> {
|
||||||
|
Calendar.getInstance().apply {
|
||||||
|
add(Calendar.DAY_OF_MONTH, -1) // yesterday
|
||||||
|
set(Calendar.HOUR_OF_DAY, 0)
|
||||||
|
set(Calendar.MINUTE, 0)
|
||||||
|
set(Calendar.SECOND, 0)
|
||||||
|
set(Calendar.MILLISECOND, 0)
|
||||||
|
}.timeInMillis
|
||||||
|
}
|
||||||
|
date.startsWith("today", ignoreCase = true) -> {
|
||||||
|
Calendar.getInstance().apply {
|
||||||
|
set(Calendar.HOUR_OF_DAY, 0)
|
||||||
|
set(Calendar.MINUTE, 0)
|
||||||
|
set(Calendar.SECOND, 0)
|
||||||
|
set(Calendar.MILLISECOND, 0)
|
||||||
|
}.timeInMillis
|
||||||
|
}
|
||||||
|
date.contains(Regex("""\d(st|nd|rd|th)""")) -> {
|
||||||
|
// Clean date (e.g. 5th December 2019 to 5 December 2019) before parsing it
|
||||||
|
date.split(" ").map {
|
||||||
|
if (it.contains(Regex("""\d\D\D"""))) {
|
||||||
|
it.replace(Regex("""\D"""), "")
|
||||||
|
} else {
|
||||||
|
it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.let { dateFormat.tryParse(it.joinToString(" ")) }
|
||||||
|
}
|
||||||
|
else -> dateFormat.tryParse(date)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parses dates in this form:
|
||||||
|
// 21 hours ago
|
||||||
|
private fun parseRelativeDate(date: String): Long {
|
||||||
|
val number = Regex("""(\d+)""").find(date)?.value?.toIntOrNull() ?: return 0
|
||||||
|
val cal = Calendar.getInstance()
|
||||||
|
|
||||||
|
return when {
|
||||||
|
WordSet("hari", "gün", "jour", "día", "dia", "day").anyWordIn(date) -> cal.apply { add(Calendar.DAY_OF_MONTH, -number) }.timeInMillis
|
||||||
|
WordSet("jam", "saat", "heure", "hora", "hour").anyWordIn(date) -> cal.apply { add(Calendar.HOUR, -number) }.timeInMillis
|
||||||
|
WordSet("menit", "dakika", "min", "minute", "minuto").anyWordIn(date) -> cal.apply { add(Calendar.MINUTE, -number) }.timeInMillis
|
||||||
|
WordSet("detik", "segundo", "second").anyWordIn(date) -> cal.apply { add(Calendar.SECOND, -number) }.timeInMillis
|
||||||
|
WordSet("month").anyWordIn(date) -> cal.apply { add(Calendar.MONTH, -number) }.timeInMillis
|
||||||
|
WordSet("year").anyWordIn(date) -> cal.apply { add(Calendar.YEAR, -number) }.timeInMillis
|
||||||
|
else -> 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
|
|
||||||
private const val PAGE_SIZE = 12
|
private const val PAGE_SIZE = 12
|
||||||
@@ -173,5 +240,9 @@ class MangareadRepository(
|
|||||||
val pos = it.indexOf('=')
|
val pos = it.indexOf('=')
|
||||||
it.substring(0, pos) to it.substring(pos + 1)
|
it.substring(0, pos) to it.substring(pos + 1)
|
||||||
}.toMutableMap()
|
}.toMutableMap()
|
||||||
|
|
||||||
|
private val dateFormat by lazy {
|
||||||
|
SimpleDateFormat("MMMM dd, yyyy", Locale.US)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import org.koitharu.kotatsu.core.exceptions.ParseException
|
|||||||
import org.koitharu.kotatsu.core.model.*
|
import org.koitharu.kotatsu.core.model.*
|
||||||
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
|
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
|
||||||
import org.koitharu.kotatsu.utils.ext.*
|
import org.koitharu.kotatsu.utils.ext.*
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
abstract class NineMangaRepository(
|
abstract class NineMangaRepository(
|
||||||
@@ -99,18 +100,19 @@ abstract class NineMangaRepository(
|
|||||||
)
|
)
|
||||||
}.orEmpty(),
|
}.orEmpty(),
|
||||||
author = infoRoot.getElementsByAttributeValue("itemprop", "author").first()?.text(),
|
author = infoRoot.getElementsByAttributeValue("itemprop", "author").first()?.text(),
|
||||||
|
state = parseStatus(infoRoot.select("li a.red").text()),
|
||||||
description = infoRoot.getElementsByAttributeValue("itemprop", "description").first()
|
description = infoRoot.getElementsByAttributeValue("itemprop", "description").first()
|
||||||
?.html()?.substringAfter("</b>"),
|
?.html()?.substringAfter("</b>"),
|
||||||
chapters = root.selectFirst("div.chapterbox")?.selectFirst("ul")
|
chapters = root.selectFirst("div.chapterbox")?.select("ul.sub_vol_ul > li")
|
||||||
?.select("li")?.asReversed()?.mapIndexed { i, li ->
|
?.asReversed()?.mapIndexed { i, li ->
|
||||||
val a = li.selectFirst("a")
|
val a = li.selectFirst("a.chapter_list_a")
|
||||||
val href = a?.relUrl("href") ?: parseFailed("Link not found")
|
val href = a?.relUrl("href")?.replace("%20", " ") ?: parseFailed("Link not found")
|
||||||
MangaChapter(
|
MangaChapter(
|
||||||
id = generateUid(href),
|
id = generateUid(href),
|
||||||
name = a.text(),
|
name = a.text(),
|
||||||
number = i + 1,
|
number = i + 1,
|
||||||
url = href,
|
url = href,
|
||||||
branch = null,
|
uploadDate = parseChapterDateByLang(li.selectFirst("span")?.text().orEmpty()),
|
||||||
source = source,
|
source = source,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -153,6 +155,50 @@ abstract class NineMangaRepository(
|
|||||||
} ?: parseFailed("Root not found")
|
} ?: parseFailed("Root not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun parseStatus(status: String) = when {
|
||||||
|
status.contains("Ongoing") -> MangaState.ONGOING
|
||||||
|
status.contains("Completed") -> MangaState.FINISHED
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun parseChapterDateByLang(date: String): Long {
|
||||||
|
val dateWords = date.split(" ")
|
||||||
|
|
||||||
|
if (dateWords.size == 3) {
|
||||||
|
if (dateWords[1].contains(",")) {
|
||||||
|
SimpleDateFormat("MMM d, yyyy", Locale.ENGLISH).tryParse(date)
|
||||||
|
} else {
|
||||||
|
val timeAgo = Integer.parseInt(dateWords[0])
|
||||||
|
return Calendar.getInstance().apply {
|
||||||
|
when (dateWords[1]) {
|
||||||
|
"minutes" -> Calendar.MINUTE // EN-FR
|
||||||
|
"hours" -> Calendar.HOUR // EN
|
||||||
|
|
||||||
|
"minutos" -> Calendar.MINUTE // ES
|
||||||
|
"horas" -> Calendar.HOUR
|
||||||
|
|
||||||
|
// "minutos" -> Calendar.MINUTE // BR
|
||||||
|
"hora" -> Calendar.HOUR
|
||||||
|
|
||||||
|
"минут" -> Calendar.MINUTE // RU
|
||||||
|
"часа" -> Calendar.HOUR
|
||||||
|
|
||||||
|
"Stunden" -> Calendar.HOUR // DE
|
||||||
|
|
||||||
|
"minuti" -> Calendar.MINUTE // IT
|
||||||
|
"ore" -> Calendar.HOUR
|
||||||
|
|
||||||
|
"heures" -> Calendar.HOUR // FR ("minutes" also French word)
|
||||||
|
else -> null
|
||||||
|
}?.let {
|
||||||
|
add(it, -timeAgo)
|
||||||
|
}
|
||||||
|
}.timeInMillis
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0L
|
||||||
|
}
|
||||||
|
|
||||||
class English(loaderContext: MangaLoaderContext) : NineMangaRepository(
|
class English(loaderContext: MangaLoaderContext) : NineMangaRepository(
|
||||||
loaderContext,
|
loaderContext,
|
||||||
MangaSource.NINEMANGA_EN,
|
MangaSource.NINEMANGA_EN,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import org.koitharu.kotatsu.core.exceptions.ParseException
|
|||||||
import org.koitharu.kotatsu.core.model.*
|
import org.koitharu.kotatsu.core.model.*
|
||||||
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
|
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
|
||||||
import org.koitharu.kotatsu.utils.ext.*
|
import org.koitharu.kotatsu.utils.ext.*
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class RemangaRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepository(loaderContext) {
|
class RemangaRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepository(loaderContext) {
|
||||||
@@ -109,12 +110,16 @@ class RemangaRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposito
|
|||||||
},
|
},
|
||||||
chapters = chapters.mapIndexed { i, jo ->
|
chapters = chapters.mapIndexed { i, jo ->
|
||||||
val id = jo.getLong("id")
|
val id = jo.getLong("id")
|
||||||
val name = jo.getString("name")
|
val name = jo.getString("name").capitalize(Locale.ROOT)
|
||||||
|
val publishers = jo.getJSONArray("publishers")
|
||||||
MangaChapter(
|
MangaChapter(
|
||||||
id = generateUid(id),
|
id = generateUid(id),
|
||||||
url = "/api/titles/chapters/$id/",
|
url = "/api/titles/chapters/$id/",
|
||||||
number = chapters.length() - i,
|
number = chapters.length() - i,
|
||||||
name = buildString {
|
name = buildString {
|
||||||
|
append("Том ")
|
||||||
|
append(jo.getString("tome"))
|
||||||
|
append(". ")
|
||||||
append("Глава ")
|
append("Глава ")
|
||||||
append(jo.getString("chapter"))
|
append(jo.getString("chapter"))
|
||||||
if (name.isNotEmpty()) {
|
if (name.isNotEmpty()) {
|
||||||
@@ -122,6 +127,8 @@ class RemangaRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposito
|
|||||||
append(name)
|
append(name)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
uploadDate = parseChapterDate(jo.getString("upload_date")),
|
||||||
|
scanlator = publishers.optJSONObject(0)?.getStringOrNull("name"),
|
||||||
source = MangaSource.REMANGA
|
source = MangaSource.REMANGA
|
||||||
)
|
)
|
||||||
}.asReversed()
|
}.asReversed()
|
||||||
@@ -171,6 +178,10 @@ class RemangaRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposito
|
|||||||
source = source
|
source = source
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private fun parseChapterDate(string: String): Long {
|
||||||
|
return SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.US).tryParse(string)
|
||||||
|
}
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
|
|
||||||
const val PAGE_SIZE = 30
|
const val PAGE_SIZE = 30
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ class YaoiChanRepository(loaderContext: MangaLoaderContext) : ChanRepository(loa
|
|||||||
name = a.text().trim(),
|
name = a.text().trim(),
|
||||||
number = i + 1,
|
number = i + 1,
|
||||||
url = href,
|
url = href,
|
||||||
|
uploadDate = 0L,
|
||||||
source = source
|
source = source
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ import org.koitharu.kotatsu.core.model.ZoomMode
|
|||||||
import org.koitharu.kotatsu.local.domain.LocalMangaRepository
|
import org.koitharu.kotatsu.local.domain.LocalMangaRepository
|
||||||
import org.koitharu.kotatsu.utils.delegates.prefs.*
|
import org.koitharu.kotatsu.utils.delegates.prefs.*
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.text.DateFormat
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
class AppSettings private constructor(private val prefs: SharedPreferences) :
|
class AppSettings private constructor(private val prefs: SharedPreferences) :
|
||||||
SharedPreferences by prefs {
|
SharedPreferences by prefs {
|
||||||
@@ -121,6 +124,12 @@ class AppSettings private constructor(private val prefs: SharedPreferences) :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun dateFormat(format: String? = prefs.getString(KEY_DATE_FORMAT, "")): DateFormat =
|
||||||
|
when (format) {
|
||||||
|
"" -> DateFormat.getDateInstance(DateFormat.SHORT)
|
||||||
|
else -> SimpleDateFormat(format, Locale.getDefault())
|
||||||
|
}
|
||||||
|
|
||||||
@Deprecated("Use observe()")
|
@Deprecated("Use observe()")
|
||||||
fun subscribe(listener: SharedPreferences.OnSharedPreferenceChangeListener) {
|
fun subscribe(listener: SharedPreferences.OnSharedPreferenceChangeListener) {
|
||||||
prefs.registerOnSharedPreferenceChangeListener(listener)
|
prefs.registerOnSharedPreferenceChangeListener(listener)
|
||||||
@@ -152,6 +161,7 @@ class AppSettings private constructor(private val prefs: SharedPreferences) :
|
|||||||
const val KEY_APP_SECTION = "app_section"
|
const val KEY_APP_SECTION = "app_section"
|
||||||
const val KEY_THEME = "theme"
|
const val KEY_THEME = "theme"
|
||||||
const val KEY_THEME_AMOLED = "amoled_theme"
|
const val KEY_THEME_AMOLED = "amoled_theme"
|
||||||
|
const val KEY_DATE_FORMAT = "date_format"
|
||||||
const val KEY_HIDE_TOOLBAR = "hide_toolbar"
|
const val KEY_HIDE_TOOLBAR = "hide_toolbar"
|
||||||
const val KEY_SOURCES_ORDER = "sources_order"
|
const val KEY_SOURCES_ORDER = "sources_order"
|
||||||
const val KEY_SOURCES_HIDDEN = "sources_hidden"
|
const val KEY_SOURCES_HIDDEN = "sources_hidden"
|
||||||
|
|||||||
@@ -85,14 +85,13 @@ class DetailsActivity : BaseActivity<ActivityDetailsBinding>(),
|
|||||||
finishAfterTransition()
|
finishAfterTransition()
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
Snackbar.make(binding.coordinator, e.getDisplayMessage(resources), Snackbar.LENGTH_LONG)
|
binding.snackbar.show(e.getDisplayMessage(resources))
|
||||||
.show()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onWindowInsetsChanged(insets: Insets) {
|
override fun onWindowInsetsChanged(insets: Insets) {
|
||||||
binding.coordinator.updatePadding(
|
binding.snackbar.updatePadding(
|
||||||
bottom = insets.bottom
|
bottom = insets.bottom
|
||||||
)
|
)
|
||||||
binding.toolbar.updatePadding(
|
binding.toolbar.updatePadding(
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
package org.koitharu.kotatsu.details.ui.adapter
|
package org.koitharu.kotatsu.details.ui.adapter
|
||||||
|
|
||||||
|
import android.text.SpannableStringBuilder
|
||||||
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
||||||
|
import org.koin.core.context.GlobalContext
|
||||||
import org.koitharu.kotatsu.R
|
import org.koitharu.kotatsu.R
|
||||||
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
||||||
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
import org.koitharu.kotatsu.databinding.ItemChapterBinding
|
import org.koitharu.kotatsu.databinding.ItemChapterBinding
|
||||||
import org.koitharu.kotatsu.details.ui.model.ChapterListItem
|
import org.koitharu.kotatsu.details.ui.model.ChapterListItem
|
||||||
import org.koitharu.kotatsu.history.domain.ChapterExtra
|
import org.koitharu.kotatsu.history.domain.ChapterExtra
|
||||||
import org.koitharu.kotatsu.utils.ext.getThemeColor
|
import org.koitharu.kotatsu.utils.ext.getThemeColor
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
fun chapterListItemAD(
|
fun chapterListItemAD(
|
||||||
clickListener: OnListItemClickListener<ChapterListItem>,
|
clickListener: OnListItemClickListener<ChapterListItem>,
|
||||||
@@ -24,6 +28,20 @@ fun chapterListItemAD(
|
|||||||
bind { payload ->
|
bind { payload ->
|
||||||
binding.textViewTitle.text = item.chapter.name
|
binding.textViewTitle.text = item.chapter.name
|
||||||
binding.textViewNumber.text = item.chapter.number.toString()
|
binding.textViewNumber.text = item.chapter.number.toString()
|
||||||
|
val settings = GlobalContext.get().get<AppSettings>()
|
||||||
|
val descriptions = mutableListOf<CharSequence>()
|
||||||
|
val dateFormat = settings.dateFormat()
|
||||||
|
if (item.chapter.uploadDate > 0) {
|
||||||
|
descriptions.add(dateFormat.format(Date(item.chapter.uploadDate)))
|
||||||
|
}
|
||||||
|
if (!item.chapter.scanlator.isNullOrBlank()) {
|
||||||
|
descriptions.add(item.chapter.scanlator!!)
|
||||||
|
}
|
||||||
|
if (descriptions.isNotEmpty()) {
|
||||||
|
binding.textViewDescription.text = descriptions.joinTo(SpannableStringBuilder(), " • ")
|
||||||
|
} else {
|
||||||
|
binding.textViewDescription.text = ""
|
||||||
|
}
|
||||||
when (item.extra) {
|
when (item.extra) {
|
||||||
ChapterExtra.UNREAD -> {
|
ChapterExtra.UNREAD -> {
|
||||||
binding.textViewNumber.setBackgroundResource(R.drawable.bg_badge_default)
|
binding.textViewNumber.setBackgroundResource(R.drawable.bg_badge_default)
|
||||||
@@ -43,6 +61,7 @@ fun chapterListItemAD(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
binding.textViewTitle.alpha = if (item.isMissing) 0.3f else 1f
|
binding.textViewTitle.alpha = if (item.isMissing) 0.3f else 1f
|
||||||
|
binding.textViewDescription.alpha = if (item.isMissing) 0.3f else 1f
|
||||||
binding.textViewNumber.alpha = if (item.isMissing) 0.3f else 1f
|
binding.textViewNumber.alpha = if (item.isMissing) 0.3f else 1f
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -72,6 +72,8 @@ class MangaIndex(source: String?) {
|
|||||||
jo.put("number", chapter.number)
|
jo.put("number", chapter.number)
|
||||||
jo.put("url", chapter.url)
|
jo.put("url", chapter.url)
|
||||||
jo.put("name", chapter.name)
|
jo.put("name", chapter.name)
|
||||||
|
jo.put("uploadDate", chapter.uploadDate)
|
||||||
|
jo.put("scanlator", chapter.scanlator)
|
||||||
jo.put("branch", chapter.branch)
|
jo.put("branch", chapter.branch)
|
||||||
jo.put("entries", "%03d\\d{3}".format(chapter.number))
|
jo.put("entries", "%03d\\d{3}".format(chapter.number))
|
||||||
chapters.put(chapter.id.toString(), jo)
|
chapters.put(chapter.id.toString(), jo)
|
||||||
@@ -98,6 +100,8 @@ class MangaIndex(source: String?) {
|
|||||||
name = v.getString("name"),
|
name = v.getString("name"),
|
||||||
url = v.getString("url"),
|
url = v.getString("url"),
|
||||||
number = v.getInt("number"),
|
number = v.getInt("number"),
|
||||||
|
uploadDate = v.getLong("uploadDate"),
|
||||||
|
scanlator = v.getStringOrNull("scanlator"),
|
||||||
branch = v.getStringOrNull("branch"),
|
branch = v.getStringOrNull("branch"),
|
||||||
source = source
|
source = source
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -123,6 +123,7 @@ class LocalMangaRepository(private val context: Context) : MangaRepository {
|
|||||||
name = s.ifEmpty { title },
|
name = s.ifEmpty { title },
|
||||||
number = i + 1,
|
number = i + 1,
|
||||||
source = MangaSource.LOCAL,
|
source = MangaSource.LOCAL,
|
||||||
|
uploadDate = 0L,
|
||||||
url = uriBuilder.fragment(s).build().toString()
|
url = uriBuilder.fragment(s).build().toString()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ import android.view.View
|
|||||||
import androidx.appcompat.app.AppCompatDelegate
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
import androidx.preference.*
|
import androidx.preference.*
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import org.koitharu.kotatsu.BuildConfig
|
|
||||||
import org.koitharu.kotatsu.R
|
import org.koitharu.kotatsu.R
|
||||||
import org.koitharu.kotatsu.base.ui.BasePreferenceFragment
|
import org.koitharu.kotatsu.base.ui.BasePreferenceFragment
|
||||||
import org.koitharu.kotatsu.base.ui.dialog.StorageSelectDialog
|
import org.koitharu.kotatsu.base.ui.dialog.StorageSelectDialog
|
||||||
@@ -21,6 +19,7 @@ import org.koitharu.kotatsu.core.prefs.ListMode
|
|||||||
import org.koitharu.kotatsu.settings.protect.ProtectSetupActivity
|
import org.koitharu.kotatsu.settings.protect.ProtectSetupActivity
|
||||||
import org.koitharu.kotatsu.utils.ext.*
|
import org.koitharu.kotatsu.utils.ext.*
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
class MainSettingsFragment : BasePreferenceFragment(R.string.settings),
|
class MainSettingsFragment : BasePreferenceFragment(R.string.settings),
|
||||||
@@ -40,6 +39,20 @@ class MainSettingsFragment : BasePreferenceFragment(R.string.settings),
|
|||||||
entryValues = ListMode.values().names()
|
entryValues = ListMode.values().names()
|
||||||
setDefaultValueCompat(ListMode.GRID.name)
|
setDefaultValueCompat(ListMode.GRID.name)
|
||||||
}
|
}
|
||||||
|
findPreference<ListPreference>(AppSettings.KEY_DATE_FORMAT)?.run {
|
||||||
|
entryValues = arrayOf("", "MM/dd/yy", "dd/MM/yy", "yyyy-MM-dd", "dd MMM yyyy", "MMM dd, yyyy")
|
||||||
|
val now = Date().time
|
||||||
|
entries = entryValues.map { value ->
|
||||||
|
val formattedDate = settings.dateFormat(value.toString()).format(now)
|
||||||
|
if (value == "") {
|
||||||
|
"${context.getString(R.string.system_default)} ($formattedDate)"
|
||||||
|
} else {
|
||||||
|
"$value ($formattedDate)"
|
||||||
|
}
|
||||||
|
}.toTypedArray()
|
||||||
|
setDefaultValueCompat("")
|
||||||
|
summary = "%s"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
|||||||
9
app/src/main/java/org/koitharu/kotatsu/utils/WordSet.kt
Normal file
9
app/src/main/java/org/koitharu/kotatsu/utils/WordSet.kt
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package org.koitharu.kotatsu.utils
|
||||||
|
|
||||||
|
class WordSet(private vararg val words: String) {
|
||||||
|
|
||||||
|
fun anyWordIn(dateString: String): Boolean = words.any {
|
||||||
|
dateString.contains(it, ignoreCase = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ import org.jsoup.nodes.Document
|
|||||||
import org.jsoup.nodes.Element
|
import org.jsoup.nodes.Element
|
||||||
import org.jsoup.nodes.Node
|
import org.jsoup.nodes.Node
|
||||||
import org.jsoup.select.Elements
|
import org.jsoup.select.Elements
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
|
||||||
fun Response.parseHtml(): Document {
|
fun Response.parseHtml(): Document {
|
||||||
try {
|
try {
|
||||||
@@ -97,4 +98,8 @@ fun Element.css(property: String): String? {
|
|||||||
val regex = Regex("${Regex.escape(property)}\\s*:\\s*[^;]+")
|
val regex = Regex("${Regex.escape(property)}\\s*:\\s*[^;]+")
|
||||||
val css = attr("style").find(regex) ?: return null
|
val css = attr("style").find(regex) ?: return null
|
||||||
return css.substringAfter(':').removeSuffix(';').trim()
|
return css.substringAfter(':').removeSuffix(';').trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun SimpleDateFormat.tryParse(str: String): Long = runCatching {
|
||||||
|
parse(str)?.time ?: 0L
|
||||||
|
}.getOrDefault(0L)
|
||||||
7
app/src/main/res/drawable/fading_snackbar_background.xml
Normal file
7
app/src/main/res/drawable/fading_snackbar_background.xml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<solid android:color="#272727" />
|
||||||
|
<corners android:radius="4dp" />
|
||||||
|
</shape>
|
||||||
@@ -116,7 +116,7 @@
|
|||||||
android:dividerPadding="8dp"
|
android:dividerPadding="8dp"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:showDividers="middle"
|
android:showDividers="middle"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/textView_author">
|
app:layout_constraintTop_toBottomOf="@+id/textView_state">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/rating_container"
|
android:id="@+id/rating_container"
|
||||||
|
|||||||
@@ -117,7 +117,7 @@
|
|||||||
android:dividerPadding="8dp"
|
android:dividerPadding="8dp"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:showDividers="middle"
|
android:showDividers="middle"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/textView_author">
|
app:layout_constraintTop_toBottomOf="@+id/textView_state">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/rating_container"
|
android:id="@+id/rating_container"
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
android:id="@+id/coordinator"
|
android:id="@+id/coordinator"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:clipToPadding="false"
|
|
||||||
tools:context=".details.ui.DetailsActivity">
|
tools:context=".details.ui.DetailsActivity">
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
@@ -43,4 +42,11 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" />
|
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" />
|
||||||
|
|
||||||
|
<org.koitharu.kotatsu.base.ui.widgets.FadingSnackbar
|
||||||
|
android:id="@+id/snackbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="bottom"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
@@ -6,7 +6,6 @@
|
|||||||
android:id="@+id/coordinator"
|
android:id="@+id/coordinator"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:clipToPadding="false"
|
|
||||||
tools:context=".details.ui.DetailsActivity">
|
tools:context=".details.ui.DetailsActivity">
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
@@ -40,4 +39,11 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" />
|
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" />
|
||||||
|
|
||||||
|
<org.koitharu.kotatsu.base.ui.widgets.FadingSnackbar
|
||||||
|
android:id="@+id/snackbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="bottom"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
android:paddingEnd="?android:listPreferredItemPaddingEnd">
|
android:paddingEnd="?android:listPreferredItemPaddingEnd">
|
||||||
|
|
||||||
<com.google.android.material.checkbox.MaterialCheckBox
|
<com.google.android.material.checkbox.MaterialCheckBox
|
||||||
android:id="@android:id/checkbox"
|
android:id="@+id/checkbox"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
tools:text="@tools:sample/lorem[3]" />
|
tools:text="@tools:sample/lorem[3]" />
|
||||||
|
|||||||
61
app/src/main/res/layout/fading_snackbar_layout.xml
Normal file
61
app/src/main/res/layout/fading_snackbar_layout.xml
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright 2018 Google LLC
|
||||||
|
~
|
||||||
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
~ you may not use this file except in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing, software
|
||||||
|
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
~ See the License for the specific language governing permissions and
|
||||||
|
~ limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<merge
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="@dimen/margin_small"
|
||||||
|
android:background="@drawable/fading_snackbar_background"
|
||||||
|
android:theme="@style/ThemeOverlay.Kotatsu"
|
||||||
|
android:elevation="8dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/snackbar_text"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical|start"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="4"
|
||||||
|
android:padding="@dimen/margin_normal"
|
||||||
|
android:textAlignment="viewStart"
|
||||||
|
android:textColor="@android:color/white"
|
||||||
|
android:textAppearance="@style/TextAppearance.Design.Snackbar.Message"
|
||||||
|
tools:text="Look at all the wonderful snack bar text..." />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/snackbar_action"
|
||||||
|
style="?borderlessButtonStyle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_gravity="center_vertical|end"
|
||||||
|
android:paddingEnd="@dimen/margin_normal"
|
||||||
|
android:paddingStart="@dimen/margin_normal"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:text="Action"
|
||||||
|
tools:visibility="visible"
|
||||||
|
tools:targetApi="o" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</merge>
|
||||||
@@ -287,6 +287,7 @@
|
|||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/desc_header"
|
app:layout_constraintTop_toBottomOf="@id/desc_header"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
tools:ignore="UnusedAttribute"
|
tools:ignore="UnusedAttribute"
|
||||||
tools:text="@tools:sample/lorem/random[25]" />
|
tools:text="@tools:sample/lorem/random[25]" />
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<RelativeLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
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"
|
||||||
@@ -13,25 +13,50 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_centerVertical="true"
|
android:layout_centerVertical="true"
|
||||||
android:layout_marginStart="12dp"
|
android:layout_marginStart="12dp"
|
||||||
android:layout_marginEnd="10dp"
|
|
||||||
android:background="@drawable/bg_badge_default"
|
android:background="@drawable/bg_badge_default"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:minWidth="26dp"
|
android:minWidth="26dp"
|
||||||
android:textAlignment="center"
|
android:textAlignment="center"
|
||||||
android:textColor="?android:textColorSecondaryInverse"
|
android:textColor="?android:textColorSecondaryInverse"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:text="13" />
|
tools:text="13" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/textView_title"
|
android:id="@+id/textView_title"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_centerVertical="true"
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
android:layout_marginEnd="12dp"
|
android:layout_marginEnd="12dp"
|
||||||
android:layout_toEndOf="@id/textView_number"
|
android:layout_toEndOf="@id/textView_number"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:maxLines="2"
|
android:maxLines="1"
|
||||||
android:text="?android:textColorPrimary"
|
android:text="?android:textColorPrimary"
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/textView_description"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/textView_number"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:text="@tools:sample/lorem[15]" />
|
tools:text="@tools:sample/lorem[15]" />
|
||||||
|
|
||||||
</RelativeLayout>
|
<TextView
|
||||||
|
android:id="@+id/textView_description"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginEnd="12dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textColor="?attr/colorControlNormal"
|
||||||
|
android:textSize="12sp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/textView_number"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/textView_title"
|
||||||
|
tools:text="05.10.2021 • Scanlator" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@@ -4,9 +4,9 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical"
|
android:layout_marginHorizontal="32dp"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:layout_marginHorizontal="32dp">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/icon"
|
android:id="@+id/icon"
|
||||||
|
|||||||
2
app/src/main/res/values-ar/strings.xml
Normal file
2
app/src/main/res/values-ar/strings.xml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources></resources>
|
||||||
@@ -233,4 +233,10 @@
|
|||||||
<string name="genres">Géneros</string>
|
<string name="genres">Géneros</string>
|
||||||
<string name="text_search_holder_secondary">Intenta reformular la consulta.</string>
|
<string name="text_search_holder_secondary">Intenta reformular la consulta.</string>
|
||||||
<string name="text_clear_search_history_prompt">¿Realmente quiere eliminar todas las consultas de búsqueda recientes\? Esta acción no se puede deshacer.</string>
|
<string name="text_clear_search_history_prompt">¿Realmente quiere eliminar todas las consultas de búsqueda recientes\? Esta acción no se puede deshacer.</string>
|
||||||
|
<string name="state_finished">Terminado</string>
|
||||||
|
<string name="state_ongoing">En curso</string>
|
||||||
|
<string name="hide_toolbar">Ocultar la barra de herramientas al desplazarse</string>
|
||||||
|
<string name="chapter_is_missing_text">Este capítulo no aparece en su dispositivo. Descárguelo o léalo en línea.</string>
|
||||||
|
<string name="auth_complete">Autorización completa</string>
|
||||||
|
<string name="about_feedback_4pda">Tema sobre 4PDA</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -240,4 +240,6 @@
|
|||||||
<string name="protect_application">Suojaa sovellus</string>
|
<string name="protect_application">Suojaa sovellus</string>
|
||||||
<string name="wrong_password">Väärä salasana</string>
|
<string name="wrong_password">Väärä salasana</string>
|
||||||
<string name="enter_password">Syötä salasana</string>
|
<string name="enter_password">Syötä salasana</string>
|
||||||
|
<string name="state_finished">Päättynyt</string>
|
||||||
|
<string name="state_ongoing">Jatkuva</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -241,4 +241,6 @@
|
|||||||
<string name="genres">Жанры</string>
|
<string name="genres">Жанры</string>
|
||||||
<string name="state_finished">Завершено</string>
|
<string name="state_finished">Завершено</string>
|
||||||
<string name="state_ongoing">Онгоинг</string>
|
<string name="state_ongoing">Онгоинг</string>
|
||||||
|
<string name="date_format">Формат даты</string>
|
||||||
|
<string name="system_default">По умолчанию</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -8,7 +8,6 @@
|
|||||||
</declare-styleable>
|
</declare-styleable>
|
||||||
|
|
||||||
<!--CoverImageView attrs-->
|
<!--CoverImageView attrs-->
|
||||||
|
|
||||||
<declare-styleable name="CoverImageView">
|
<declare-styleable name="CoverImageView">
|
||||||
<attr name="android:orientation" />
|
<attr name="android:orientation" />
|
||||||
</declare-styleable>
|
</declare-styleable>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
<color name="color_primary">#1976D2</color>
|
<color name="color_primary">#1976D2</color>
|
||||||
<color name="color_primary_variant">#1565C0</color>
|
<color name="color_primary_variant">#1565C0</color>
|
||||||
<color name="color_on_secondary">@android:color/white</color>
|
<color name="color_on_secondary">@android:color/white</color>
|
||||||
|
<color name="color_primary_alpha">#1F1976D2</color>
|
||||||
|
|
||||||
<color name="color_control_light">#39000000</color>
|
<color name="color_control_light">#39000000</color>
|
||||||
<color name="color_surface">#FFFFFF</color>
|
<color name="color_surface">#FFFFFF</color>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
<dimen name="grid_spacing_outer">2dp</dimen>
|
<dimen name="grid_spacing_outer">2dp</dimen>
|
||||||
<dimen name="manga_list_item_height">86dp</dimen>
|
<dimen name="manga_list_item_height">86dp</dimen>
|
||||||
<dimen name="manga_list_details_item_height">120dp</dimen>
|
<dimen name="manga_list_details_item_height">120dp</dimen>
|
||||||
<dimen name="chapter_list_item_height">46dp</dimen>
|
<dimen name="chapter_list_item_height">56dp</dimen>
|
||||||
<dimen name="preferred_grid_width">120dp</dimen>
|
<dimen name="preferred_grid_width">120dp</dimen>
|
||||||
<dimen name="header_height">48dp</dimen>
|
<dimen name="header_height">48dp</dimen>
|
||||||
<dimen name="elevation_large">16dp</dimen>
|
<dimen name="elevation_large">16dp</dimen>
|
||||||
|
|||||||
@@ -242,4 +242,6 @@
|
|||||||
<string name="genres">Genres</string>
|
<string name="genres">Genres</string>
|
||||||
<string name="state_finished">Finished</string>
|
<string name="state_finished">Finished</string>
|
||||||
<string name="state_ongoing">Ongoing</string>
|
<string name="state_ongoing">Ongoing</string>
|
||||||
|
<string name="date_format">Date format</string>
|
||||||
|
<string name="system_default">Default</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -28,6 +28,7 @@
|
|||||||
<item name="tabTextColor">@color/tab_text</item>
|
<item name="tabTextColor">@color/tab_text</item>
|
||||||
<item name="tabTextAppearance">@style/TextAppearance.Kotatsu.Tab</item>
|
<item name="tabTextAppearance">@style/TextAppearance.Kotatsu.Tab</item>
|
||||||
<item name="tabIndicatorAnimationMode">elastic</item>
|
<item name="tabIndicatorAnimationMode">elastic</item>
|
||||||
|
<item name="tabRippleColor">@color/color_primary_alpha</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="Widget.Kotatsu.NavigationView" parent="@style/Widget.MaterialComponents.NavigationView">
|
<style name="Widget.Kotatsu.NavigationView" parent="@style/Widget.MaterialComponents.NavigationView">
|
||||||
|
|||||||
@@ -10,6 +10,10 @@
|
|||||||
<item name="colorOnSecondary">@color/color_on_secondary</item>
|
<item name="colorOnSecondary">@color/color_on_secondary</item>
|
||||||
<item name="colorControlLight">@color/color_control_light</item>
|
<item name="colorControlLight">@color/color_control_light</item>
|
||||||
|
|
||||||
|
<!-- Handles RTL text -->
|
||||||
|
<item name="android:textAlignment">gravity</item>
|
||||||
|
<item name="android:textDirection">locale</item>
|
||||||
|
|
||||||
<!-- Window decor -->
|
<!-- Window decor -->
|
||||||
<item name="android:windowLightStatusBar" tools:targetApi="m">@bool/use_light_status</item>
|
<item name="android:windowLightStatusBar" tools:targetApi="m">@bool/use_light_status</item>
|
||||||
<item name="android:statusBarColor">@color/status_bar_scrim</item>
|
<item name="android:statusBarColor">@color/status_bar_scrim</item>
|
||||||
|
|||||||
@@ -20,6 +20,11 @@
|
|||||||
android:title="@string/black_dark_theme"
|
android:title="@string/black_dark_theme"
|
||||||
app:iconSpaceReserved="false" />
|
app:iconSpaceReserved="false" />
|
||||||
|
|
||||||
|
<ListPreference
|
||||||
|
android:key="date_format"
|
||||||
|
android:title="@string/date_format"
|
||||||
|
app:iconSpaceReserved="false" />
|
||||||
|
|
||||||
<SwitchPreference
|
<SwitchPreference
|
||||||
android:defaultValue="true"
|
android:defaultValue="true"
|
||||||
android:key="hide_toolbar"
|
android:key="hide_toolbar"
|
||||||
|
|||||||
Reference in New Issue
Block a user