Merge branch 'devel' into feature/nextgen
This commit is contained in:
27
README.md
27
README.md
@@ -10,9 +10,9 @@ Kotatsu is a free and open source manga reader for Android.
|
||||
alt="Get it on F-Droid"
|
||||
height="80">](https://f-droid.org/packages/org.koitharu.kotatsu)
|
||||
|
||||
Download APK from GitHub Releases:
|
||||
Download APK directly from GitHub:
|
||||
|
||||
- [Latest release](https://github.com/KotatsuApp/Kotatsu/releases/latest)
|
||||
- **[Latest release](https://github.com/KotatsuApp/Kotatsu/releases/latest)**
|
||||
|
||||
### Main Features
|
||||
|
||||
@@ -24,8 +24,8 @@ Download APK from GitHub Releases:
|
||||
* Tablet-optimized material design UI
|
||||
* Standard and Webtoon-optimized reader
|
||||
* Notifications about new chapters with updates feed
|
||||
* Available in multiple languages
|
||||
* Password protect access to the app
|
||||
* Shikimori integration (manga tracking)
|
||||
* Password/fingerprint protect access to the app
|
||||
|
||||
### Screenshots
|
||||
|
||||
@@ -38,23 +38,20 @@ Download APK from GitHub Releases:
|
||||
|
||||
### Localization
|
||||
|
||||
<a href="https://hosted.weblate.org/engage/kotatsu/">
|
||||
<img src="https://hosted.weblate.org/widgets/kotatsu/-/287x66-white.png" alt="Translation status" />
|
||||
</a>
|
||||
[<img src="https://hosted.weblate.org/widgets/kotatsu/-/287x66-white.png" alt="Translation status">](https://hosted.weblate.org/engage/kotatsu/)
|
||||
|
||||
Kotatsu is localized in a number of different languages, if you would like to help improve these or add new languages,
|
||||
please head over to the Weblate <a href="https://hosted.weblate.org/engage/kotatsu/">project page</a>
|
||||
please head over to the [Weblate project page](https://hosted.weblate.org/engage/kotatsu/)
|
||||
|
||||
### License
|
||||
|
||||
[](http://www.gnu.org/licenses/gpl-3.0.en.html)
|
||||
|
||||
Kotatsu is Free Software: You can use, study share and improve it at your
|
||||
will. Specifically you can redistribute and/or modify it under the terms of the
|
||||
[GNU General Public License](https://www.gnu.org/licenses/gpl.html) as
|
||||
published by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
You may copy, distribute and modify the software as long as you track changes/dates in source files. Any modifications
|
||||
to or software including (via compiler) GPL-licensed code must also be made available under the GPL along with build &
|
||||
install instructions.
|
||||
|
||||
### Disclaimer
|
||||
### DMCA disclaimer
|
||||
|
||||
The developers of this application does not have any affiliation with the content providers available.
|
||||
The developers of this application does not have any affiliation with the content available in the app.
|
||||
It is collecting from the sources freely available through any web browser.
|
||||
|
||||
@@ -14,8 +14,8 @@ android {
|
||||
applicationId 'org.koitharu.kotatsu'
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 32
|
||||
versionCode 412
|
||||
versionName '3.4'
|
||||
versionCode 413
|
||||
versionName '3.4.1'
|
||||
generatedDensities = []
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
@@ -77,7 +77,7 @@ afterEvaluate {
|
||||
}
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
|
||||
implementation('com.github.nv95:kotatsu-parsers:da3b0ae0cf') {
|
||||
implementation('com.github.nv95:kotatsu-parsers:8c26f3c790') {
|
||||
exclude group: 'org.json', module: 'json'
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package org.koitharu.kotatsu.reader.ui.pager.webtoon
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlin.math.sign
|
||||
|
||||
class WebtoonLayoutManager : LinearLayoutManager {
|
||||
|
||||
private var scrollDirection: Int = 0
|
||||
|
||||
constructor(context: Context) : super(context)
|
||||
constructor(
|
||||
context: Context,
|
||||
orientation: Int,
|
||||
reverseLayout: Boolean,
|
||||
) : super(context, orientation, reverseLayout)
|
||||
|
||||
constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet?,
|
||||
defStyleAttr: Int,
|
||||
defStyleRes: Int,
|
||||
) : super(context, attrs, defStyleAttr, defStyleRes)
|
||||
|
||||
override fun scrollVerticallyBy(dy: Int, recycler: RecyclerView.Recycler?, state: RecyclerView.State): Int {
|
||||
scrollDirection = dy.sign
|
||||
return super.scrollVerticallyBy(dy, recycler, state)
|
||||
}
|
||||
|
||||
override fun calculateExtraLayoutSpace(state: RecyclerView.State, extraLayoutSpace: IntArray) {
|
||||
if (state.hasTargetScrollPosition()) {
|
||||
super.calculateExtraLayoutSpace(state, extraLayoutSpace)
|
||||
return
|
||||
}
|
||||
val pageSize = height
|
||||
extraLayoutSpace[0] = if (scrollDirection < 0) pageSize else 0
|
||||
extraLayoutSpace[1] = if (scrollDirection < 0) 0 else pageSize
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,6 @@ import org.koitharu.kotatsu.reader.ui.ReaderState
|
||||
import org.koitharu.kotatsu.reader.ui.pager.BaseReader
|
||||
import org.koitharu.kotatsu.reader.ui.pager.BaseReaderAdapter
|
||||
import org.koitharu.kotatsu.reader.ui.pager.ReaderPage
|
||||
import org.koitharu.kotatsu.utils.ext.doOnCurrentItemChanged
|
||||
import org.koitharu.kotatsu.utils.ext.findCenterViewPosition
|
||||
import org.koitharu.kotatsu.utils.ext.firstVisibleItemPosition
|
||||
import org.koitharu.kotatsu.utils.ext.viewLifecycleScope
|
||||
@@ -33,7 +32,7 @@ class WebtoonReaderFragment : BaseReader<FragmentReaderWebtoonBinding>() {
|
||||
with(binding.recyclerView) {
|
||||
setHasFixedSize(true)
|
||||
adapter = webtoonAdapter
|
||||
doOnCurrentItemChanged(::notifyPageChanged)
|
||||
addOnPageScrollListener(PageScrollListener())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,4 +92,12 @@ class WebtoonReaderFragment : BaseReader<FragmentReaderWebtoonBinding>() {
|
||||
override fun switchPageTo(position: Int, smooth: Boolean) {
|
||||
binding.recyclerView.firstVisibleItemPosition = position
|
||||
}
|
||||
|
||||
private inner class PageScrollListener : WebtoonRecyclerView.OnPageScrollListener() {
|
||||
|
||||
override fun onPageChanged(recyclerView: WebtoonRecyclerView, index: Int) {
|
||||
super.onPageChanged(recyclerView, index)
|
||||
notifyPageChanged(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,25 +2,27 @@ package org.koitharu.kotatsu.reader.ui.pager.webtoon
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.ViewCompat.TYPE_TOUCH
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.koitharu.kotatsu.utils.ext.findCenterViewPosition
|
||||
import java.util.*
|
||||
|
||||
class WebtoonRecyclerView @JvmOverloads constructor(
|
||||
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
||||
) : RecyclerView(context, attrs, defStyleAttr) {
|
||||
|
||||
override fun startNestedScroll(axes: Int) = startNestedScroll(axes, ViewCompat.TYPE_TOUCH)
|
||||
private var onPageScrollListeners: MutableList<OnPageScrollListener>? = null
|
||||
|
||||
override fun startNestedScroll(axes: Int, type: Int): Boolean {
|
||||
return true
|
||||
}
|
||||
override fun startNestedScroll(axes: Int) = startNestedScroll(axes, TYPE_TOUCH)
|
||||
|
||||
override fun startNestedScroll(axes: Int, type: Int): Boolean = true
|
||||
|
||||
override fun dispatchNestedPreScroll(
|
||||
dx: Int,
|
||||
dy: Int,
|
||||
consumed: IntArray?,
|
||||
offsetInWindow: IntArray?
|
||||
) = dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow, ViewCompat.TYPE_TOUCH)
|
||||
) = dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow, TYPE_TOUCH)
|
||||
|
||||
override fun dispatchNestedPreScroll(
|
||||
dx: Int,
|
||||
@@ -34,6 +36,7 @@ class WebtoonRecyclerView @JvmOverloads constructor(
|
||||
consumed[0] = 0
|
||||
consumed[1] = consumedY
|
||||
}
|
||||
notifyScrollChanged(dy)
|
||||
return consumedY != 0 || dy == 0
|
||||
}
|
||||
|
||||
@@ -75,4 +78,39 @@ class WebtoonRecyclerView @JvmOverloads constructor(
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
fun addOnPageScrollListener(listener: OnPageScrollListener) {
|
||||
val list = onPageScrollListeners ?: LinkedList<OnPageScrollListener>().also { onPageScrollListeners = it }
|
||||
list.add(listener)
|
||||
}
|
||||
|
||||
fun removeOnPageScrollListener(listener: OnPageScrollListener) {
|
||||
onPageScrollListeners?.remove(listener)
|
||||
}
|
||||
|
||||
private fun notifyScrollChanged(dy: Int) {
|
||||
val listeners = onPageScrollListeners
|
||||
if (listeners.isNullOrEmpty()) {
|
||||
return
|
||||
}
|
||||
val centerPosition = findCenterViewPosition()
|
||||
listeners.forEach { it.dispatchScroll(this, dy, centerPosition) }
|
||||
}
|
||||
|
||||
abstract class OnPageScrollListener {
|
||||
|
||||
private var lastPosition = NO_POSITION
|
||||
|
||||
fun dispatchScroll(recyclerView: WebtoonRecyclerView, dy: Int, centerPosition: Int) {
|
||||
onScroll(recyclerView, dy)
|
||||
if (centerPosition != NO_POSITION && centerPosition != lastPosition) {
|
||||
lastPosition = centerPosition
|
||||
onPageChanged(recyclerView, centerPosition)
|
||||
}
|
||||
}
|
||||
|
||||
open fun onScroll(recyclerView: WebtoonRecyclerView, dy: Int) = Unit
|
||||
|
||||
open fun onPageChanged(recyclerView: WebtoonRecyclerView, index: Int) = Unit
|
||||
}
|
||||
}
|
||||
@@ -91,22 +91,6 @@ fun View.resetTransformations() {
|
||||
rotationY = 0f
|
||||
}
|
||||
|
||||
inline fun RecyclerView.doOnCurrentItemChanged(crossinline callback: (Int) -> Unit) {
|
||||
addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
|
||||
private var lastItem = -1
|
||||
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
val item = recyclerView.findCenterViewPosition()
|
||||
if (item != RecyclerView.NO_POSITION && item != lastItem) {
|
||||
lastItem = item
|
||||
callback(item)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun RecyclerView.findCenterViewPosition(): Int {
|
||||
val centerX = width / 2f
|
||||
val centerY = height / 2f
|
||||
|
||||
@@ -42,9 +42,8 @@
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_titles"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toBottomOf="@id/imageView_cover"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/imageView_cover"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
@@ -151,6 +150,14 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:id="@+id/barrier_header"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:barrierDirection="bottom"
|
||||
app:barrierMargin="8dp"
|
||||
app:constraint_referenced_ids="layout_titles,imageView_cover" />
|
||||
|
||||
<org.koitharu.kotatsu.base.ui.widgets.ChipsView
|
||||
android:id="@+id/chips_tags"
|
||||
android:layout_width="0dp"
|
||||
@@ -162,7 +169,7 @@
|
||||
app:chipSpacingVertical="6dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/layout_titles" />
|
||||
app:layout_constraintTop_toBottomOf="@+id/barrier_header" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_bookmarks"
|
||||
@@ -223,7 +230,7 @@
|
||||
android:textIsSelectable="true"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/recyclerView_bookmarks"
|
||||
app:layout_constraintTop_toBottomOf="@id/scrobbling_layout"
|
||||
tools:ignore="UnusedAttribute"
|
||||
tools:text="@tools:sample/lorem/random[250]" />
|
||||
|
||||
|
||||
@@ -6,4 +6,4 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
|
||||
app:layoutManager="org.koitharu.kotatsu.reader.ui.pager.webtoon.WebtoonLayoutManager" />
|
||||
@@ -302,4 +302,19 @@
|
||||
<string name="use_fingerprint">Использовать отпечаток пальца, если доступно</string>
|
||||
<string name="appwidget_shelf_description">Манга из Вашего избранного</string>
|
||||
<string name="appwidget_recent_description">Манга, которую Вы недавно читали</string>
|
||||
<string name="status_reading">Читаю</string>
|
||||
<string name="status_planned">Запланировано</string>
|
||||
<string name="status_on_hold">Отложено</string>
|
||||
<string name="status_dropped">Заброшено</string>
|
||||
<string name="status_completed">Завершено</string>
|
||||
<string name="show_reading_indicators_summary">Показать процент прочитанного в истории и избранном</string>
|
||||
<string name="exclude_nsfw_from_history_summary">Манга, помеченная как NSFW, никогда не будет добавлена в историю и ваш прогресс чтения не будет сохранен</string>
|
||||
<string name="percent_string_pattern">%1$s%%</string>
|
||||
<string name="report">Отчёт</string>
|
||||
<string name="logout">Выйти</string>
|
||||
<string name="status_re_reading">Перечитываю</string>
|
||||
<string name="show_reading_indicators">Показать индикаторы прогресса чтения</string>
|
||||
<string name="data_deletion">Удаление данных</string>
|
||||
<string name="clear_cookies_summary">Может помочь в случае каких-либо проблем. Все авторизации будут аннулированы</string>
|
||||
<string name="show_all">Показать все</string>
|
||||
</resources>
|
||||
@@ -1,4 +1,6 @@
|
||||
Kotatsu is a free and open source manga reader for Android.<br>
|
||||
Kotatsu is a free and open source manga reader for Android.
|
||||
|
||||
|
||||
**Main Features:**
|
||||
|
||||
- Online manga catalogues
|
||||
@@ -9,5 +11,5 @@ Kotatsu is a free and open source manga reader for Android.<br>
|
||||
- Tablet-optimized material design UI
|
||||
- Standard and Webtoon-optimized reader
|
||||
- Notifications about new chapters with updates feed
|
||||
- Available in multiple languages
|
||||
- Password protect access to the app
|
||||
- Shikimori integration (manga tracking)
|
||||
- Password/fingerprint protect access to the app
|
||||
@@ -1,4 +1,6 @@
|
||||
Kotatsu - приложения для чтения манги с открытым исходным кодом.<br>
|
||||
Kotatsu - приложения для чтения манги с открытым исходным кодом.
|
||||
|
||||
|
||||
**Основные возможности:**
|
||||
|
||||
- Онлайн каталоги с мангой
|
||||
@@ -9,3 +11,5 @@ Kotatsu - приложения для чтения манги с открыты
|
||||
- Интерфейс также оптимизирован для планшетов
|
||||
- Поддержка манхвы (Webtoon)
|
||||
- Уведомления о новых главах и лента обновлений
|
||||
- Интеграция с Shikimori
|
||||
- Защита доступа в приложение паролем/отпечатком пальца
|
||||
|
||||
Reference in New Issue
Block a user