mirror of
https://github.com/null2264/yokai.git
synced 2025-06-21 10:44:42 +00:00
Merge branch 'hinge-support'
This commit is contained in:
commit
067f6997a5
12 changed files with 242 additions and 23 deletions
|
@ -136,6 +136,7 @@ dependencies {
|
|||
implementation("androidx.core:core-ktx:1.8.0")
|
||||
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1")
|
||||
implementation("com.google.android.flexbox:flexbox:3.0.0")
|
||||
implementation("androidx.window:window:1.0.0")
|
||||
|
||||
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
||||
|
||||
|
@ -144,7 +145,7 @@ dependencies {
|
|||
implementation("com.google.firebase:firebase-core:21.1.0")
|
||||
implementation("com.google.firebase:firebase-analytics-ktx:21.1.0")
|
||||
|
||||
val lifecycleVersion = "2.4.0-rc01"
|
||||
val lifecycleVersion = "2.5.1"
|
||||
kapt("androidx.lifecycle:lifecycle-compiler:$lifecycleVersion")
|
||||
implementation("androidx.lifecycle:lifecycle-process:$lifecycleVersion")
|
||||
implementation("androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion")
|
||||
|
|
|
@ -78,8 +78,6 @@ open class App : Application(), DefaultLifecycleObserver {
|
|||
setupAcra()
|
||||
setupNotificationChannels()
|
||||
|
||||
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
|
||||
|
||||
MangaCoverMetadata.load()
|
||||
preferences.nightMode()
|
||||
.asImmediateFlow { AppCompatDelegate.setDefaultNightMode(it) }
|
||||
|
|
|
@ -43,7 +43,12 @@ import androidx.core.view.forEach
|
|||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.window.layout.DisplayFeature
|
||||
import androidx.window.layout.FoldingFeature
|
||||
import androidx.window.layout.WindowInfoTracker
|
||||
import com.bluelinelabs.conductor.Conductor
|
||||
import com.bluelinelabs.conductor.Controller
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler
|
||||
|
@ -152,6 +157,8 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
|
|||
private var overflowDialog: Dialog? = null
|
||||
var currentToolbar: Toolbar? = null
|
||||
var ogWidth: Int = Int.MAX_VALUE
|
||||
var hingeGapSize = 0
|
||||
private set
|
||||
|
||||
private val actionButtonSize: Pair<Int, Int> by lazy {
|
||||
val attrs = intArrayOf(android.R.attr.minWidth, android.R.attr.minHeight)
|
||||
|
@ -522,6 +529,25 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
|
|||
}
|
||||
}
|
||||
setFloatingToolbar(canShowFloatingToolbar(router.backstack.lastOrNull()?.controller), changeBG = false)
|
||||
|
||||
lifecycleScope.launchUI {
|
||||
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
WindowInfoTracker.getOrCreate(this@MainActivity).windowLayoutInfo(this@MainActivity)
|
||||
.collect { newLayoutInfo ->
|
||||
hingeGapSize = 0
|
||||
for (displayFeature: DisplayFeature in newLayoutInfo.displayFeatures) {
|
||||
if (displayFeature is FoldingFeature && displayFeature.occlusionType == FoldingFeature.OcclusionType.FULL &&
|
||||
displayFeature.isSeparating && displayFeature.orientation == FoldingFeature.Orientation.VERTICAL
|
||||
) {
|
||||
hingeGapSize = displayFeature.bounds.width()
|
||||
}
|
||||
}
|
||||
if (hingeGapSize > 0) {
|
||||
(router.backstack.lastOrNull()?.controller as? HingeSupportedController)?.updateForHinge()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun reEnableBackPressedCallBack() {
|
||||
|
@ -1388,6 +1414,10 @@ interface RootSearchInterface {
|
|||
|
||||
interface TabbedInterface
|
||||
|
||||
interface HingeSupportedController {
|
||||
fun updateForHinge()
|
||||
}
|
||||
|
||||
interface FloatingSearchInterface {
|
||||
fun searchTitle(title: String?): String? {
|
||||
if (this is Controller) {
|
||||
|
|
|
@ -190,8 +190,7 @@ class FullCoverDialog(val controller: MangaDetailsController, drawable: Drawable
|
|||
}
|
||||
|
||||
override fun cancel() {
|
||||
super.cancel()
|
||||
thumbView.alpha = 1f
|
||||
animateBack()
|
||||
}
|
||||
|
||||
override fun dismiss() {
|
||||
|
|
|
@ -19,6 +19,7 @@ import android.view.MotionEvent
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.annotation.FloatRange
|
||||
|
@ -26,6 +27,7 @@ import androidx.appcompat.app.AppCompatActivity
|
|||
import androidx.appcompat.view.ActionMode
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.WindowInsetsCompat.Type.systemBars
|
||||
|
@ -68,6 +70,7 @@ import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
|||
import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder
|
||||
import eu.kanade.tachiyomi.ui.library.LibraryController
|
||||
import eu.kanade.tachiyomi.ui.main.FloatingSearchInterface
|
||||
import eu.kanade.tachiyomi.ui.main.HingeSupportedController
|
||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
import eu.kanade.tachiyomi.ui.main.SearchActivity
|
||||
import eu.kanade.tachiyomi.ui.manga.chapter.ChapterHolder
|
||||
|
@ -104,6 +107,7 @@ import eu.kanade.tachiyomi.util.system.rootWindowInsetsCompat
|
|||
import eu.kanade.tachiyomi.util.system.setCustomTitleAndMessage
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import eu.kanade.tachiyomi.util.view.activityBinding
|
||||
import eu.kanade.tachiyomi.util.view.findChild
|
||||
import eu.kanade.tachiyomi.util.view.getText
|
||||
import eu.kanade.tachiyomi.util.view.isControllerVisible
|
||||
import eu.kanade.tachiyomi.util.view.previousController
|
||||
|
@ -134,6 +138,7 @@ class MangaDetailsController :
|
|||
ActionMode.Callback,
|
||||
MangaDetailsAdapter.MangaDetailsInterface,
|
||||
SmallToolbarInterface,
|
||||
HingeSupportedController,
|
||||
FlexibleAdapter.OnItemMoveListener {
|
||||
|
||||
constructor(
|
||||
|
@ -370,6 +375,30 @@ class MangaDetailsController :
|
|||
tabletAdapter = MangaDetailsAdapter(this)
|
||||
binding.tabletRecycler.adapter = tabletAdapter
|
||||
binding.tabletRecycler.layoutManager = LinearLayoutManager(view.context)
|
||||
updateForHinge()
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateForHinge() {
|
||||
if (isTablet) {
|
||||
val hingeGapSize = (activity as? MainActivity)?.hingeGapSize?.takeIf { it > 0 }
|
||||
if (hingeGapSize != null) {
|
||||
binding.tabletDivider.updateLayoutParams<ViewGroup.LayoutParams> {
|
||||
width = hingeGapSize
|
||||
}
|
||||
binding.tabletRecycler.updateLayoutParams<ConstraintLayout.LayoutParams> {
|
||||
matchConstraintPercentWidth = 1f
|
||||
width = 0
|
||||
matchConstraintDefaultWidth =
|
||||
ConstraintLayout.LayoutParams.MATCH_CONSTRAINT_SPREAD
|
||||
}
|
||||
val swipeCircle = binding.swipeRefresh.findChild<ImageView>()
|
||||
swipeCircle?.translationX =
|
||||
(activity!!.window.decorView.width / 2 + hingeGapSize) /
|
||||
2f
|
||||
} else {
|
||||
binding.tabletRecycler.updateLayoutParams<ConstraintLayout.LayoutParams> { matchConstraintPercentWidth = 0.4f }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -440,6 +469,12 @@ class MangaDetailsController :
|
|||
binding.touchView.setOnTouchListener { _, event ->
|
||||
if (event.action == MotionEvent.ACTION_DOWN) {
|
||||
finishFloatingActionMode()
|
||||
val hingeGapSize = (activity as? MainActivity)?.hingeGapSize?.takeIf { it > 0 }
|
||||
if (hingeGapSize != null) {
|
||||
val swipeCircle = binding.swipeRefresh.findChild<ImageView>()
|
||||
swipeCircle?.translationX = (binding.root.width / 2 + hingeGapSize) / 2 *
|
||||
(if (event.x > binding.root.width / 2) 1 else -1).toFloat()
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import android.graphics.drawable.LayerDrawable
|
|||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.Gravity
|
||||
import android.view.HapticFeedbackConstants
|
||||
import android.view.KeyEvent
|
||||
import android.view.Menu
|
||||
|
@ -29,6 +30,7 @@ import android.view.WindowManager
|
|||
import android.view.animation.AnimationUtils
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.activity.addCallback
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.core.app.ActivityOptionsCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.graphics.ColorUtils
|
||||
|
@ -46,7 +48,11 @@ import androidx.core.view.updateLayoutParams
|
|||
import androidx.core.view.updatePaddingRelative
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
|
||||
import androidx.window.layout.DisplayFeature
|
||||
import androidx.window.layout.FoldingFeature
|
||||
import androidx.window.layout.WindowInfoTracker
|
||||
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
|
@ -117,6 +123,7 @@ import eu.kanade.tachiyomi.widget.doOnStart
|
|||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.drop
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.merge
|
||||
|
@ -211,6 +218,11 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
|
|||
}
|
||||
|
||||
var isScrollingThroughPagesOrChapters = false
|
||||
private var hingeGapSize = 0
|
||||
set(value) {
|
||||
field = value
|
||||
(viewer as? PagerViewer)?.config?.hingeGapSize = value
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
|
@ -315,6 +327,35 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
|
|||
SecureActivityDelegate.setSecure(this)
|
||||
}
|
||||
reEnableBackPressedCallBack()
|
||||
lifecycleScope.launchUI {
|
||||
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
WindowInfoTracker.getOrCreate(this@ReaderActivity).windowLayoutInfo(this@ReaderActivity)
|
||||
.collect { newLayoutInfo ->
|
||||
hingeGapSize = 0
|
||||
for (displayFeature: DisplayFeature in newLayoutInfo.displayFeatures) {
|
||||
if (displayFeature is FoldingFeature && displayFeature.occlusionType == FoldingFeature.OcclusionType.FULL &&
|
||||
displayFeature.isSeparating && displayFeature.orientation == FoldingFeature.Orientation.VERTICAL
|
||||
) {
|
||||
hingeGapSize = displayFeature.bounds.width()
|
||||
}
|
||||
}
|
||||
if (hingeGapSize > 0) {
|
||||
binding.navLayout.updateLayoutParams<CoordinatorLayout.LayoutParams> {
|
||||
gravity = Gravity.TOP or Gravity.CENTER
|
||||
anchorGravity = Gravity.TOP or Gravity.CENTER
|
||||
width = (binding.root.width - hingeGapSize) / 2 - 24.dpToPx
|
||||
}
|
||||
binding.chaptersSheet.root.updateLayoutParams<CoordinatorLayout.LayoutParams> {
|
||||
gravity = Gravity.END
|
||||
width = (binding.root.width - hingeGapSize) / 2
|
||||
}
|
||||
binding.pleaseWait.updateLayoutParams<CoordinatorLayout.LayoutParams> {
|
||||
marginStart = binding.root.width / 2 + hingeGapSize
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1097,6 +1138,7 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
|
|||
}
|
||||
|
||||
if (newViewer is PagerViewer) {
|
||||
newViewer.config.hingeGapSize = hingeGapSize
|
||||
if (preferences.pageLayout().get() == PageLayout.AUTOMATIC.value) {
|
||||
setDoublePageMode(newViewer)
|
||||
}
|
||||
|
@ -1287,6 +1329,11 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
|
|||
}
|
||||
|
||||
val totalPages = pages.size.toString()
|
||||
if (hingeGapSize > 0) {
|
||||
binding.pageNumber.updateLayoutParams<CoordinatorLayout.LayoutParams> {
|
||||
marginStart = (binding.root.width) / 2 + hingeGapSize
|
||||
}
|
||||
}
|
||||
binding.pageNumber.text = if (resources.isLTR) "$currentPage/$totalPages" else "$totalPages/$currentPage"
|
||||
if (viewer is R2LPagerViewer) {
|
||||
binding.readerNav.rightPageText.text = currentPage
|
||||
|
|
|
@ -312,6 +312,7 @@ open class ReaderPageImageView @JvmOverloads constructor(
|
|||
val zoomStartPosition: PagerConfig.ZoomType = PagerConfig.ZoomType.Center,
|
||||
val landscapeZoom: Boolean = false,
|
||||
val insetInfo: InsetInfo? = null,
|
||||
val hingeGapSize: Int = 0,
|
||||
)
|
||||
|
||||
data class InsetInfo(
|
||||
|
|
|
@ -64,6 +64,8 @@ class PagerConfig(
|
|||
}
|
||||
}
|
||||
|
||||
var hingeGapSize = 0
|
||||
|
||||
var invertDoublePages = false
|
||||
|
||||
var autoDoublePages = preferences.pageLayout().get() == PageLayout.AUTOMATIC.value
|
||||
|
|
|
@ -18,6 +18,7 @@ import android.widget.LinearLayout
|
|||
import android.widget.TextView
|
||||
import androidx.core.net.toUri
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
|
@ -120,6 +121,11 @@ class PagerPageHolder(
|
|||
|
||||
init {
|
||||
addView(progressBar)
|
||||
if (viewer.config.hingeGapSize > 0) {
|
||||
progressBar.updateLayoutParams<MarginLayoutParams> {
|
||||
marginStart = ((context.resources.displayMetrics.widthPixels) / 2 + viewer.config.hingeGapSize) / 2
|
||||
}
|
||||
}
|
||||
scope = CoroutineScope(Job() + Default)
|
||||
observeStatus()
|
||||
setBackgroundColor(
|
||||
|
@ -566,6 +572,7 @@ class PagerPageHolder(
|
|||
isSplitScreen = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && viewer.activity.isInMultiWindowMode,
|
||||
insets = viewer.activity.window.decorView.rootWindowInsets,
|
||||
),
|
||||
hingeGapSize = viewer.config.hingeGapSize,
|
||||
)
|
||||
|
||||
private suspend fun setBG(bytesArray: ByteArray): Drawable {
|
||||
|
@ -771,18 +778,18 @@ class PagerPageHolder(
|
|||
imageBytes.inputStream()
|
||||
}
|
||||
}
|
||||
return imageStream
|
||||
return supportHingeIfThere(imageStream)
|
||||
}
|
||||
if (page.fullPage == true) return imageStream
|
||||
if (page.fullPage == true) return supportHingeIfThere(imageStream)
|
||||
if (ImageUtil.isAnimatedAndSupported(imageStream)) {
|
||||
page.fullPage = true
|
||||
splitDoublePages()
|
||||
return imageStream
|
||||
} else if (ImageUtil.isAnimatedAndSupported(imageStream)) {
|
||||
} else if (ImageUtil.isAnimatedAndSupported(imageStream2)) {
|
||||
page.isolatedPage = true
|
||||
extraPage?.fullPage = true
|
||||
splitDoublePages()
|
||||
return imageStream
|
||||
return supportHingeIfThere(imageStream)
|
||||
}
|
||||
val imageBytes = imageStream.readBytes()
|
||||
val imageBitmap = try {
|
||||
|
@ -804,7 +811,7 @@ class PagerPageHolder(
|
|||
imageStream.close()
|
||||
page.fullPage = true
|
||||
splitDoublePages()
|
||||
return imageBytes.inputStream()
|
||||
return supportHingeIfThere(imageBytes.inputStream())
|
||||
}
|
||||
|
||||
val imageBytes2 = imageStream2.readBytes()
|
||||
|
@ -829,7 +836,7 @@ class PagerPageHolder(
|
|||
extraPage?.fullPage = true
|
||||
page.isolatedPage = true
|
||||
splitDoublePages()
|
||||
return imageBytes.inputStream()
|
||||
return supportHingeIfThere(imageBytes.inputStream())
|
||||
}
|
||||
val isLTR = (viewer !is R2LPagerViewer).xor(viewer.config.invertDoublePages)
|
||||
val bg = if (viewer.config.readerTheme >= 2 || viewer.config.readerTheme == 0) {
|
||||
|
@ -840,7 +847,7 @@ class PagerPageHolder(
|
|||
|
||||
imageStream.close()
|
||||
imageStream2.close()
|
||||
return ImageUtil.mergeBitmaps(imageBitmap, imageBitmap2, isLTR, bg) {
|
||||
return ImageUtil.mergeBitmaps(imageBitmap, imageBitmap2, isLTR, bg, viewer.config.hingeGapSize, context) {
|
||||
scope?.launchUI {
|
||||
if (it == 100) {
|
||||
progressBar.completeAndFadeOut()
|
||||
|
@ -851,6 +858,38 @@ class PagerPageHolder(
|
|||
}
|
||||
}
|
||||
|
||||
private fun supportHingeIfThere(imageStream: InputStream): InputStream {
|
||||
if (viewer.config.hingeGapSize > 0 && !ImageUtil.isAnimatedAndSupported(imageStream)) {
|
||||
val imageBytes = imageStream.readBytes()
|
||||
val imageBitmap = try {
|
||||
BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size)
|
||||
} catch (e: Exception) {
|
||||
imageStream.close()
|
||||
val wasNotFullPage = page.fullPage != true
|
||||
page.fullPage = true
|
||||
if (wasNotFullPage) {
|
||||
splitDoublePages()
|
||||
}
|
||||
return imageBytes.inputStream()
|
||||
}
|
||||
val isLTR = (viewer !is R2LPagerViewer).xor(viewer.config.invertDoublePages)
|
||||
val bg = if (viewer.config.readerTheme >= 2 || viewer.config.readerTheme == 0) {
|
||||
Color.WHITE
|
||||
} else {
|
||||
Color.BLACK
|
||||
}
|
||||
return ImageUtil.padSingleImage(
|
||||
imageBitmap = imageBitmap,
|
||||
isLTR = isLTR,
|
||||
atBeginning = if (viewer.config.doublePages) page.index == 0 else null,
|
||||
background = bg,
|
||||
hingeGap = viewer.config.hingeGapSize,
|
||||
context = context,
|
||||
)
|
||||
}
|
||||
return imageStream
|
||||
}
|
||||
|
||||
private fun splitDoublePages() {
|
||||
// extraPage ?: return
|
||||
scope?.launchUI {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package eu.kanade.tachiyomi.ui.reader.viewer.pager
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
|
@ -9,6 +10,7 @@ import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
|||
import android.widget.LinearLayout
|
||||
import android.widget.ProgressBar
|
||||
import androidx.appcompat.widget.AppCompatTextView
|
||||
import androidx.core.view.updatePaddingRelative
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.reader.model.ChapterTransition
|
||||
import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
|
||||
|
@ -59,6 +61,12 @@ class PagerTransitionHolder(
|
|||
|
||||
transitionView.bind(transition, viewer.downloadManager, viewer.activity.presenter.manga)
|
||||
transition.to?.let { observeStatus(it) }
|
||||
|
||||
if (viewer.config.hingeGapSize > 0) {
|
||||
val fullWidth = (context as? Activity)?.window?.decorView?.width
|
||||
?: context.resources.displayMetrics.widthPixels
|
||||
updatePaddingRelative(start = sidePadding + fullWidth / 2 + viewer.config.hingeGapSize)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package eu.kanade.tachiyomi.util.system
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.content.res.Resources
|
||||
|
@ -429,6 +430,8 @@ object ImageUtil {
|
|||
imageBitmap2: Bitmap,
|
||||
isLTR: Boolean,
|
||||
@ColorInt background: Int = Color.WHITE,
|
||||
hingeGap: Int = 0,
|
||||
context: Context? = null,
|
||||
progressCallback: ((Int) -> Unit)? = null,
|
||||
): ByteArrayInputStream {
|
||||
val height = imageBitmap.height
|
||||
|
@ -436,21 +439,27 @@ object ImageUtil {
|
|||
val height2 = imageBitmap2.height
|
||||
val width2 = imageBitmap2.width
|
||||
val maxHeight = max(height, height2)
|
||||
val result = Bitmap.createBitmap(width + width2, max(height, height2), Bitmap.Config.ARGB_8888)
|
||||
val maxWidth = max(width, width2)
|
||||
val adjustedHingeGap = context?.let {
|
||||
val fullHeight = (context as? Activity)?.window?.decorView?.height
|
||||
?: context.resources.displayMetrics.heightPixels
|
||||
(maxHeight.toFloat() / fullHeight * hingeGap).toInt()
|
||||
} ?: hingeGap
|
||||
val result = Bitmap.createBitmap((maxWidth * 2) + adjustedHingeGap, maxHeight, Bitmap.Config.ARGB_8888)
|
||||
val canvas = Canvas(result)
|
||||
canvas.drawColor(background)
|
||||
val upperPart = Rect(
|
||||
if (isLTR) 0 else width2,
|
||||
if (isLTR) max(maxWidth - imageBitmap.width, 0) else maxWidth + adjustedHingeGap,
|
||||
(maxHeight - imageBitmap.height) / 2,
|
||||
(if (isLTR) 0 else width2) + imageBitmap.width,
|
||||
(if (isLTR) max(maxWidth - imageBitmap.width, 0) else maxWidth + adjustedHingeGap) + imageBitmap.width,
|
||||
imageBitmap.height + (maxHeight - imageBitmap.height) / 2,
|
||||
)
|
||||
canvas.drawBitmap(imageBitmap, imageBitmap.rect, upperPart, null)
|
||||
progressCallback?.invoke(98)
|
||||
val bottomPart = Rect(
|
||||
if (!isLTR) 0 else width,
|
||||
if (!isLTR) max(maxWidth - imageBitmap2.width, 0) else maxWidth + adjustedHingeGap,
|
||||
(maxHeight - imageBitmap2.height) / 2,
|
||||
(if (!isLTR) 0 else width) + imageBitmap2.width,
|
||||
(if (!isLTR) max(maxWidth - imageBitmap2.width, 0) else maxWidth + adjustedHingeGap) + imageBitmap2.width,
|
||||
imageBitmap2.height + (maxHeight - imageBitmap2.height) / 2,
|
||||
)
|
||||
canvas.drawBitmap(imageBitmap2, imageBitmap2.rect, bottomPart, null)
|
||||
|
@ -462,6 +471,56 @@ object ImageUtil {
|
|||
return ByteArrayInputStream(output.toByteArray())
|
||||
}
|
||||
|
||||
fun padSingleImage(
|
||||
imageBitmap: Bitmap,
|
||||
isLTR: Boolean,
|
||||
atBeginning: Boolean?,
|
||||
@ColorInt background: Int,
|
||||
hingeGap: Int,
|
||||
context: Context,
|
||||
progressCallback: ((Int) -> Unit)? = null,
|
||||
): ByteArrayInputStream {
|
||||
val height = imageBitmap.height
|
||||
val width = imageBitmap.width
|
||||
val isFullPageSpread = height < width
|
||||
val fullHeight = (context as? Activity)?.window?.decorView?.height
|
||||
?: context.resources.displayMetrics.heightPixels
|
||||
val adjustedHingeGap = (height.toFloat() / fullHeight * hingeGap).toInt()
|
||||
val result = Bitmap.createBitmap(
|
||||
(if (isFullPageSpread) width else (width * 2)) + adjustedHingeGap,
|
||||
height,
|
||||
Bitmap.Config.ARGB_8888,
|
||||
)
|
||||
val canvas = Canvas(result)
|
||||
canvas.drawColor(background)
|
||||
if (isFullPageSpread) {
|
||||
val leftPart = Rect(0, 0, width / 2, height)
|
||||
canvas.drawBitmap(imageBitmap, imageBitmap.rect.also { it.right /= 2 }, leftPart, null)
|
||||
progressCallback?.invoke(98)
|
||||
val rightPart = Rect(
|
||||
width / 2 + adjustedHingeGap,
|
||||
0,
|
||||
width + adjustedHingeGap,
|
||||
height,
|
||||
)
|
||||
canvas.drawBitmap(imageBitmap, imageBitmap.rect.also { it.left = width / 2 }, rightPart, null)
|
||||
} else {
|
||||
val placeOnLeft = if (atBeginning == null) true else isLTR.xor(atBeginning)
|
||||
val upperPart = Rect(
|
||||
if (placeOnLeft) 0 else width + adjustedHingeGap,
|
||||
0,
|
||||
(if (placeOnLeft) 0 else width + adjustedHingeGap) + width,
|
||||
height,
|
||||
)
|
||||
canvas.drawBitmap(imageBitmap, imageBitmap.rect, upperPart, null)
|
||||
}
|
||||
progressCallback?.invoke(99)
|
||||
val output = ByteArrayOutputStream()
|
||||
result.compress(Bitmap.CompressFormat.JPEG, 100, output)
|
||||
progressCallback?.invoke(100)
|
||||
return ByteArrayInputStream(output.toByteArray())
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the image is considered a tall image.
|
||||
*
|
||||
|
|
|
@ -28,8 +28,7 @@
|
|||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/recycler"
|
||||
app:layout_constraintWidth_percent="0.4"
|
||||
app:layout_constraintEnd_toStartOf="@id/tablet_divider"
|
||||
tools:itemCount="1"
|
||||
tools:listitem="@layout/manga_header_item" />
|
||||
|
||||
|
@ -41,7 +40,7 @@
|
|||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/tablet_recycler"
|
||||
app:layout_constraintStart_toEndOf="@id/tablet_divider"
|
||||
tools:listitem="@layout/chapters_item" />
|
||||
|
||||
<View
|
||||
|
@ -51,17 +50,18 @@
|
|||
android:alpha=".80"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="20dp"
|
||||
app:layout_constraintWidth_percent=".6"
|
||||
app:layout_constraintStart_toStartOf="@id/recycler"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<View
|
||||
android:id="@+id/tablet_divider"
|
||||
android:visibility="gone"
|
||||
android:layout_width="1dp"
|
||||
android:visibility="gone"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/divider"
|
||||
app:layout_constraintEnd_toStartOf="@id/tablet_overlay"
|
||||
app:layout_constraintStart_toEndOf="@id/tablet_recycler"
|
||||
app:layout_constraintEnd_toStartOf="@id/recycler"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue