Tune ui
This commit is contained in:
@@ -18,8 +18,8 @@ import com.google.android.material.R as materialR
|
||||
|
||||
class AnimatedPlaceholderDrawable(context: Context) : Drawable(), Animatable, TimeAnimator.TimeListener {
|
||||
|
||||
private val colorLow = context.getThemeColor(materialR.attr.colorSurfaceContainerLow)
|
||||
private val colorHigh = context.getThemeColor(materialR.attr.colorSurfaceContainerHigh)
|
||||
private val colorLow = context.getThemeColor(materialR.attr.colorBackgroundFloating)
|
||||
private val colorHigh = context.getThemeColor(materialR.attr.colorSurfaceContainer)
|
||||
private var currentColor: Int = colorLow
|
||||
private var alpha: Int = 255
|
||||
private val interpolator = FastOutSlowInInterpolator()
|
||||
|
||||
@@ -22,6 +22,10 @@ import androidx.core.graphics.ColorUtils
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
@@ -131,14 +135,17 @@ abstract class BaseAdaptiveSheet<B : ViewBinding> : AppCompatDialogFragment() {
|
||||
dialog?.window?.statusBarColor = defaultStatusBarColor
|
||||
}
|
||||
|
||||
fun addSheetCallback(callback: AdaptiveSheetCallback) {
|
||||
val b = behavior ?: return
|
||||
fun addSheetCallback(callback: AdaptiveSheetCallback, lifecycleOwner: LifecycleOwner): Boolean {
|
||||
val b = behavior ?: return false
|
||||
b.addCallback(callback)
|
||||
val rootView = dialog?.findViewById<View>(materialR.id.design_bottom_sheet)
|
||||
?: dialog?.findViewById(materialR.id.coordinator)
|
||||
?: view
|
||||
if (rootView != null) {
|
||||
callback.onStateChanged(rootView, b.state)
|
||||
}
|
||||
lifecycleOwner.lifecycle.addObserver(CallbackRemoveObserver(b, callback))
|
||||
return true
|
||||
}
|
||||
|
||||
protected abstract fun onCreateViewBinding(inflater: LayoutInflater, container: ViewGroup?): B
|
||||
@@ -146,7 +153,8 @@ abstract class BaseAdaptiveSheet<B : ViewBinding> : AppCompatDialogFragment() {
|
||||
protected open fun onViewBindingCreated(binding: B, savedInstanceState: Bundle?) = Unit
|
||||
|
||||
fun startSupportActionMode(callback: ActionMode.Callback): ActionMode? {
|
||||
val delegate = (dialog as? AppCompatDialog)?.delegate ?: (activity as? AppCompatActivity)?.delegate ?: return null
|
||||
val delegate =
|
||||
(dialog as? AppCompatDialog)?.delegate ?: (activity as? AppCompatActivity)?.delegate ?: return null
|
||||
return delegate.startSupportActionMode(callback)
|
||||
}
|
||||
|
||||
@@ -292,4 +300,16 @@ abstract class BaseAdaptiveSheet<B : ViewBinding> : AppCompatDialogFragment() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class CallbackRemoveObserver(
|
||||
private val behavior: AdaptiveSheetBehavior,
|
||||
private val callback: AdaptiveSheetCallback,
|
||||
) : DefaultLifecycleObserver {
|
||||
|
||||
override fun onDestroy(owner: LifecycleOwner) {
|
||||
super.onDestroy(owner)
|
||||
owner.lifecycle.removeObserver(this)
|
||||
behavior.removeCallback(callback)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ fun ImageRequest.Builder.defaultPlaceholders(context: Context): ImageRequest.Bui
|
||||
val errorColor = ColorUtils.blendARGB(
|
||||
context.getThemeColor(materialR.attr.colorErrorContainer),
|
||||
context.getThemeColor(materialR.attr.colorBackgroundFloating),
|
||||
0.4f,
|
||||
0.25f,
|
||||
)
|
||||
return placeholder(AnimatedPlaceholderDrawable(context))
|
||||
.fallback(ColorDrawable(context.getThemeColor(materialR.attr.colorSurfaceContainer)))
|
||||
|
||||
@@ -97,7 +97,7 @@ import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.parsers.model.MangaTag
|
||||
import org.koitharu.kotatsu.parsers.util.ellipsize
|
||||
import org.koitharu.kotatsu.reader.ui.ReaderActivity.IntentBuilder
|
||||
import org.koitharu.kotatsu.reader.ui.ReaderActivity
|
||||
import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblingInfo
|
||||
import org.koitharu.kotatsu.scrobbling.common.ui.selector.ScrobblingSelectorSheet
|
||||
import org.koitharu.kotatsu.search.ui.MangaListActivity
|
||||
@@ -320,7 +320,7 @@ class DetailsActivity :
|
||||
|
||||
override fun onItemClick(item: Bookmark, view: View) {
|
||||
startActivity(
|
||||
IntentBuilder(view.context).bookmark(item).incognito(true).build(),
|
||||
ReaderActivity.IntentBuilder(view.context).bookmark(item).incognito(true).build(),
|
||||
)
|
||||
Toast.makeText(view.context, R.string.incognito_mode, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
@@ -535,17 +535,18 @@ class DetailsActivity :
|
||||
}
|
||||
|
||||
private fun onHistoryChanged(info: HistoryInfo, isLoading: Boolean) = with(viewBinding) {
|
||||
buttonRead.setTitle(if (info.history != null) R.string._continue else R.string.read)
|
||||
buttonRead.setTitle(if (info.canContinue) R.string._continue else R.string.read)
|
||||
buttonRead.subtitle = when {
|
||||
isLoading -> getString(R.string.loading_)
|
||||
info.isIncognitoMode -> getString(R.string.incognito_mode)
|
||||
info.isChapterMissing -> getString(R.string.chapter_is_missing)
|
||||
info.currentChapter >= 0 -> getString(R.string.chapter_d_of_d, info.currentChapter + 1, info.totalChapters)
|
||||
info.totalChapters == 0 -> getString(R.string.no_chapters)
|
||||
info.totalChapters == -1 -> getString(R.string.error_occurred)
|
||||
else -> resources.getQuantityString(R.plurals.chapters, info.totalChapters, info.totalChapters)
|
||||
}
|
||||
buttonRead.setProgress(info.history?.percent?.coerceIn(0f, 1f) ?: 0f, true)
|
||||
buttonDownload?.isEnabled = info.isValid
|
||||
buttonDownload?.isEnabled = info.isValid && info.canDownload
|
||||
buttonRead.isEnabled = info.isValid
|
||||
}
|
||||
|
||||
@@ -605,7 +606,7 @@ class DetailsActivity :
|
||||
.show()
|
||||
} else {
|
||||
startActivity(
|
||||
IntentBuilder(this)
|
||||
ReaderActivity.IntentBuilder(this)
|
||||
.manga(manga)
|
||||
.branch(viewModel.selectedBranchValue)
|
||||
.incognito(isIncognitoMode)
|
||||
|
||||
@@ -84,8 +84,8 @@ class DetailsViewModel @Inject constructor(
|
||||
) : BaseViewModel() {
|
||||
|
||||
private val intent = MangaIntent(savedStateHandle)
|
||||
private val mangaId = intent.mangaId
|
||||
private var loadingJob: Job
|
||||
val mangaId = intent.mangaId
|
||||
|
||||
val onActionDone = MutableEventFlow<ReversibleAction>()
|
||||
val onShowTip = MutableEventFlow<Unit>()
|
||||
@@ -131,7 +131,7 @@ class DetailsViewModel @Inject constructor(
|
||||
)
|
||||
|
||||
val historyInfo: StateFlow<HistoryInfo> = combine(
|
||||
manga,
|
||||
details,
|
||||
selectedBranch,
|
||||
history,
|
||||
interactor.observeIncognitoMode(manga),
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package org.koitharu.kotatsu.details.ui.model
|
||||
|
||||
import org.koitharu.kotatsu.core.model.MangaHistory
|
||||
import org.koitharu.kotatsu.core.model.MangaSource
|
||||
import org.koitharu.kotatsu.core.model.isLocal
|
||||
import org.koitharu.kotatsu.details.data.MangaDetails
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
|
||||
data class HistoryInfo(
|
||||
@@ -8,26 +11,34 @@ data class HistoryInfo(
|
||||
val currentChapter: Int,
|
||||
val history: MangaHistory?,
|
||||
val isIncognitoMode: Boolean,
|
||||
val isChapterMissing: Boolean,
|
||||
val canDownload: Boolean,
|
||||
) {
|
||||
val isValid: Boolean
|
||||
get() = totalChapters >= 0
|
||||
|
||||
val canContinue: Boolean
|
||||
get() = history != null && !isChapterMissing
|
||||
}
|
||||
|
||||
fun HistoryInfo(
|
||||
manga: Manga?,
|
||||
manga: MangaDetails?,
|
||||
branch: String?,
|
||||
history: MangaHistory?,
|
||||
isIncognitoMode: Boolean
|
||||
): HistoryInfo {
|
||||
val chapters = manga?.getChapters(branch)
|
||||
val chapters = manga?.chapters?.get(branch)
|
||||
val currentChapter = if (history != null && !chapters.isNullOrEmpty()) {
|
||||
chapters.indexOfFirst { it.id == history.chapterId }
|
||||
} else {
|
||||
-2
|
||||
}
|
||||
return HistoryInfo(
|
||||
totalChapters = chapters?.size ?: -1,
|
||||
currentChapter = if (history != null && !chapters.isNullOrEmpty()) {
|
||||
chapters.indexOfFirst { it.id == history.chapterId }
|
||||
} else {
|
||||
-1
|
||||
},
|
||||
currentChapter = currentChapter,
|
||||
history = history,
|
||||
isIncognitoMode = isIncognitoMode,
|
||||
isChapterMissing = currentChapter == -1,
|
||||
canDownload = manga?.isLocal == false,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.koitharu.kotatsu.details.ui.pager
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.view.ActionMode
|
||||
import androidx.core.view.isVisible
|
||||
@@ -12,6 +13,12 @@ import com.google.android.material.tabs.TabLayoutMediator
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.koitharu.kotatsu.core.exceptions.resolve.SnackbarErrorObserver
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.ui.sheet.AdaptiveSheetBehavior
|
||||
import org.koitharu.kotatsu.core.ui.sheet.AdaptiveSheetBehavior.Companion.STATE_COLLAPSED
|
||||
import org.koitharu.kotatsu.core.ui.sheet.AdaptiveSheetBehavior.Companion.STATE_DRAGGING
|
||||
import org.koitharu.kotatsu.core.ui.sheet.AdaptiveSheetBehavior.Companion.STATE_EXPANDED
|
||||
import org.koitharu.kotatsu.core.ui.sheet.AdaptiveSheetBehavior.Companion.STATE_SETTLING
|
||||
import org.koitharu.kotatsu.core.ui.sheet.AdaptiveSheetCallback
|
||||
import org.koitharu.kotatsu.core.ui.sheet.BaseAdaptiveSheet
|
||||
import org.koitharu.kotatsu.core.ui.util.ActionModeListener
|
||||
import org.koitharu.kotatsu.core.ui.util.ReversibleActionObserver
|
||||
@@ -28,7 +35,7 @@ import org.koitharu.kotatsu.download.ui.worker.DownloadStartedObserver
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class ChaptersPagesSheet : BaseAdaptiveSheet<SheetChaptersPagesBinding>(), ActionModeListener {
|
||||
class ChaptersPagesSheet : BaseAdaptiveSheet<SheetChaptersPagesBinding>(), ActionModeListener, AdaptiveSheetCallback {
|
||||
|
||||
@Inject
|
||||
lateinit var settings: AppSettings
|
||||
@@ -58,6 +65,7 @@ class ChaptersPagesSheet : BaseAdaptiveSheet<SheetChaptersPagesBinding>(), Actio
|
||||
binding.toolbar.addMenuProvider(menuProvider)
|
||||
|
||||
actionModeDelegate?.addListener(this, viewLifecycleOwner)
|
||||
addSheetCallback(this, viewLifecycleOwner)
|
||||
|
||||
viewModel.onError.observeEvent(viewLifecycleOwner, SnackbarErrorObserver(binding.pager, this))
|
||||
viewModel.onActionDone.observeEvent(viewLifecycleOwner, ReversibleActionObserver(binding.pager, null))
|
||||
@@ -65,6 +73,14 @@ class ChaptersPagesSheet : BaseAdaptiveSheet<SheetChaptersPagesBinding>(), Actio
|
||||
viewModel.newChaptersCount.observe(viewLifecycleOwner, ::onNewChaptersChanged)
|
||||
}
|
||||
|
||||
override fun onStateChanged(sheet: View, newState: Int) {
|
||||
if (newState == STATE_DRAGGING || newState == STATE_SETTLING) {
|
||||
return
|
||||
}
|
||||
val isActionModeStarted = actionModeDelegate?.isActionModeStarted == true
|
||||
viewBinding?.toolbar?.menuView?.isVisible = newState != STATE_COLLAPSED && !isActionModeStarted
|
||||
}
|
||||
|
||||
override fun onActionModeStarted(mode: ActionMode) {
|
||||
expandAndLock()
|
||||
viewBinding?.toolbar?.menuView?.isVisible = false
|
||||
@@ -72,7 +88,8 @@ class ChaptersPagesSheet : BaseAdaptiveSheet<SheetChaptersPagesBinding>(), Actio
|
||||
|
||||
override fun onActionModeFinished(mode: ActionMode) {
|
||||
unlock()
|
||||
viewBinding?.toolbar?.menuView?.isVisible = true
|
||||
val state = behavior?.state ?: STATE_EXPANDED
|
||||
viewBinding?.toolbar?.menuView?.isVisible = state != STATE_COLLAPSED
|
||||
}
|
||||
|
||||
override fun expandAndLock() {
|
||||
|
||||
@@ -75,7 +75,9 @@ class PagesViewModel @Inject constructor(
|
||||
|
||||
private suspend fun doInit(state: State) {
|
||||
chaptersLoader.init(state.details)
|
||||
val initialChapterId = state.history?.chapterId ?: state.details.allChapters.firstOrNull()?.id ?: return
|
||||
val initialChapterId = state.history?.chapterId?.takeIf {
|
||||
chaptersLoader.peekChapter(it) != null
|
||||
} ?: state.details.allChapters.firstOrNull()?.id ?: return
|
||||
if (!chaptersLoader.hasPages(initialChapterId)) {
|
||||
chaptersLoader.loadSingleChapter(initialChapterId)
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import android.view.View.OnClickListener
|
||||
import android.view.View.OnLongClickListener
|
||||
import android.view.View.OnTouchListener
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.widget.ImageViewCompat
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
@@ -78,7 +79,7 @@ fun categoryAD(
|
||||
)
|
||||
}
|
||||
binding.imageViewTracker.isVisible = item.category.isTrackingEnabled
|
||||
binding.imageViewVisible.isVisible = item.category.isVisibleInLibrary
|
||||
binding.imageViewHidden.isGone = item.category.isVisibleInLibrary
|
||||
repeat(coverViews.size) { i ->
|
||||
val cover = item.covers.getOrNull(i)
|
||||
coverViews[i].newImageRequest(lifecycleOwner, cover?.url)?.run {
|
||||
|
||||
@@ -53,7 +53,7 @@ class TagsCatalogSheet : BaseAdaptiveSheet<SheetTagsBinding>(), OnListItemClickL
|
||||
binding.editSearch.onFocusChangeListener = this
|
||||
binding.editSearch.setOnEditorActionListener(this)
|
||||
viewModel.content.observe(viewLifecycleOwner, adapter)
|
||||
addSheetCallback(this)
|
||||
addSheetCallback(this, viewLifecycleOwner)
|
||||
disableFitToContents()
|
||||
}
|
||||
|
||||
|
||||
@@ -262,7 +262,7 @@
|
||||
android:id="@+id/chips_tags"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/margin_small"
|
||||
android:layout_marginTop="@dimen/margin_normal"
|
||||
android:paddingStart="@dimen/screen_padding"
|
||||
android:paddingEnd="@dimen/screen_padding"
|
||||
app:chipSpacingHorizontal="6dp"
|
||||
|
||||
@@ -104,13 +104,13 @@
|
||||
android:layout_marginEnd="4dp"
|
||||
android:contentDescription="@string/check_for_new_chapters"
|
||||
app:layout_constraintBottom_toBottomOf="@id/textView_subtitle"
|
||||
app:layout_constraintEnd_toStartOf="@id/imageView_visible"
|
||||
app:layout_constraintEnd_toStartOf="@id/imageView_hidden"
|
||||
app:layout_constraintStart_toEndOf="@id/textView_subtitle"
|
||||
app:layout_constraintTop_toTopOf="@id/textView_subtitle"
|
||||
app:srcCompat="@drawable/ic_notification" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView_visible"
|
||||
android:id="@+id/imageView_hidden"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:contentDescription="@string/show_on_shelf"
|
||||
@@ -118,7 +118,7 @@
|
||||
app:layout_constraintEnd_toEndOf="@id/textView_title"
|
||||
app:layout_constraintStart_toEndOf="@id/imageView_tracker"
|
||||
app:layout_constraintTop_toTopOf="@id/textView_subtitle"
|
||||
app:srcCompat="@drawable/ic_eye" />
|
||||
app:srcCompat="@drawable/ic_eye_off" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView_edit"
|
||||
|
||||
Reference in New Issue
Block a user