Added Source Migration to browse section

Don't ask me how I did it.
This commit is contained in:
Jays2Kings 2021-03-23 12:58:57 -04:00
parent 90a6543334
commit d6b07b7f40
11 changed files with 477 additions and 128 deletions

View File

@ -1,19 +1,33 @@
package eu.kanade.tachiyomi.ui.extension
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.extension.ExtensionManager
import eu.kanade.tachiyomi.extension.ExtensionsChangedListener
import eu.kanade.tachiyomi.extension.model.Extension
import eu.kanade.tachiyomi.extension.model.InstallStep
import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.ui.migration.MangaItem
import eu.kanade.tachiyomi.ui.migration.SelectionHeader
import eu.kanade.tachiyomi.ui.migration.SourceItem
import eu.kanade.tachiyomi.util.lang.combineLatest
import eu.kanade.tachiyomi.util.system.LocaleHelper
import eu.kanade.tachiyomi.util.system.executeOnIO
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import rx.Observable
import rx.android.schedulers.AndroidSchedulers
import rx.schedulers.Schedulers
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@ -32,10 +46,22 @@ class ExtensionBottomPresenter(
private var extensions = emptyList<ExtensionItem>()
var sourceItems = emptyList<SourceItem>()
private set
var mangaItems = hashMapOf<Long, List<MangaItem>>()
private set
private var currentDownloads = hashMapOf<String, InstallStep>()
private val sourceManager: SourceManager = Injekt.get()
private var selectedSource: Long? = null
private val db: DatabaseHelper = Injekt.get()
fun onCreate() {
scope.launch {
val extensionJob = async {
extensionManager.findAvailableExtensionsAsync()
extensions = toItems(
Triple(
@ -47,6 +73,35 @@ class ExtensionBottomPresenter(
withContext(Dispatchers.Main) { bottomSheet.setExtensions(extensions) }
extensionManager.setListener(this@ExtensionBottomPresenter)
}
val migrationJob = async {
val favs = db.getFavoriteMangas().executeOnIO()
sourceItems = findSourcesWithManga(favs)
mangaItems = HashMap(sourceItems.associate {
it.source.id to this@ExtensionBottomPresenter.libraryToMigrationItem(favs, it.source.id)
})
withContext(Dispatchers.Main) {
if (selectedSource != null) {
bottomSheet.setMigrationManga(mangaItems[selectedSource])
}
else {
bottomSheet.setMigrationSources(sourceItems)
}
}
}
listOf(migrationJob, extensionJob).awaitAll()
}
}
private fun findSourcesWithManga(library: List<Manga>): List<SourceItem> {
val header = SelectionHeader()
return library.map { it.source }.toSet()
.mapNotNull { if (it != LocalSource.ID) sourceManager.getOrStub(it) else null }
.sortedBy { it.name }
.map { SourceItem(it, header) }
}
private fun libraryToMigrationItem(library: List<Manga>, sourceId: Long): List<MangaItem> {
return library.filter { it.source == sourceId }.map(::MangaItem)
}
fun onDestroy() {
@ -66,6 +121,24 @@ class ExtensionBottomPresenter(
}
}
fun refreshMigrations() {
scope.launch {
val favs = db.getFavoriteMangas().executeOnIO()
sourceItems = findSourcesWithManga(favs)
mangaItems = HashMap(sourceItems.associate {
it.source.id to this@ExtensionBottomPresenter.libraryToMigrationItem(favs, it.source.id)
})
withContext(Dispatchers.Main) {
if (selectedSource != null) {
bottomSheet.setMigrationManga(mangaItems[selectedSource])
}
else {
bottomSheet.setMigrationSources(sourceItems)
}
}
}
}
override fun extensionsUpdated() {
refreshExtensions()
}
@ -166,4 +239,18 @@ class ExtensionBottomPresenter(
fun trustSignature(signatureHash: String) {
extensionManager.trustSignature(signatureHash)
}
fun setSelectedSource(source: Source) {
selectedSource = source.id
scope.launch {
withContext(Dispatchers.Main) { bottomSheet.setMigrationManga(mangaItems[source.id]) }
}
}
fun deselectSource() {
selectedSource = null
scope.launch {
withContext(Dispatchers.Main) { bottomSheet.setMigrationSources(sourceItems) }
}
}
}

View File

@ -2,16 +2,33 @@ package eu.kanade.tachiyomi.ui.extension
import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.LinearLayout
import androidx.core.view.get
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.IFlexible
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.extension.model.Extension
import eu.kanade.tachiyomi.ui.main.MainActivity
import eu.kanade.tachiyomi.ui.migration.MangaAdapter
import eu.kanade.tachiyomi.ui.migration.MangaItem
import eu.kanade.tachiyomi.ui.migration.SourceAdapter
import eu.kanade.tachiyomi.ui.migration.SourceItem
import eu.kanade.tachiyomi.ui.migration.manga.design.PreMigrationController
import eu.kanade.tachiyomi.ui.recents.RecentMangaHolder
import eu.kanade.tachiyomi.ui.source.SourceController
import eu.kanade.tachiyomi.util.system.getResourceColor
import eu.kanade.tachiyomi.util.system.await
import eu.kanade.tachiyomi.util.system.launchUI
import eu.kanade.tachiyomi.util.view.collapse
import eu.kanade.tachiyomi.util.view.doOnApplyWindowInsets
import eu.kanade.tachiyomi.util.view.expand
@ -19,15 +36,25 @@ import eu.kanade.tachiyomi.util.view.isExpanded
import eu.kanade.tachiyomi.util.view.updateLayoutParams
import eu.kanade.tachiyomi.util.view.updatePaddingRelative
import eu.kanade.tachiyomi.util.view.withFadeTransaction
import eu.kanade.tachiyomi.widget.ViewPagerAdapter
import kotlinx.android.synthetic.main.extensions_bottom_sheet.view.*
import kotlinx.android.synthetic.main.main_activity.*
import kotlinx.android.synthetic.main.migration_controller.*
import kotlinx.android.synthetic.main.recents_controller.*
import kotlinx.android.synthetic.main.recycler_with_scroller.view.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import rx.schedulers.Schedulers
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class ExtensionBottomSheet @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
LinearLayout(context, attrs),
ExtensionAdapter.OnButtonClickListener,
FlexibleAdapter.OnItemClickListener,
FlexibleAdapter.OnItemLongClickListener,
ExtensionTrustDialog.Listener {
ExtensionTrustDialog.Listener,
SourceAdapter.OnAllClickListener {
var sheetBehavior: BottomSheetBehavior<*>? = null
@ -37,28 +64,79 @@ class ExtensionBottomSheet @JvmOverloads constructor(context: Context, attrs: At
* Adapter containing the list of extensions
*/
private var adapter: FlexibleAdapter<IFlexible<*>>? = null
private var migAdapter: FlexibleAdapter<IFlexible<*>>? = null
val presenter = ExtensionBottomPresenter(this)
private var extensions: List<ExtensionItem> = emptyList()
var canExpand = false
lateinit var controller: SourceController
private val pAdapter = FlexibleAdapter<IFlexible<*>>(null, this, true)
val extensionFrameLayout =
inflate(context, R.layout.recycler_with_scroller, null) as FrameLayout
val migrationFrameLayout =
inflate(context, R.layout.recycler_with_scroller, null) as FrameLayout
fun onCreate(controller: SourceController) {
// Initialize adapter, scroll listener and recycler views
adapter = ExtensionAdapter(this)
migAdapter = ExtensionAdapter(this)
sheetBehavior = BottomSheetBehavior.from(this)
// Create recycler and set adapter.
ext_recycler.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(context)
ext_recycler.adapter = adapter
ext_recycler.setHasFixedSize(true)
ext_recycler.addItemDecoration(ExtensionDividerItemDecoration(context))
adapter?.fastScroller = fast_scroller
val extRecyler = extensionFrameLayout.recycler
val migRecyler = migrationFrameLayout.recycler
extRecyler.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(context)
extRecyler.adapter = adapter
extRecyler.setHasFixedSize(true)
extRecyler.addItemDecoration(ExtensionDividerItemDecoration(context))
migRecyler.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(context)
migRecyler.setHasFixedSize(true)
adapter?.fastScroller = extensionFrameLayout.fast_scroller
this.controller = controller
ext_recycler.doOnApplyWindowInsets { view, _, _ ->
val bottomBar = (this@ExtensionBottomSheet.controller.activity as? MainActivity)?.bottom_nav
view.updatePaddingRelative(bottom = bottomBar?.height ?: 0)
pager.doOnApplyWindowInsets { _, _, _ ->
val bottomBar =
(this@ExtensionBottomSheet.controller.activity as? MainActivity)?.bottom_nav
extRecyler.updatePaddingRelative(bottom = bottomBar?.height ?: 0)
migRecyler.updatePaddingRelative(bottom = bottomBar?.height ?: 0)
}
pager.adapter = TabbedSheetAdapter()
tabs.setupWithViewPager(pager)
tabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab?) {
if (canExpand) {
this@ExtensionBottomSheet.sheetBehavior?.expand()
}
this@ExtensionBottomSheet.controller.updateTitleAndMenu()
when (tab?.position) {
0 -> extensionFrameLayout
else -> migrationFrameLayout
}.recycler?.isNestedScrollingEnabled = true
when (tab?.position) {
0 -> extensionFrameLayout
else -> migrationFrameLayout
}.recycler?.requestLayout()
}
override fun onTabUnselected(tab: TabLayout.Tab?) {
when (tab?.position) {
0 -> extensionFrameLayout
else -> migrationFrameLayout
}.recycler?.isNestedScrollingEnabled = false
}
override fun onTabReselected(tab: TabLayout.Tab?) {
this@ExtensionBottomSheet.sheetBehavior?.expand()
when (tab?.position) {
0 -> extensionFrameLayout
else -> migrationFrameLayout
}.recycler?.isNestedScrollingEnabled = true
}
})
presenter.onCreate()
updateExtTitle()
@ -66,12 +144,6 @@ class ExtensionBottomSheet @JvmOverloads constructor(context: Context, attrs: At
val array = context.obtainStyledAttributes(attrsArray)
val headerHeight = array.getDimensionPixelSize(0, 0)
array.recycle()
ext_recycler_layout.doOnApplyWindowInsets { v, windowInsets, _ ->
v.updateLayoutParams<MarginLayoutParams> {
topMargin = windowInsets.systemWindowInsetTop + headerHeight -
(sheet_layout.height)
}
}
sheet_layout.setOnClickListener {
if (!sheetBehavior.isExpanded()) {
sheetBehavior?.expand()
@ -92,18 +164,8 @@ class ExtensionBottomSheet @JvmOverloads constructor(context: Context, attrs: At
fun updateExtTitle() {
val extCount = presenter.getExtensionUpdateCount()
title_text.text = if (extCount == 0) context.getString(R.string.extensions)
else resources.getQuantityString(
R.plurals.extension_updates_available,
extCount,
extCount
)
title_text.setTextColor(
context.getResourceColor(
if (extCount == 0) R.attr.actionBarTintColor else R.attr.colorAccent
)
)
if (extCount > 0) tabs.getTabAt(0)?.orCreateBadge?.number = extCount
else tabs.getTabAt(0)?.removeBadge()
}
override fun onButtonClick(position: Int) {
@ -126,22 +188,54 @@ class ExtensionBottomSheet @JvmOverloads constructor(context: Context, attrs: At
}
override fun onItemClick(view: View?, position: Int): Boolean {
val extension = (adapter?.getItem(position) as? ExtensionItem)?.extension ?: return false
when (tabs.selectedTabPosition) {
0 -> {
val extension =
(adapter?.getItem(position) as? ExtensionItem)?.extension ?: return false
if (extension is Extension.Installed) {
openDetails(extension)
} else if (extension is Extension.Untrusted) {
openTrustDialog(extension)
}
}
else -> {
val item = migAdapter?.getItem(position) ?: return false
if (item is MangaItem) {
PreMigrationController.navigateToMigration(
Injekt.get<PreferencesHelper>().skipPreMigration().getOrDefault(),
controller.router,
listOf(item.manga.id!!)
)
} else if (item is SourceItem) {
presenter.setSelectedSource(item.source)
}
}
}
return false
}
override fun onItemLongClick(position: Int) {
if (tabs.selectedTabPosition == 0) {
val extension = (adapter?.getItem(position) as? ExtensionItem)?.extension ?: return
if (extension is Extension.Installed || extension is Extension.Untrusted) {
uninstallExtension(extension.pkgName)
}
}
}
override fun onAllClick(position: Int) {
val item = migAdapter?.getItem(position) as? SourceItem ?: return
val sourceMangas =
presenter.mangaItems[item.source.id]?.mapNotNull { it.manga.id }?.toList()
?: emptyList()
PreMigrationController.navigateToMigration(
Injekt.get<PreferencesHelper>().skipPreMigration().getOrDefault(),
controller.router,
sourceMangas
)
}
private fun openDetails(extension: Extension.Installed) {
val controller = ExtensionDetailsController(extension.pkgName)
@ -159,8 +253,28 @@ class ExtensionBottomSheet @JvmOverloads constructor(context: Context, attrs: At
drawExtensions()
}
fun setMigrationSources(sources: List<SourceItem>) {
val migRecyler = migrationFrameLayout.recycler
if (migAdapter !is SourceAdapter) {
migAdapter = SourceAdapter(this)
migRecyler.adapter = migAdapter
migAdapter?.fastScroller = migrationFrameLayout.fast_scroller
}
migAdapter?.updateDataSet(sources)
}
fun setMigrationManga(manga: List<MangaItem>?) {
val migRecyler = migrationFrameLayout.recycler
if (migAdapter !is MangaAdapter) {
migAdapter = MangaAdapter(this)
migRecyler.adapter = migAdapter
migAdapter?.fastScroller = migrationFrameLayout.fast_scroller
}
migAdapter?.updateDataSet(manga)
}
fun drawExtensions() {
if (!controller.extQuery.isBlank()) {
if (controller.extQuery.isNotBlank()) {
adapter?.updateDataSet(
extensions.filter {
it.extension.name.contains(controller.extQuery, ignoreCase = true)
@ -172,6 +286,14 @@ class ExtensionBottomSheet @JvmOverloads constructor(context: Context, attrs: At
updateExtTitle()
}
fun canGoBack(): Boolean {
if (tabs.selectedTabPosition == 1 && migAdapter is MangaAdapter) {
presenter.deselectSource()
return false
}
return true
}
fun downloadUpdate(item: ExtensionItem) {
adapter?.updateItem(item, item.installStep)
}
@ -183,4 +305,25 @@ class ExtensionBottomSheet @JvmOverloads constructor(context: Context, attrs: At
override fun uninstallExtension(pkgName: String) {
presenter.uninstallExtension(pkgName)
}
private inner class TabbedSheetAdapter : ViewPagerAdapter() {
override fun createView(container: ViewGroup, position: Int): View {
return when (position) {
0 -> extensionFrameLayout
else -> migrationFrameLayout
}
}
override fun getCount(): Int {
return 2
}
override fun getPageTitle(position: Int): CharSequence {
return context.getString(when (position) {
0 -> R.string.extensions
else -> R.string.migration
})
}
}
}

View File

@ -3,8 +3,8 @@ package eu.kanade.tachiyomi.ui.migration
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.IFlexible
class MangaAdapter(controller: MigrationController) :
FlexibleAdapter<IFlexible<*>>(null, controller) {
class MangaAdapter(listener: Any) :
FlexibleAdapter<IFlexible<*>>(null, listener) {
private var items: List<IFlexible<*>>? = null

View File

@ -8,8 +8,8 @@ import eu.davidea.flexibleadapter.items.IFlexible
*
* @param controller instance of [MigrationController].
*/
class SourceAdapter(val controller: MigrationController) :
FlexibleAdapter<IFlexible<*>>(null, controller, true) {
class SourceAdapter(val allClickListener: OnAllClickListener) :
FlexibleAdapter<IFlexible<*>>(null, allClickListener, true) {
private var items: List<IFlexible<*>>? = null
@ -17,11 +17,6 @@ class SourceAdapter(val controller: MigrationController) :
setDisplayHeadersAtStartUp(true)
}
/**
* Listener for auto item clicks.
*/
val allClickListener: OnAllClickListener? = controller
/**
* Listener which should be called when user clicks select.
*/

View File

@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.ui.source
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
import android.app.Activity
import android.content.res.ColorStateList
import android.os.Parcelable
import android.view.LayoutInflater
import android.view.Menu
@ -10,6 +11,8 @@ import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.SearchView
import androidx.core.content.ContextCompat
import androidx.core.graphics.ColorUtils
import androidx.recyclerview.widget.RecyclerView
import com.bluelinelabs.conductor.ControllerChangeHandler
import com.bluelinelabs.conductor.ControllerChangeType
@ -29,12 +32,15 @@ import eu.kanade.tachiyomi.ui.extension.SettingsExtensionsController
import eu.kanade.tachiyomi.ui.main.BottomSheetController
import eu.kanade.tachiyomi.ui.main.MainActivity
import eu.kanade.tachiyomi.ui.main.RootSearchInterface
import eu.kanade.tachiyomi.ui.setting.SettingsBrowseController
import eu.kanade.tachiyomi.ui.setting.SettingsSourcesController
import eu.kanade.tachiyomi.ui.source.browse.BrowseSourceController
import eu.kanade.tachiyomi.ui.source.global_search.GlobalSearchController
import eu.kanade.tachiyomi.ui.source.latest.LatestUpdatesController
import eu.kanade.tachiyomi.util.system.dpToPx
import eu.kanade.tachiyomi.util.system.getBottomGestureInsets
import eu.kanade.tachiyomi.util.system.spToPx
import eu.kanade.tachiyomi.util.system.getResourceColor
import eu.kanade.tachiyomi.util.system.openInBrowser
import eu.kanade.tachiyomi.util.view.collapse
import eu.kanade.tachiyomi.util.view.expand
import eu.kanade.tachiyomi.util.view.isCollapsed
@ -48,17 +54,20 @@ import eu.kanade.tachiyomi.util.view.updatePaddingRelative
import eu.kanade.tachiyomi.util.view.withFadeTransaction
import kotlinx.android.parcel.Parcelize
import kotlinx.android.synthetic.main.extensions_bottom_sheet.*
import kotlinx.android.synthetic.main.extensions_bottom_sheet.ext_bottom_sheet
import kotlinx.android.synthetic.main.extensions_bottom_sheet.sheet_layout
import kotlinx.android.synthetic.main.extensions_bottom_sheet.view.*
import kotlinx.android.synthetic.main.filter_bottom_sheet.*
import kotlinx.android.synthetic.main.library_list_controller.*
import kotlinx.android.synthetic.main.main_activity.*
import kotlinx.android.synthetic.main.recycler_with_scroller.view.*
import kotlinx.android.synthetic.main.rounded_category_hopper.*
import kotlinx.android.synthetic.main.source_controller.*
import kotlinx.android.synthetic.main.source_controller.shadow2
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import kotlin.math.max
import kotlin.math.min
/**
* This controller shows and manages the different catalogues enabled by the user.
@ -101,7 +110,12 @@ class SourceController :
override fun getTitle(): String? {
return if (showingExtensions)
view?.context?.getString(R.string.extensions)
view?.context?.getString(
when (ext_bottom_sheet.tabs.selectedTabPosition) {
0 -> R.string.extensions
else -> R.string.source_migration
}
)
else view?.context?.getString(R.string.sources)
}
@ -144,6 +158,7 @@ class SourceController :
recycler?.post {
setBottomPadding()
setBottomSheetTabs(if (ext_bottom_sheet?.sheetBehavior.isCollapsed()) 0f else 1f)
}
recycler.addOnScrollListener(
object : RecyclerView.OnScrollListener() {
@ -163,12 +178,10 @@ class SourceController :
override fun onSlide(bottomSheet: View, progress: Float) {
val recycler = recycler ?: return
shadow2?.alpha = (1 - max(0f, progress)) * 0.25f
activity?.appbar?.elevation = max(
progress * 15f,
activity?.appbar?.elevation = min(
(1f - progress) * 15f,
if (recycler.canScrollVertically(-1)) 15f else 0f
)
sheet_layout?.alpha = 1 - progress
activity?.appbar?.y = max(activity!!.appbar.y, -headerHeight * (1 - progress))
val oldShow = showingExtensions
showingExtensions = progress > 0.92f
@ -176,9 +189,7 @@ class SourceController :
setTitle()
activity?.invalidateOptionsMenu()
}
val bottomBar = activity?.bottom_nav ?: return
val pad = bottomBar.translationY - bottomBar.height
ext_bottom_sheet.updatePaddingRelative(bottom = (pad * (1 - max(progress, 0f))).toInt())
setBottomSheetTabs(max(0f, progress))
}
override fun onStateChanged(p0: View, state: Int) {
@ -189,8 +200,6 @@ class SourceController :
if (state == BottomSheetBehavior.STATE_EXPANDED ||
state == BottomSheetBehavior.STATE_COLLAPSED
) {
sheet_layout?.alpha =
if (state == BottomSheetBehavior.STATE_COLLAPSED) 1f else 0f
showingExtensions = state == BottomSheetBehavior.STATE_EXPANDED
setTitle()
if (state == BottomSheetBehavior.STATE_EXPANDED)
@ -203,6 +212,7 @@ class SourceController :
RetainViewMode.RETAIN_DETACH else RetainViewMode.RELEASE_DETACH
sheet_layout.isClickable = state == BottomSheetBehavior.STATE_COLLAPSED
sheet_layout.isFocusable = state == BottomSheetBehavior.STATE_COLLAPSED
setBottomSheetTabs(if (state == BottomSheetBehavior.STATE_COLLAPSED) 0f else 1f)
}
}
)
@ -212,21 +222,73 @@ class SourceController :
}
}
fun updateTitleAndMenu() {
setTitle()
activity?.invalidateOptionsMenu()
}
fun setBottomSheetTabs(progress: Float) {
val bottomSheet = ext_bottom_sheet ?: return
val bottomBar = activity?.bottom_nav ?: return
ext_bottom_sheet.tabs.updateLayoutParams<ViewGroup.MarginLayoutParams> {
topMargin = ((activity?.appbar?.height?.minus(9f.dpToPx) ?: 0f) * progress).toInt()
}
val selectedColor = ColorUtils.setAlphaComponent(
ContextCompat.getColor(ext_bottom_sheet.tabs.context, R.color.colorAccent),
(progress * 255).toInt()
)
val unselectedColor = ColorUtils.setAlphaComponent(
bottomSheet.context.getResourceColor(R.attr.colorOnBackground),
153
)
ext_bottom_sheet.sheet_layout.elevation = progress * 5
ext_bottom_sheet.pager.alpha = progress * 10
ext_bottom_sheet.tabs.setSelectedTabIndicatorColor(selectedColor)
ext_bottom_sheet.tabs.setTabTextColors(
ColorUtils.blendARGB(
bottomSheet.context.getResourceColor(R.attr.actionBarTintColor),
unselectedColor,
progress),
ColorUtils.blendARGB(
bottomSheet.context.getResourceColor(R.attr.actionBarTintColor),
selectedColor,
progress)
)
val pad = bottomBar.translationY - bottomBar.height
val padding = (max(
(-pad).toInt(),
this@SourceController.view?.rootWindowInsets?.getBottomGestureInsets() ?: 0
) * (1f - progress)).toInt()
ext_bottom_sheet.updatePaddingRelative(
bottom = padding
)
ext_bottom_sheet.sheet_layout.backgroundTintList = ColorStateList.valueOf(
ColorUtils.blendARGB(
bottomSheet.context.getResourceColor(R.attr.colorPrimaryVariant),
bottomSheet.context.getResourceColor(R.attr.colorSecondary),
progress)
)
}
fun setBottomPadding() {
val bottomBar = activity?.bottom_nav ?: return
ext_bottom_sheet.updatePaddingRelative(
bottom =
ext_bottom_sheet ?: return
val pad = bottomBar.translationY - bottomBar.height
val padding = max(
(-pad).toInt(),
if (ext_bottom_sheet.sheetBehavior.isExpanded()) 0 else
max(
(-bottomBar.translationY + bottomBar.height).toInt(),
this@SourceController.view?.rootWindowInsets?.getBottomGestureInsets()
?: 0
)
ext_bottom_sheet.updatePaddingRelative(
bottom = padding
)
val pad = bottomBar.translationY - bottomBar.height
shadow2.translationY = pad
ext_bottom_sheet.sheetBehavior?.peekHeight = 48.spToPx + ext_bottom_sheet.paddingBottom
ext_bottom_sheet.fast_scroller.updateLayoutParams<ViewGroup.MarginLayoutParams> {
ext_bottom_sheet.sheetBehavior?.peekHeight = 60.dpToPx + padding
ext_bottom_sheet.extensionFrameLayout.fast_scroller.updateLayoutParams<ViewGroup.MarginLayoutParams> {
bottomMargin = -pad.toInt()
}
ext_bottom_sheet.migrationFrameLayout.fast_scroller.updateLayoutParams<ViewGroup.MarginLayoutParams> {
bottomMargin = -pad.toInt()
}
}
@ -247,7 +309,9 @@ class SourceController :
override fun handleSheetBack(): Boolean {
if (!ext_bottom_sheet.sheetBehavior.isCollapsed()) {
if (ext_bottom_sheet.canGoBack()) {
ext_bottom_sheet.sheetBehavior?.collapse()
}
return true
}
return false
@ -265,6 +329,23 @@ class SourceController :
ext_bottom_sheet.presenter.refreshExtensions()
}
if (!type.isEnter) {
ext_bottom_sheet.canExpand = false
activity?.appbar?.elevation =
when {
ext_bottom_sheet.sheetBehavior.isExpanded() -> 0f
recycler.canScrollVertically(-1) -> 15f
else -> 0f
}
} else {
ext_bottom_sheet.presenter.refreshMigrations()
}
setBottomPadding()
}
override fun onChangeEnded(handler: ControllerChangeHandler, type: ControllerChangeType) {
super.onChangeEnded(handler, type)
if (type.isEnter) {
ext_bottom_sheet.canExpand = true
setBottomPadding()
}
}
@ -272,6 +353,8 @@ class SourceController :
override fun onActivityResumed(activity: Activity) {
super.onActivityResumed(activity)
ext_bottom_sheet?.presenter?.refreshExtensions()
ext_bottom_sheet?.presenter?.refreshMigrations()
setBottomPadding()
}
override fun onItemClick(view: View, position: Int): Boolean {
@ -351,6 +434,7 @@ class SourceController :
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
if (onRoot) (activity as? MainActivity)?.setDismissIcon(showingExtensions)
if (showingExtensions) {
if (ext_bottom_sheet.tabs.selectedTabPosition == 0) {
// Inflate menu
inflater.inflate(R.menu.extension_main, menu)
@ -367,6 +451,9 @@ class SourceController :
ext_bottom_sheet.drawExtensions()
true
}
} else {
inflater.inflate(R.menu.migration_main, menu)
}
} else {
// Inflate menu
inflater.inflate(R.menu.catalogue_main, menu)
@ -410,6 +497,12 @@ class SourceController :
).pushChangeHandler(FadeChangeHandler())
)
}
R.id.action_migration_guide -> {
activity?.openInBrowser(HELP_URL)
}
R.id.action_sources_settings -> {
router.pushController(SettingsBrowseController().withFadeTransaction())
}
else -> return super.onOptionsItemSelected(item)
}
return true
@ -437,4 +530,8 @@ class SourceController :
@Parcelize
data class SmartSearchConfig(val origTitle: String, val origMangaId: Long) : Parcelable
companion object {
const val HELP_URL = "https://tachiyomi.org/help/guides/source-migration/"
}
}

View File

@ -1,6 +1,7 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/actionBarTintColor"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path

View File

@ -1,6 +1,7 @@
<!-- drawable/pin.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:tint="?attr/actionBarTintColor"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">

View File

@ -9,7 +9,7 @@
android:background="@drawable/bottom_sheet_rounded_background"
android:backgroundTint="?android:attr/colorBackground"
android:orientation="vertical"
app:behavior_peekHeight="48sp"
app:behavior_peekHeight="60sp"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
<LinearLayout
@ -19,6 +19,7 @@
android:background="@drawable/bottom_sheet_rounded_background"
android:backgroundTint="?attr/colorPrimaryVariant"
android:orientation="vertical"
android:elevation="5dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
@ -37,41 +38,25 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/title_text"
style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title"
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabs"
style="@style/Theme.Widget.Tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="6dp"
android:layout_marginBottom="10dp"
android:ellipsize="end"
android:gravity="center"
android:maxLines="1"
android:textAlignment="center"
android:textColor="?actionBarTintColor"
android:textSize="18sp"
tools:text="Extensions" />
android:background="@android:color/transparent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/menu"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:tabTextColor="@color/tabs_selector_background"
app:tabIndicatorColor="?attr/colorAccent"
app:tabRippleColor="@color/rippleColor"
app:tabGravity="fill"
app:tabMode="fixed" />
</LinearLayout>
<FrameLayout
android:id="@+id/ext_recycler_layout"
<androidx.viewpager.widget.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/ext_recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/colorBackground"
android:clipToPadding="false"
tools:listitem="@layout/extension_card_header" />
<eu.kanade.tachiyomi.ui.base.MaterialFastScroll
android:id="@+id/fast_scroller"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:fastScrollerBubbleEnabled="false" />
</FrameLayout>
android:layout_height="match_parent" />
</eu.kanade.tachiyomi.ui.extension.ExtensionBottomSheet>

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/recycler_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/colorBackground"
android:clipToPadding="false"
android:nestedScrollingEnabled="false"
tools:listitem="@layout/extension_card_header" />
<eu.kanade.tachiyomi.ui.base.MaterialFastScroll
android:id="@+id/fast_scroller"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:fastScrollerBubbleEnabled="false" />
</FrameLayout>

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_sources_settings"
android:icon="@drawable/ic_tune_24dp"
android:title="@string/options"
app:showAsAction="ifRoom" />
<item
android:id="@+id/action_migration_guide"
android:title="@string/help"
android:icon="@drawable/ic_help_24dp"
app:showAsAction="ifRoom"/>
</menu>

View File

@ -620,6 +620,7 @@
<string name="migration_sources_changed">Migration sources changed</string>
<string name="you_can_migrate_in_library">You can also migrate by selecting manga in your
library</string>
<string name="source_migration_guide">Source migration guide</string>
<!-- About section -->
<string name="version">Version</string>