From eea427216db4f19650e34c35001bcef55cfd6465 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Wed, 5 Jan 2022 11:42:03 +0200 Subject: [PATCH] Fullscreen cover view activity --- app/src/main/AndroidManifest.xml | 1 + .../kotatsu/details/ui/DetailsFragment.kt | 14 +++ .../kotatsu/image/ui/ImageActivity.kt | 92 +++++++++++++++++++ .../org/koitharu/kotatsu/utils/ext/CoilExt.kt | 15 +-- .../progress/ImageRequestIndicatorListener.kt | 18 ++++ app/src/main/res/layout/activity_image.xml | 26 ++++++ app/src/main/res/layout/fragment_details.xml | 1 + 7 files changed, 161 insertions(+), 6 deletions(-) create mode 100644 app/src/main/java/org/koitharu/kotatsu/image/ui/ImageActivity.kt create mode 100644 app/src/main/java/org/koitharu/kotatsu/utils/progress/ImageRequestIndicatorListener.kt create mode 100644 app/src/main/res/layout/activity_image.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b9aa1d3f1..2cc8e53e2 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -99,6 +99,7 @@ + (), View.OnClickList binding.buttonFavorite.setOnClickListener(this) binding.buttonRead.setOnClickListener(this) binding.buttonRead.setOnLongClickListener(this) + binding.coverCard.setOnClickListener(this) viewModel.manga.observe(viewLifecycleOwner, ::onMangaUpdated) viewModel.isLoading.observe(viewLifecycleOwner, ::onLoadingStateChanged) viewModel.favouriteCategories.observe(viewLifecycleOwner, ::onFavouriteChanged) @@ -189,6 +192,17 @@ class DetailsFragment : BaseFragment(), View.OnClickList ) ) } + R.id.cover_card -> { + val options = ActivityOptions.makeSceneTransitionAnimation( + requireActivity(), + binding.imageViewCover, + binding.imageViewCover.transitionName, + ) + startActivity( + ImageActivity.newIntent(v.context, manga.largeCoverUrl ?: manga.coverUrl), + options.toBundle() + ) + } } } diff --git a/app/src/main/java/org/koitharu/kotatsu/image/ui/ImageActivity.kt b/app/src/main/java/org/koitharu/kotatsu/image/ui/ImageActivity.kt new file mode 100644 index 000000000..ca14d2f4d --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/image/ui/ImageActivity.kt @@ -0,0 +1,92 @@ +package org.koitharu.kotatsu.image.ui + +import android.content.Context +import android.content.Intent +import android.graphics.drawable.Drawable +import android.net.Uri +import android.os.Bundle +import androidx.appcompat.app.AppCompatDelegate +import androidx.core.graphics.Insets +import androidx.core.graphics.drawable.toBitmap +import androidx.core.view.updatePadding +import coil.ImageLoader +import coil.request.CachePolicy +import coil.request.ImageRequest +import coil.target.PoolableViewTarget +import com.davemorrissey.labs.subscaleview.ImageSource +import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView +import org.koin.android.ext.android.inject +import org.koitharu.kotatsu.base.ui.BaseActivity +import org.koitharu.kotatsu.databinding.ActivityImageBinding +import org.koitharu.kotatsu.utils.ext.enqueueWith +import org.koitharu.kotatsu.utils.ext.indicator + +class ImageActivity : BaseActivity() { + + private val coil: ImageLoader by inject() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(ActivityImageBinding.inflate(layoutInflater)) + supportActionBar?.run { + setDisplayHomeAsUpEnabled(true) + setDisplayShowTitleEnabled(false) + } + loadImage(intent.data) + } + + override fun onWindowInsetsChanged(insets: Insets) { + binding.toolbar.updatePadding( + left = insets.left, + right = insets.right, + top = insets.top, + ) + } + + private fun loadImage(url: Uri?) { + ImageRequest.Builder(this) + .data(url) + .memoryCachePolicy(CachePolicy.DISABLED) + .lifecycle(this) + .target(SsivTarget(binding.ssiv)) + .indicator(binding.progressBar) + .enqueueWith(coil) + } + + private class SsivTarget( + override val view: SubsamplingScaleImageView, + ) : PoolableViewTarget { + + override fun onStart(placeholder: Drawable?) = setDrawable(placeholder) + + override fun onError(error: Drawable?) = setDrawable(error) + + override fun onSuccess(result: Drawable) = setDrawable(result) + + override fun onClear() = setDrawable(null) + + override fun equals(other: Any?): Boolean { + return (this === other) || (other is SsivTarget && view == other.view) + } + + override fun hashCode() = view.hashCode() + + override fun toString() = "SsivTarget(view=$view)" + + private fun setDrawable(drawable: Drawable?) { + if (drawable != null) { + view.setImage(ImageSource.bitmap(drawable.toBitmap())) + } else { + view.recycle() + } + } + } + + companion object { + + fun newIntent(context: Context, url: String): Intent { + return Intent(context, ImageActivity::class.java) + .setData(Uri.parse(url)) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/ext/CoilExt.kt b/app/src/main/java/org/koitharu/kotatsu/utils/ext/CoilExt.kt index e31e3e20b..53cf3bdb2 100644 --- a/app/src/main/java/org/koitharu/kotatsu/utils/ext/CoilExt.kt +++ b/app/src/main/java/org/koitharu/kotatsu/utils/ext/CoilExt.kt @@ -7,16 +7,16 @@ import coil.request.ErrorResult import coil.request.ImageRequest import coil.request.ImageResult import coil.request.SuccessResult +import com.google.android.material.progressindicator.BaseProgressIndicator import org.koitharu.kotatsu.core.network.CommonHeaders +import org.koitharu.kotatsu.utils.progress.ImageRequestIndicatorListener -@Suppress("NOTHING_TO_INLINE") -inline fun ImageView.newImageRequest(url: String) = ImageRequest.Builder(context) +fun ImageView.newImageRequest(url: String) = ImageRequest.Builder(context) .data(url) .crossfade(true) .target(this) -@Suppress("NOTHING_TO_INLINE") -inline fun ImageRequest.Builder.enqueueWith(loader: ImageLoader) = loader.enqueue(build()) +fun ImageRequest.Builder.enqueueWith(loader: ImageLoader) = loader.enqueue(build()) fun ImageResult.requireBitmap() = when (this) { is SuccessResult -> drawable.toBitmap() @@ -32,7 +32,10 @@ fun ImageResult.toBitmapOrNull() = when (this) { is ErrorResult -> null } -@Suppress("NOTHING_TO_INLINE") -inline fun ImageRequest.Builder.referer(referer: String): ImageRequest.Builder { +fun ImageRequest.Builder.referer(referer: String): ImageRequest.Builder { return setHeader(CommonHeaders.REFERER, referer) +} + +fun ImageRequest.Builder.indicator(indicator: BaseProgressIndicator<*>): ImageRequest.Builder { + return listener(ImageRequestIndicatorListener(indicator)) } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/progress/ImageRequestIndicatorListener.kt b/app/src/main/java/org/koitharu/kotatsu/utils/progress/ImageRequestIndicatorListener.kt new file mode 100644 index 000000000..eb38e1d32 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/utils/progress/ImageRequestIndicatorListener.kt @@ -0,0 +1,18 @@ +package org.koitharu.kotatsu.utils.progress + +import coil.request.ImageRequest +import coil.request.ImageResult +import com.google.android.material.progressindicator.BaseProgressIndicator + +class ImageRequestIndicatorListener( + private val indicator: BaseProgressIndicator<*>, +) : ImageRequest.Listener { + + override fun onCancel(request: ImageRequest) = indicator.hide() + + override fun onError(request: ImageRequest, throwable: Throwable) = indicator.hide() + + override fun onStart(request: ImageRequest) = indicator.show() + + override fun onSuccess(request: ImageRequest, metadata: ImageResult.Metadata) = indicator.hide() +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_image.xml b/app/src/main/res/layout/activity_image.xml new file mode 100644 index 000000000..251ff1c27 --- /dev/null +++ b/app/src/main/res/layout/activity_image.xml @@ -0,0 +1,26 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_details.xml b/app/src/main/res/layout/fragment_details.xml index 341e70b33..d25e033a2 100644 --- a/app/src/main/res/layout/fragment_details.xml +++ b/app/src/main/res/layout/fragment_details.xml @@ -40,6 +40,7 @@ android:layout_height="wrap_content" android:orientation="horizontal" android:scaleType="centerCrop" + android:transitionName="cover" tools:background="@tools:sample/backgrounds/scenic" tools:ignore="ContentDescription" />