diff --git a/app/build.gradle b/app/build.gradle index 8094da3d7..b96db0bfa 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -81,7 +81,7 @@ afterEvaluate { } dependencies { //noinspection GradleDependency - implementation('com.github.KotatsuApp:kotatsu-parsers:06a043d290') { + implementation('com.github.KotatsuApp:kotatsu-parsers:a1598fd712') { exclude group: 'org.json', module: 'json' } @@ -127,8 +127,8 @@ dependencies { implementation 'com.hannesdorfmann:adapterdelegates4-kotlin-dsl:4.3.2' implementation 'com.hannesdorfmann:adapterdelegates4-kotlin-dsl-viewbinding:4.3.2' - implementation 'com.google.dagger:hilt-android:2.46.1' - kapt 'com.google.dagger:hilt-compiler:2.46.1' + implementation 'com.google.dagger:hilt-android:2.47' + kapt 'com.google.dagger:hilt-compiler:2.47' implementation 'androidx.hilt:hilt-work:1.0.0' kapt 'androidx.hilt:hilt-compiler:1.0.0' @@ -157,6 +157,6 @@ dependencies { androidTestImplementation 'androidx.room:room-testing:2.5.2' androidTestImplementation 'com.squareup.moshi:moshi-kotlin:1.15.0' - androidTestImplementation 'com.google.dagger:hilt-android-testing:2.46.1' - kaptAndroidTest 'com.google.dagger:hilt-android-compiler:2.46.1' + androidTestImplementation 'com.google.dagger:hilt-android-testing:2.47' + kaptAndroidTest 'com.google.dagger:hilt-android-compiler:2.47' } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/domain/Bookmark.kt b/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/domain/Bookmark.kt index 1ef35e5c2..ba53800d2 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/domain/Bookmark.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/domain/Bookmark.kt @@ -1,6 +1,7 @@ package org.koitharu.kotatsu.bookmarks.domain import org.koitharu.kotatsu.list.ui.model.ListModel +import org.koitharu.kotatsu.local.data.ImageFileFilter import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.MangaPage import java.util.Date @@ -37,7 +38,8 @@ class Bookmark( ) private fun isImageUrlDirect(): Boolean { - return imageUrl.substringAfterLast('.').length in 2..4 + val extension = imageUrl.substringAfterLast('.') + return extension.isNotEmpty() && ImageFileFilter().isExtensionValid(extension) } override fun equals(other: Any?): Boolean { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarkListAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarkListAD.kt index f8b9c411e..c9516e3d5 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarkListAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarkListAD.kt @@ -5,15 +5,19 @@ import coil.ImageLoader import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import org.koitharu.kotatsu.R import org.koitharu.kotatsu.bookmarks.domain.Bookmark +import org.koitharu.kotatsu.core.ui.drawable.TextDrawable import org.koitharu.kotatsu.core.ui.image.CoverSizeResolver import org.koitharu.kotatsu.core.ui.list.AdapterDelegateClickListenerAdapter import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener import org.koitharu.kotatsu.core.util.ext.decodeRegion import org.koitharu.kotatsu.core.util.ext.disposeImageRequest import org.koitharu.kotatsu.core.util.ext.enqueueWith +import org.koitharu.kotatsu.core.util.ext.getThemeResId import org.koitharu.kotatsu.core.util.ext.newImageRequest import org.koitharu.kotatsu.core.util.ext.source import org.koitharu.kotatsu.databinding.ItemBookmarkBinding +import org.koitharu.kotatsu.parsers.util.format +import com.google.android.material.R as materialR fun bookmarkListAD( coil: ImageLoader, diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/drawable/TextDrawable.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/drawable/TextDrawable.kt new file mode 100644 index 000000000..21b5835e7 --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/drawable/TextDrawable.kt @@ -0,0 +1,100 @@ +package org.koitharu.kotatsu.core.ui.drawable + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.ColorFilter +import android.graphics.Paint +import android.graphics.PixelFormat +import android.graphics.Typeface +import android.graphics.drawable.Drawable +import android.os.Build +import android.text.Layout +import android.text.StaticLayout +import android.text.TextPaint +import androidx.annotation.ColorInt +import androidx.annotation.Px +import androidx.annotation.StyleRes +import androidx.core.graphics.withTranslation +import com.google.android.material.resources.TextAppearance +import com.google.android.material.resources.TextAppearanceFontCallback +import org.koitharu.kotatsu.core.util.ext.getThemeColor + +class TextDrawable( + val text: CharSequence, +) : Drawable() { + + private val paint = TextPaint(Paint.ANTI_ALIAS_FLAG) + private var cachedLayout: StaticLayout? = null + + @SuppressLint("RestrictedApi") + constructor(context: Context, text: CharSequence, @StyleRes textAppearanceId: Int) : this(text) { + val ta = TextAppearance(context, textAppearanceId) + paint.color = ta.textColor?.defaultColor ?: context.getThemeColor(android.R.attr.textColorPrimary, Color.BLACK) + paint.typeface = ta.fallbackFont + ta.getFontAsync( + context, paint, + object : TextAppearanceFontCallback() { + override fun onFontRetrieved(typeface: Typeface?, fontResolvedSynchronously: Boolean) = Unit + override fun onFontRetrievalFailed(reason: Int) = Unit + }, + ) + paint.letterSpacing = ta.letterSpacing + } + + var alignment = Layout.Alignment.ALIGN_NORMAL + + var lineSpacingMultiplier = 1f + + @Px + var lineSpacingExtra = 0f + + @get:ColorInt + var textColor: Int + get() = paint.color + set(@ColorInt value) { + paint.color = value + } + + override fun draw(canvas: Canvas) { + val b = bounds + if (b.isEmpty) { + return + } + canvas.withTranslation(x = b.left.toFloat(), y = b.top.toFloat()) { + obtainLayout().draw(canvas) + } + } + + override fun setAlpha(alpha: Int) { + paint.alpha = alpha + } + + override fun setColorFilter(colorFilter: ColorFilter?) { + paint.setColorFilter(colorFilter) + } + + @Suppress("DeprecatedCallableAddReplaceWith") + @Deprecated("Deprecated in Java") + override fun getOpacity(): Int = PixelFormat.TRANSLUCENT + + private fun obtainLayout(): StaticLayout { + val width = bounds.width() + cachedLayout?.let { + if (it.width == width) { + return it + } + } + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + StaticLayout.Builder.obtain(text, 0, text.length, paint, width) + .setAlignment(alignment) + .setLineSpacing(lineSpacingExtra, lineSpacingMultiplier) + .setIncludePad(true) + .build() + } else { + @Suppress("DEPRECATION") + StaticLayout(text, paint, width, alignment, lineSpacingMultiplier, lineSpacingExtra, true) + }.also { cachedLayout = it } + } +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Theme.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Theme.kt index 4ce515d9b..23ae5e3c9 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Theme.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Theme.kt @@ -66,6 +66,13 @@ fun Context.getThemeColorStateList( it.getColorStateList(0) } +fun Context.getThemeResId( + @AttrRes resId: Int, + fallback: Int +): Int = obtainStyledAttributes(intArrayOf(resId)).use { + it.getResourceId(0, fallback) +} + fun TypedArray.getDrawableCompat(context: Context, index: Int): Drawable? { val resId = getResourceId(index, 0) return if (resId != 0) ContextCompat.getDrawable(context, resId) else null diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/ImageFileFilter.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/ImageFileFilter.kt index 29b946b0b..4dc76c9f6 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/ImageFileFilter.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/ImageFileFilter.kt @@ -23,7 +23,7 @@ class ImageFileFilter : FilenameFilter, FileFilter { return isExtensionValid(ext) } - private fun isExtensionValid(ext: String): Boolean { + fun isExtensionValid(ext: String): Boolean { return ext == "png" || ext == "jpg" || ext == "jpeg" || ext == "webp" } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/anilist/data/ScoreFormat.kt b/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/anilist/data/ScoreFormat.kt index 47b7bcadf..45a1ad85d 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/anilist/data/ScoreFormat.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/anilist/data/ScoreFormat.kt @@ -13,7 +13,7 @@ enum class ScoreFormat { POINT_5 -> score / 5f POINT_3 -> score / 3f - } + }.coerceIn(0f, 1f) companion object { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/mal/data/MALRepository.kt b/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/mal/data/MALRepository.kt index 877cbabf5..84d39309f 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/mal/data/MALRepository.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/mal/data/MALRepository.kt @@ -173,7 +173,7 @@ class MALRepository @Inject constructor( status = json.getString("status"), chapter = json.getInt("num_chapters_read"), comment = json.getString("comments"), - rating = json.getDouble("score").toFloat() / 10f, + rating = (json.getDouble("score").toFloat() / 10f).coerceIn(0f, 1f), ) db.scrobblingDao.upsert(entity) } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/shikimori/data/ShikimoriRepository.kt b/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/shikimori/data/ShikimoriRepository.kt index e61791deb..3f833596b 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/shikimori/data/ShikimoriRepository.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/shikimori/data/ShikimoriRepository.kt @@ -190,7 +190,7 @@ class ShikimoriRepository @Inject constructor( status = json.getString("status"), chapter = json.getInt("chapters"), comment = json.getString("text"), - rating = json.getDouble("score").toFloat() / 10f, + rating = (json.getDouble("score").toFloat() / 10f).coerceIn(0f, 1f), ) db.scrobblingDao.upsert(entity) } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/shelf/domain/ShelfContentObserveUseCase.kt b/app/src/main/kotlin/org/koitharu/kotatsu/shelf/domain/ShelfContentObserveUseCase.kt index b3f23a9c6..6bdaf89ac 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/shelf/domain/ShelfContentObserveUseCase.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/shelf/domain/ShelfContentObserveUseCase.kt @@ -13,7 +13,6 @@ import kotlinx.coroutines.flow.onStart import org.koitharu.kotatsu.core.db.MangaDatabase import org.koitharu.kotatsu.core.model.FavouriteCategory import org.koitharu.kotatsu.core.prefs.AppSettings -import org.koitharu.kotatsu.core.prefs.observeAsFlow import org.koitharu.kotatsu.favourites.data.FavouriteCategoryEntity import org.koitharu.kotatsu.favourites.data.toFavouriteCategory import org.koitharu.kotatsu.favourites.data.toMangaList @@ -52,8 +51,8 @@ class ShelfContentObserveUseCase @Inject constructor( private fun observeLocalManga(sortOrder: SortOrder, limit: Int): Flow> { return combine( localStorageChanges, - settings.observe().filter { it == AppSettings.KEY_LOCAL_MANGA_DIRS } - ) { _, _ -> Any() } + settings.observe().filter { it == AppSettings.KEY_LOCAL_MANGA_DIRS }.onStart { emit("") } + ) { a, b -> a to b } .onStart { emit(null) } .mapLatest { localMangaRepository.getList(0, null, sortOrder).take(limit) diff --git a/app/src/main/res/values-ne/strings.xml b/app/src/main/res/values-ne/strings.xml index 344c263f6..4253ae3d5 100644 --- a/app/src/main/res/values-ne/strings.xml +++ b/app/src/main/res/values-ne/strings.xml @@ -225,4 +225,8 @@ विभिन्न भाषाहरू अध्याय खोज्नुहोस् उपस्थिति + हालैको मंगा सर्टकट देखाउनुहोस् + दायाँ किनारामा ट्याप गर्नुहोस् वा दायाँ कुञ्जी थिच्दा सधैं अर्को पृष्ठमा स्विच हुन्छ + Ergonomic पाठक नियन्त्रण + त्रुटि विवरण:<br> <tt>%1$s</tt><br><br> 1. <a href=%2$s>वेब ब्राउजरमा मंगा खोल्ने</a> प्रयास गर्नुहोस् कि यो यसको स्रोतमा उपलब्ध छ<br> 2. निश्चित गर्नुहोस् कि तपाइँ <a href=kotatsu://about>Kotatsu को नवीनतम संस्करण</a> प्रयोग गर्दै हुनुहुन्छ<br> 3. यदि यो उपलब्ध छ भने, विकासकर्ताहरूलाई त्रुटि रिपोर्ट पठाउनुहोस्। \ No newline at end of file diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 36bd451aa..18a373ee1 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -389,7 +389,7 @@ Địa chỉ Đảo màu Cổng không hợp lệ - Chọn các chương bằng tay + Chọn thủ công các chương Tất cả các chương chưa đọc Tiếp tục Xoá thông tin về chương mới diff --git a/build.gradle b/build.gradle index f0ec27d8a..4f5a72761 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { dependencies { classpath 'com.android.tools.build:gradle:8.0.2' classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.22' - classpath 'com.google.dagger:hilt-android-gradle-plugin:2.46.1' + classpath 'com.google.dagger:hilt-android-gradle-plugin:2.47' } }