mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-31 14:27:57 +01:00 
			
		
		
		
	Replace MotionLayout with full screen dialog (#5806)
* Remove MotionLayout and add full screen dialog for enlarged cover * Address some of the review comments
This commit is contained in:
		| @@ -30,6 +30,7 @@ import androidx.recyclerview.widget.RecyclerView | ||||
| import androidx.swiperefreshlayout.widget.SwipeRefreshLayout | ||||
| import coil.imageLoader | ||||
| import coil.request.ImageRequest | ||||
| import com.bluelinelabs.conductor.Controller | ||||
| import com.bluelinelabs.conductor.ControllerChangeHandler | ||||
| import com.bluelinelabs.conductor.ControllerChangeType | ||||
| import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton | ||||
| @@ -73,6 +74,7 @@ import eu.kanade.tachiyomi.ui.manga.chapter.DeleteChaptersDialog | ||||
| import eu.kanade.tachiyomi.ui.manga.chapter.DownloadCustomChaptersDialog | ||||
| import eu.kanade.tachiyomi.ui.manga.chapter.MangaChaptersHeaderAdapter | ||||
| import eu.kanade.tachiyomi.ui.manga.chapter.base.BaseChaptersAdapter | ||||
| import eu.kanade.tachiyomi.ui.manga.info.MangaFullCoverDialog | ||||
| import eu.kanade.tachiyomi.ui.manga.info.MangaInfoHeaderAdapter | ||||
| import eu.kanade.tachiyomi.ui.manga.track.TrackItem | ||||
| import eu.kanade.tachiyomi.ui.manga.track.TrackSearchDialog | ||||
| @@ -179,6 +181,8 @@ class MangaController : | ||||
|  | ||||
|     private var trackSheet: TrackSheet? = null | ||||
|  | ||||
|     private var dialog: MangaFullCoverDialog? = null | ||||
|  | ||||
|     init { | ||||
|         setHasOptionsMenu(true) | ||||
|     } | ||||
| @@ -435,7 +439,6 @@ class MangaController : | ||||
|  | ||||
|         // Hide options for non-library manga | ||||
|         menu.findItem(R.id.action_edit_categories).isVisible = presenter.manga.favorite && presenter.getCategories().isNotEmpty() | ||||
|         menu.findItem(R.id.action_edit_cover).isVisible = presenter.manga.favorite | ||||
|         menu.findItem(R.id.action_migrate).isVisible = presenter.manga.favorite | ||||
|     } | ||||
|  | ||||
| @@ -446,10 +449,6 @@ class MangaController : | ||||
|             R.id.download_custom, R.id.download_unread, R.id.download_all | ||||
|             -> downloadChapters(item.itemId) | ||||
|  | ||||
|             R.id.action_share_cover -> shareCover() | ||||
|             R.id.action_save_cover -> saveCover() | ||||
|             R.id.action_edit_cover -> changeCover() | ||||
|  | ||||
|             R.id.action_edit_categories -> onCategoriesClick() | ||||
|             R.id.action_migrate -> migrateManga() | ||||
|         } | ||||
| @@ -728,6 +727,21 @@ class MangaController : | ||||
|         context.imageLoader.enqueue(req) | ||||
|     } | ||||
|  | ||||
|     fun showFullCoverDialog() { | ||||
|         if (dialog != null) return | ||||
|         val manga = manga ?: return | ||||
|         dialog = MangaFullCoverDialog(this, manga) | ||||
|         dialog?.addLifecycleListener( | ||||
|             object : LifecycleListener() { | ||||
|                 override fun postDestroy(controller: Controller) { | ||||
|                     super.postDestroy(controller) | ||||
|                     dialog = null | ||||
|                 } | ||||
|             } | ||||
|         ) | ||||
|         dialog?.showDialog(router) | ||||
|     } | ||||
|  | ||||
|     fun shareCover() { | ||||
|         try { | ||||
|             val activity = activity!! | ||||
| @@ -800,6 +814,7 @@ class MangaController : | ||||
|  | ||||
|     fun onSetCoverSuccess() { | ||||
|         mangaInfoAdapter?.notifyDataSetChanged() | ||||
|         dialog?.setImage(manga) | ||||
|         activity?.toast(R.string.cover_updated) | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -0,0 +1,119 @@ | ||||
| package eu.kanade.tachiyomi.ui.manga.info | ||||
|  | ||||
| import android.app.Dialog | ||||
| import android.graphics.drawable.BitmapDrawable | ||||
| import android.graphics.drawable.ColorDrawable | ||||
| import android.os.Bundle | ||||
| import android.util.TypedValue | ||||
| import android.view.View | ||||
| import androidx.core.graphics.ColorUtils | ||||
| import androidx.core.os.bundleOf | ||||
| import androidx.core.view.WindowCompat | ||||
| import coil.imageLoader | ||||
| import coil.request.Disposable | ||||
| import coil.request.ImageRequest | ||||
| import com.davemorrissey.labs.subscaleview.ImageSource | ||||
| import dev.chrisbanes.insetter.applyInsetter | ||||
| import eu.kanade.tachiyomi.R | ||||
| import eu.kanade.tachiyomi.data.database.DatabaseHelper | ||||
| import eu.kanade.tachiyomi.data.database.models.Manga | ||||
| import eu.kanade.tachiyomi.databinding.MangaFullCoverDialogBinding | ||||
| import eu.kanade.tachiyomi.ui.base.controller.DialogController | ||||
| import eu.kanade.tachiyomi.ui.manga.MangaController | ||||
| import eu.kanade.tachiyomi.util.view.setNavigationBarTransparentCompat | ||||
| import eu.kanade.tachiyomi.widget.TachiyomiFullscreenDialog | ||||
| import uy.kohesive.injekt.Injekt | ||||
| import uy.kohesive.injekt.api.get | ||||
|  | ||||
| class MangaFullCoverDialog : DialogController { | ||||
|  | ||||
|     private var manga: Manga? = null | ||||
|  | ||||
|     private var binding: MangaFullCoverDialogBinding? = null | ||||
|  | ||||
|     private var disposable: Disposable? = null | ||||
|  | ||||
|     private val mangaController | ||||
|         get() = targetController as MangaController | ||||
|  | ||||
|     constructor(targetController: MangaController, manga: Manga) : super(bundleOf("mangaId" to manga.id)) { | ||||
|         this.targetController = targetController | ||||
|         this.manga = manga | ||||
|     } | ||||
|  | ||||
|     @Suppress("unused") | ||||
|     constructor(bundle: Bundle) : super(bundle) { | ||||
|         val db = Injekt.get<DatabaseHelper>() | ||||
|         manga = db.getManga(bundle.getLong("mangaId")).executeAsBlocking() | ||||
|     } | ||||
|  | ||||
|     override fun onCreateDialog(savedViewState: Bundle?): Dialog { | ||||
|         binding = MangaFullCoverDialogBinding.inflate(activity!!.layoutInflater) | ||||
|  | ||||
|         binding?.toolbar?.apply { | ||||
|             setNavigationOnClickListener { dialog?.dismiss() } | ||||
|             setOnMenuItemClickListener { | ||||
|                 when (it.itemId) { | ||||
|                     R.id.action_share_cover -> mangaController.shareCover() | ||||
|                     R.id.action_save_cover -> mangaController.saveCover() | ||||
|                     R.id.action_edit_cover -> mangaController.changeCover() | ||||
|                 } | ||||
|                 true | ||||
|             } | ||||
|             menu?.findItem(R.id.action_edit_cover)?.isVisible = manga?.favorite ?: false | ||||
|         } | ||||
|  | ||||
|         binding?.fullCover?.setOnClickListener { | ||||
|             dialog?.dismiss() | ||||
|         } | ||||
|         setImage(manga) | ||||
|  | ||||
|         binding?.appbar?.applyInsetter { | ||||
|             type(navigationBars = true, statusBars = true) { | ||||
|                 padding(left = true, top = true, right = true) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         binding?.fullCover?.applyInsetter { | ||||
|             type(navigationBars = true) { | ||||
|                 // Padding will make to image top align | ||||
|                 // This is likely an issue with SubsamplingScaleImageView | ||||
|                 margin(bottom = true) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return TachiyomiFullscreenDialog(activity!!, binding!!.root).apply { | ||||
|             val typedValue = TypedValue() | ||||
|             val theme = context.theme | ||||
|             theme.resolveAttribute(android.R.attr.colorBackground, typedValue, true) | ||||
|             window?.setBackgroundDrawable(ColorDrawable(ColorUtils.setAlphaComponent(typedValue.data, 230))) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     override fun onAttach(view: View) { | ||||
|         super.onAttach(view) | ||||
|         dialog?.window?.let { window -> | ||||
|             window.setNavigationBarTransparentCompat(window.context) | ||||
|             WindowCompat.setDecorFitsSystemWindows(window, false) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     override fun onDetach(view: View) { | ||||
|         super.onDetach(view) | ||||
|         disposable?.dispose() | ||||
|         disposable = null | ||||
|     } | ||||
|  | ||||
|     fun setImage(manga: Manga?) { | ||||
|         val manga = manga ?: return | ||||
|         val request = ImageRequest.Builder(applicationContext!!) | ||||
|             .data(manga) | ||||
|             .target { | ||||
|                 val bitmap = (it as BitmapDrawable).bitmap | ||||
|                 binding?.fullCover?.setImage(ImageSource.cachedBitmap(bitmap)) | ||||
|             } | ||||
|             .build() | ||||
|  | ||||
|         disposable = applicationContext?.imageLoader?.enqueue(request) | ||||
|     } | ||||
| } | ||||
| @@ -3,11 +3,9 @@ package eu.kanade.tachiyomi.ui.manga.info | ||||
| import android.view.LayoutInflater | ||||
| import android.view.View | ||||
| import android.view.ViewGroup | ||||
| import androidx.constraintlayout.widget.ConstraintLayout | ||||
| import androidx.core.view.isVisible | ||||
| import androidx.core.view.updateLayoutParams | ||||
| import androidx.recyclerview.widget.RecyclerView | ||||
| import coil.target.ImageViewTarget | ||||
| import com.google.android.material.dialog.MaterialAlertDialogBuilder | ||||
| import eu.kanade.tachiyomi.R | ||||
| import eu.kanade.tachiyomi.data.database.models.Manga | ||||
| @@ -87,15 +85,10 @@ class MangaInfoHeaderAdapter( | ||||
|         binding.mangaCover.updateLayoutParams<ViewGroup.MarginLayoutParams> { | ||||
|             topMargin += appBarHeight | ||||
|         } | ||||
|         binding.root.getConstraintSet(R.id.end) | ||||
|             ?.setMargin(R.id.manga_cover, ConstraintLayout.LayoutParams.TOP, appBarHeight) | ||||
|     } | ||||
|  | ||||
|     inner class HeaderViewHolder(private val view: View) : RecyclerView.ViewHolder(view) { | ||||
|         fun bind() { | ||||
|             val headerTransition = binding.root.getTransition(R.id.manga_info_header_transition) | ||||
|             headerTransition.applySystemAnimatorScale(view.context) | ||||
|  | ||||
|             val summaryTransition = binding.mangaSummarySection.getTransition(R.id.manga_summary_section_transition) | ||||
|             summaryTransition.applySystemAnimatorScale(view.context) | ||||
|  | ||||
| @@ -199,6 +192,12 @@ class MangaInfoHeaderAdapter( | ||||
|                 } | ||||
|                 .launchIn(controller.viewScope) | ||||
|  | ||||
|             binding.mangaCover.clicks() | ||||
|                 .onEach { | ||||
|                     controller.showFullCoverDialog() | ||||
|                 } | ||||
|                 .launchIn(controller.viewScope) | ||||
|  | ||||
|             binding.mangaCover.longClicks() | ||||
|                 .onEach { | ||||
|                     showCoverOptionsDialog() | ||||
| @@ -286,17 +285,7 @@ class MangaInfoHeaderAdapter( | ||||
|  | ||||
|             // Set cover if changed. | ||||
|             binding.backdrop.loadAnyAutoPause(manga) | ||||
|             binding.mangaCover.loadAnyAutoPause(manga) { | ||||
|                 listener( | ||||
|                     onSuccess = { request, _ -> | ||||
|                         (request.target as? ImageViewTarget)?.drawable?.let { drawable -> | ||||
|                             val ratio = drawable.minimumWidth / drawable.minimumHeight.toFloat() | ||||
|                             binding.root.getConstraintSet(R.id.end) | ||||
|                                 ?.setDimensionRatio(R.id.manga_cover, ratio.toString()) | ||||
|                         } | ||||
|                     } | ||||
|                 ) | ||||
|             } | ||||
|             binding.mangaCover.loadAnyAutoPause(manga) | ||||
|  | ||||
|             // Manga info section | ||||
|             val hasInfoContent = !manga.description.isNullOrBlank() || !manga.genre.isNullOrBlank() | ||||
|   | ||||
| @@ -7,7 +7,6 @@ import android.view.LayoutInflater | ||||
| import android.view.View | ||||
| import android.view.inputmethod.EditorInfo | ||||
| import android.view.inputmethod.InputMethodManager | ||||
| import androidx.appcompat.app.AppCompatDialog | ||||
| import androidx.core.content.getSystemService | ||||
| import androidx.core.os.bundleOf | ||||
| import androidx.core.view.WindowCompat | ||||
| @@ -21,6 +20,7 @@ import eu.kanade.tachiyomi.databinding.TrackSearchDialogBinding | ||||
| import eu.kanade.tachiyomi.ui.base.controller.DialogController | ||||
| import eu.kanade.tachiyomi.ui.manga.MangaController | ||||
| import eu.kanade.tachiyomi.util.view.setNavigationBarTransparentCompat | ||||
| import eu.kanade.tachiyomi.widget.TachiyomiFullscreenDialog | ||||
| import kotlinx.coroutines.flow.filter | ||||
| import kotlinx.coroutines.flow.launchIn | ||||
| import kotlinx.coroutines.flow.onEach | ||||
| @@ -142,9 +142,7 @@ class TrackSearchDialog : DialogController { | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return AppCompatDialog(activity!!, R.style.ThemeOverlay_Tachiyomi_Dialog_Fullscreen).apply { | ||||
|             setContentView(binding!!.root) | ||||
|         } | ||||
|         return TachiyomiFullscreenDialog(activity!!, binding!!.root) | ||||
|     } | ||||
|  | ||||
|     override fun onAttach(view: View) { | ||||
|   | ||||
| @@ -0,0 +1,13 @@ | ||||
| package eu.kanade.tachiyomi.widget | ||||
|  | ||||
| import android.content.Context | ||||
| import android.view.View | ||||
| import androidx.appcompat.app.AppCompatDialog | ||||
| import eu.kanade.tachiyomi.R | ||||
|  | ||||
| class TachiyomiFullscreenDialog(context: Context, view: View) : AppCompatDialog(context, R.style.ThemeOverlay_Tachiyomi_Dialog_Fullscreen) { | ||||
|  | ||||
|     init { | ||||
|         setContentView(view) | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user