Merge branch 'devel' into feature/nextgen

pull/163/head
Koitharu 4 years ago
commit b7442fe445
No known key found for this signature in database
GPG Key ID: 8E861F8CE6E7CE27

@ -10,9 +10,9 @@ Kotatsu is a free and open source manga reader for Android.
alt="Get it on F-Droid" alt="Get it on F-Droid"
height="80">](https://f-droid.org/packages/org.koitharu.kotatsu) 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 ### Main Features
@ -24,8 +24,8 @@ Download APK from GitHub Releases:
* Tablet-optimized material design UI * Tablet-optimized material design UI
* Standard and Webtoon-optimized reader * Standard and Webtoon-optimized reader
* Notifications about new chapters with updates feed * Notifications about new chapters with updates feed
* Available in multiple languages * Shikimori integration (manga tracking)
* Password protect access to the app * Password/fingerprint protect access to the app
### Screenshots ### Screenshots
@ -38,23 +38,20 @@ Download APK from GitHub Releases:
### Localization ### Localization
<a href="https://hosted.weblate.org/engage/kotatsu/"> [<img src="https://hosted.weblate.org/widgets/kotatsu/-/287x66-white.png" alt="Translation status">](https://hosted.weblate.org/engage/kotatsu/)
<img src="https://hosted.weblate.org/widgets/kotatsu/-/287x66-white.png" alt="Translation status" />
</a>
Kotatsu is localized in a number of different languages, if you would like to help improve these or add new languages, 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 ### License
[![GNU GPLv3 Image](https://www.gnu.org/graphics/gplv3-127x51.png)](http://www.gnu.org/licenses/gpl-3.0.en.html) [![GNU GPLv3 Image](https://www.gnu.org/graphics/gplv3-127x51.png)](http://www.gnu.org/licenses/gpl-3.0.en.html)
Kotatsu is Free Software: You can use, study share and improve it at your You may copy, distribute and modify the software as long as you track changes/dates in source files. Any modifications
will. Specifically you can redistribute and/or modify it under the terms of the to or software including (via compiler) GPL-licensed code must also be made available under the GPL along with build &
[GNU General Public License](https://www.gnu.org/licenses/gpl.html) as install instructions.
published by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
### 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' applicationId 'org.koitharu.kotatsu'
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 32 targetSdkVersion 32
versionCode 412 versionCode 413
versionName '3.4' versionName '3.4.1'
generatedDensities = [] generatedDensities = []
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@ -77,7 +77,7 @@ afterEvaluate {
} }
dependencies { dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar']) 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' 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.BaseReader
import org.koitharu.kotatsu.reader.ui.pager.BaseReaderAdapter import org.koitharu.kotatsu.reader.ui.pager.BaseReaderAdapter
import org.koitharu.kotatsu.reader.ui.pager.ReaderPage 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.findCenterViewPosition
import org.koitharu.kotatsu.utils.ext.firstVisibleItemPosition import org.koitharu.kotatsu.utils.ext.firstVisibleItemPosition
import org.koitharu.kotatsu.utils.ext.viewLifecycleScope import org.koitharu.kotatsu.utils.ext.viewLifecycleScope
@ -33,7 +32,7 @@ class WebtoonReaderFragment : BaseReader<FragmentReaderWebtoonBinding>() {
with(binding.recyclerView) { with(binding.recyclerView) {
setHasFixedSize(true) setHasFixedSize(true)
adapter = webtoonAdapter adapter = webtoonAdapter
doOnCurrentItemChanged(::notifyPageChanged) addOnPageScrollListener(PageScrollListener())
} }
} }
@ -93,4 +92,12 @@ class WebtoonReaderFragment : BaseReader<FragmentReaderWebtoonBinding>() {
override fun switchPageTo(position: Int, smooth: Boolean) { override fun switchPageTo(position: Int, smooth: Boolean) {
binding.recyclerView.firstVisibleItemPosition = position 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.content.Context
import android.util.AttributeSet import android.util.AttributeSet
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat.TYPE_TOUCH
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import org.koitharu.kotatsu.utils.ext.findCenterViewPosition
import java.util.*
class WebtoonRecyclerView @JvmOverloads constructor( class WebtoonRecyclerView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : RecyclerView(context, attrs, defStyleAttr) { ) : 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 { override fun startNestedScroll(axes: Int) = startNestedScroll(axes, TYPE_TOUCH)
return true
} override fun startNestedScroll(axes: Int, type: Int): Boolean = true
override fun dispatchNestedPreScroll( override fun dispatchNestedPreScroll(
dx: Int, dx: Int,
dy: Int, dy: Int,
consumed: IntArray?, consumed: IntArray?,
offsetInWindow: IntArray? offsetInWindow: IntArray?
) = dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow, ViewCompat.TYPE_TOUCH) ) = dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow, TYPE_TOUCH)
override fun dispatchNestedPreScroll( override fun dispatchNestedPreScroll(
dx: Int, dx: Int,
@ -34,6 +36,7 @@ class WebtoonRecyclerView @JvmOverloads constructor(
consumed[0] = 0 consumed[0] = 0
consumed[1] = consumedY consumed[1] = consumedY
} }
notifyScrollChanged(dy)
return consumedY != 0 || dy == 0 return consumedY != 0 || dy == 0
} }
@ -75,4 +78,39 @@ class WebtoonRecyclerView @JvmOverloads constructor(
} }
return 0 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 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 { fun RecyclerView.findCenterViewPosition(): Int {
val centerX = width / 2f val centerX = width / 2f
val centerY = height / 2f val centerY = height / 2f

@ -42,9 +42,8 @@
<LinearLayout <LinearLayout
android:id="@+id/layout_titles" android:id="@+id/layout_titles"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="@id/imageView_cover"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/imageView_cover" app:layout_constraintStart_toEndOf="@id/imageView_cover"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
@ -151,6 +150,14 @@
</LinearLayout> </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 <org.koitharu.kotatsu.base.ui.widgets.ChipsView
android:id="@+id/chips_tags" android:id="@+id/chips_tags"
android:layout_width="0dp" android:layout_width="0dp"
@ -162,7 +169,7 @@
app:chipSpacingVertical="6dp" app:chipSpacingVertical="6dp"
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/layout_titles" /> app:layout_constraintTop_toBottomOf="@+id/barrier_header" />
<TextView <TextView
android:id="@+id/textView_bookmarks" android:id="@+id/textView_bookmarks"
@ -223,7 +230,7 @@
android:textIsSelectable="true" android:textIsSelectable="true"
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/recyclerView_bookmarks" app:layout_constraintTop_toBottomOf="@id/scrobbling_layout"
tools:ignore="UnusedAttribute" tools:ignore="UnusedAttribute"
tools:text="@tools:sample/lorem/random[250]" /> tools:text="@tools:sample/lorem/random[250]" />

@ -6,4 +6,4 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" 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="use_fingerprint">Использовать отпечаток пальца, если доступно</string>
<string name="appwidget_shelf_description">Манга из Вашего избранного</string> <string name="appwidget_shelf_description">Манга из Вашего избранного</string>
<string name="appwidget_recent_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> </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:** **Main Features:**
- Online manga catalogues - Online manga catalogues
@ -9,5 +11,5 @@ Kotatsu is a free and open source manga reader for Android.<br>
- Tablet-optimized material design UI - Tablet-optimized material design UI
- Standard and Webtoon-optimized reader - Standard and Webtoon-optimized reader
- Notifications about new chapters with updates feed - Notifications about new chapters with updates feed
- Available in multiple languages - Shikimori integration (manga tracking)
- Password protect access to the app - Password/fingerprint protect access to the app

@ -1,4 +1,6 @@
Kotatsu - приложения для чтения манги с открытым исходным кодом.<br> Kotatsu - приложения для чтения манги с открытым исходным кодом.
**Основные возможности:** **Основные возможности:**
- Онлайн каталоги с мангой - Онлайн каталоги с мангой
@ -9,3 +11,5 @@ Kotatsu - приложения для чтения манги с открыты
- Интерфейс также оптимизирован для планшетов - Интерфейс также оптимизирован для планшетов
- Поддержка манхвы (Webtoon) - Поддержка манхвы (Webtoon)
- Уведомления о новых главах и лента обновлений - Уведомления о новых главах и лента обновлений
- Интеграция с Shikimori
- Защита доступа в приложение паролем/отпечатком пальца

Loading…
Cancel
Save