diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/GroupleRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/GroupleRepository.kt index 0aeb751e1..b353aae80 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/GroupleRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/GroupleRepository.kt @@ -13,6 +13,7 @@ import java.util.* private const val PAGE_SIZE = 70 private const val PAGE_SIZE_SEARCH = 50 +private const val NSFW_ALERT = "сексуальные сцены" abstract class GroupleRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepository(loaderContext) { @@ -131,6 +132,7 @@ abstract class GroupleRepository(loaderContext: MangaLoaderContext) : source = source ) }, + isNsfw = root.select(".alert-warning").any { it.ownText().contains(NSFW_ALERT) }, chapters = root.selectFirst("div.chapters-link")?.selectFirst("table") ?.select("tr:has(td > a)")?.asReversed()?.mapIndexedNotNull { i, tr -> val a = tr.selectFirst("a") ?: return@mapIndexedNotNull null diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/HentaiLibRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/HentaiLibRepository.kt index 2a089cb6a..c15eca844 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/HentaiLibRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/HentaiLibRepository.kt @@ -1,5 +1,6 @@ package org.koitharu.kotatsu.core.parser.site +import org.jsoup.nodes.Document import org.koitharu.kotatsu.base.domain.MangaLoaderContext import org.koitharu.kotatsu.core.model.MangaSource @@ -9,4 +10,5 @@ class HentaiLibRepository(loaderContext: MangaLoaderContext) : MangaLibRepositor override val source = MangaSource.HENTAILIB + override fun isNsfw(doc: Document) = true } diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaLibRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaLibRepository.kt index 87868b327..d25c7a959 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaLibRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaLibRepository.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.core.parser.site import androidx.collection.ArraySet import org.json.JSONArray import org.json.JSONObject +import org.jsoup.nodes.Document import org.koitharu.kotatsu.base.domain.MangaLoaderContext import org.koitharu.kotatsu.core.exceptions.AuthRequiredException import org.koitharu.kotatsu.core.exceptions.ParseException @@ -148,6 +149,7 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) : source = source ) } ?: manga.tags, + isNsfw = isNsfw(doc), description = info?.selectFirst("div.media-description__text")?.html(), chapters = chapters ) @@ -230,6 +232,11 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) : return body.selectFirst(".profile-user__username")?.text() ?: parseFailed("Cannot find username") } + protected open fun isNsfw(doc: Document): Boolean { + val sidebar = doc.body().selectFirst(".media-sidebar") ?: parseFailed("Sidebar not found") + return sidebar.getElementsContainingOwnText("18+").isNotEmpty() + } + private fun getSortKey(sortOrder: SortOrder?) = when (sortOrder) { SortOrder.RATING -> "desc&sort=rate" SortOrder.ALPHABETICAL -> "asc&sort=name" diff --git a/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsFragment.kt index 98425c798..3cd328ce2 100644 --- a/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsFragment.kt @@ -7,7 +7,7 @@ import android.text.method.LinkMovementMethod import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.core.content.res.ResourcesCompat +import androidx.core.content.ContextCompat import androidx.core.graphics.Insets import androidx.core.net.toUri import androidx.core.text.parseAsHtml @@ -52,7 +52,7 @@ class DetailsFragment : BaseFragment(), View.OnClickList binding.buttonFavorite.setOnClickListener(this) binding.buttonRead.setOnClickListener(this) binding.buttonRead.setOnLongClickListener(this) - binding.coverCard.setOnClickListener(this) + binding.imageViewCover.setOnClickListener(this) binding.textViewDescription.movementMethod = LinkMovementMethod.getInstance() viewModel.manga.observe(viewLifecycleOwner, ::onMangaUpdated) viewModel.isLoading.observe(viewLifecycleOwner, ::onLoadingStateChanged) @@ -67,61 +67,61 @@ class DetailsFragment : BaseFragment(), View.OnClickList textViewTitle.text = manga.title textViewSubtitle.textAndVisible = manga.altTitle textViewAuthor.textAndVisible = manga.author - sourceContainer.isVisible = manga.source != MangaSource.LOCAL - textViewSource.text = manga.source.title - textViewDescription.text = - manga.description?.parseAsHtml()?.takeUnless(Spanned::isBlank) - ?: getString(R.string.no_description) + textViewDescription.text = manga.description?.parseAsHtml()?.takeUnless(Spanned::isBlank) + ?: getString(R.string.no_description) when (manga.state) { MangaState.FINISHED -> { textViewState.apply { textAndVisible = resources.getString(R.string.state_finished) - drawableStart = ResourcesCompat.getDrawable(resources, - R.drawable.ic_state_finished, - context.theme) + drawableStart = ContextCompat.getDrawable(context, R.drawable.ic_state_finished) } } MangaState.ONGOING -> { textViewState.apply { textAndVisible = resources.getString(R.string.state_ongoing) - drawableStart = ResourcesCompat.getDrawable(resources, - R.drawable.ic_state_ongoing, - context.theme) + drawableStart = ContextCompat.getDrawable(context, R.drawable.ic_state_ongoing) } } else -> textViewState.isVisible = false } // Info containers - if (manga.chapters?.isNotEmpty() == true) { - chaptersContainer.isVisible = true - textViewChapters.text = manga.chapters.let { - resources.getQuantityString( - R.plurals.chapters, - it.size, - manga.chapters.size - ) - } + if (manga.chapters.isNullOrEmpty()) { + infoLayout.textViewChapters.isVisible = false } else { - chaptersContainer.isVisible = false + infoLayout.textViewChapters.isVisible = true + infoLayout.textViewChapters.text = resources.getQuantityString( + R.plurals.chapters, + manga.chapters.size, + manga.chapters.size, + ) } if (manga.rating == Manga.NO_RATING) { - ratingContainer.isVisible = false + infoLayout.ratingContainer.isVisible = false } else { - textViewRating.text = String.format("%.1f", manga.rating * 5) - ratingContainer.isVisible = true + infoLayout.textViewRating.text = String.format("%.1f", manga.rating * 5) + infoLayout.ratingContainer.isVisible = true } - val file = manga.url.toUri().toFileOrNull() - if (file != null) { - viewLifecycleScope.launch { - val size = file.computeSize() - textViewSize.text = FileSize.BYTES.format(requireContext(), size) + if (manga.source == MangaSource.LOCAL) { + infoLayout.textViewSource.isVisible = false + val file = manga.url.toUri().toFileOrNull() + if (file != null) { + viewLifecycleScope.launch { + val size = file.computeSize() + infoLayout.textViewSize.text = FileSize.BYTES.format(requireContext(), size) + infoLayout.textViewSize.isVisible = true + } + } else { + infoLayout.textViewSize.isVisible = false } - sizeContainer.isVisible = true } else { - sizeContainer.isVisible = false + infoLayout.textViewSource.text = manga.source.title + infoLayout.textViewSource.isVisible = true + infoLayout.textViewSize.isVisible = false } + infoLayout.textViewNsfw.isVisible = manga.isNsfw + // Buttons buttonRead.isEnabled = !manga.chapters.isNullOrEmpty() @@ -143,13 +143,12 @@ class DetailsFragment : BaseFragment(), View.OnClickList } private fun onFavouriteChanged(isFavourite: Boolean) { - with(binding.buttonFavorite) { - if (isFavourite) { - this.setIconResource(R.drawable.ic_heart) - } else { - this.setIconResource(R.drawable.ic_heart_outline) - } + val iconRes = if (isFavourite) { + R.drawable.ic_heart + } else { + R.drawable.ic_heart_outline } + binding.buttonFavorite.setIconResource(iconRes) } private fun onLoadingStateChanged(isLoading: Boolean) { @@ -189,7 +188,7 @@ class DetailsFragment : BaseFragment(), View.OnClickList ) ) } - R.id.cover_card -> { + R.id.imageView_cover -> { val options = ActivityOptions.makeSceneTransitionAnimation( requireActivity(), binding.imageViewCover, diff --git a/app/src/main/res/layout-w600dp-land/fragment_details.xml b/app/src/main/res/layout-w600dp-land/fragment_details.xml index 4be412a26..55df1bb95 100644 --- a/app/src/main/res/layout-w600dp-land/fragment_details.xml +++ b/app/src/main/res/layout-w600dp-land/fragment_details.xml @@ -13,262 +13,171 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - + + + + + + + + + + + + + + + + + + + app:layout_constraintTop_toBottomOf="@+id/barrier_header" /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintWidth_percent="0.3" + app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Kotatsu.Cover" + tools:background="@tools:sample/backgrounds/scenic" + tools:ignore="ContentDescription,UnusedAttribute" /> - + - + - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + app:layout_constraintTop_toBottomOf="@+id/barrier_header" /> + tools:text="@tools:sample/lorem/random[250]" /> - - - - - - - - - - - - - - - - - - - - - - - + android:layout_marginStart="8dp" + android:layout_marginTop="8dp" + android:foreground="?selectableItemBackground" + android:scaleType="centerCrop" + android:transitionName="cover" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="0" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintWidth_percent="0.3" + app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Kotatsu.Cover" + tools:background="@tools:sample/backgrounds/scenic" + tools:ignore="ContentDescription,UnusedAttribute" /> - + - + - + - + - + - - - - - - - - - - - - - - - - - + app:layout_constraintTop_toBottomOf="@id/barrier_header" /> - + - - - + + app:layout_constraintTop_toBottomOf="@+id/button_read" /> + tools:text="@tools:sample/lorem/random[250]" /> + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a5dc3b73a..238e096f5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -266,4 +266,5 @@ Always Preload pages Logged in as %s + 18+ \ No newline at end of file