From d6b07b7f404b4b68f5d8be49ff4509f0f6980a64 Mon Sep 17 00:00:00 2001 From: Jays2Kings Date: Tue, 23 Mar 2021 12:58:57 -0400 Subject: [PATCH] Added Source Migration to browse section Don't ask me how I did it. --- .../ui/extension/ExtensionBottomPresenter.kt | 105 ++++++++- .../ui/extension/ExtensionBottomSheet.kt | 219 +++++++++++++++--- .../tachiyomi/ui/migration/MangaAdapter.kt | 4 +- .../tachiyomi/ui/migration/SourceAdapter.kt | 9 +- .../tachiyomi/ui/source/SourceController.kt | 167 ++++++++++--- app/src/main/res/drawable/ic_browse_24dp.xml | 9 +- app/src/main/res/drawable/ic_pin_24dp.xml | 1 + .../res/layout/extensions_bottom_sheet.xml | 51 ++-- .../res/layout/recycler_with_scroller.xml | 24 ++ app/src/main/res/menu/migration_main.xml | 15 ++ app/src/main/res/values/strings.xml | 1 + 11 files changed, 477 insertions(+), 128 deletions(-) create mode 100644 app/src/main/res/layout/recycler_with_scroller.xml create mode 100644 app/src/main/res/menu/migration_main.xml diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionBottomPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionBottomPresenter.kt index 521caf95c2..e25257b711 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionBottomPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionBottomPresenter.kt @@ -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,23 +46,64 @@ class ExtensionBottomPresenter( private var extensions = emptyList() + var sourceItems = emptyList() + private set + + var mangaItems = hashMapOf>() + private set + private var currentDownloads = hashMapOf() + private val sourceManager: SourceManager = Injekt.get() + + private var selectedSource: Long? = null + private val db: DatabaseHelper = Injekt.get() + fun onCreate() { scope.launch { - extensionManager.findAvailableExtensionsAsync() - extensions = toItems( - Triple( - extensionManager.installedExtensions, - extensionManager.untrustedExtensions, - extensionManager.availableExtensions + val extensionJob = async { + extensionManager.findAvailableExtensionsAsync() + extensions = toItems( + Triple( + extensionManager.installedExtensions, + extensionManager.untrustedExtensions, + extensionManager.availableExtensions + ) ) - ) - withContext(Dispatchers.Main) { bottomSheet.setExtensions(extensions) } - extensionManager.setListener(this@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): List { + 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, sourceId: Long): List { + return library.filter { it.source == sourceId }.map(::MangaItem) + } + fun onDestroy() { extensionManager.removeListener(this) } @@ -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) } + } + } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionBottomSheet.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionBottomSheet.kt index 58be714390..8749dafd3e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionBottomSheet.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionBottomSheet.kt @@ -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>? = null + private var migAdapter: FlexibleAdapter>? = null val presenter = ExtensionBottomPresenter(this) private var extensions: List = emptyList() + var canExpand = false lateinit var controller: SourceController + private val pAdapter = FlexibleAdapter>(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 { - 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,23 +188,55 @@ 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 - if (extension is Extension.Installed) { - openDetails(extension) - } else if (extension is Extension.Untrusted) { - openTrustDialog(extension) - } + 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().skipPreMigration().getOrDefault(), + controller.router, + listOf(item.manga.id!!) + ) + } else if (item is SourceItem) { + presenter.setSelectedSource(item.source) + } + } + } return false } override fun onItemLongClick(position: Int) { - val extension = (adapter?.getItem(position) as? ExtensionItem)?.extension ?: return - if (extension is Extension.Installed || extension is Extension.Untrusted) { - uninstallExtension(extension.pkgName) + 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().skipPreMigration().getOrDefault(), + controller.router, + sourceMangas + ) + } + private fun openDetails(extension: Extension.Installed) { val controller = ExtensionDetailsController(extension.pkgName) this.controller.router.pushController(controller.withFadeTransaction()) @@ -159,8 +253,28 @@ class ExtensionBottomSheet @JvmOverloads constructor(context: Context, attrs: At drawExtensions() } + fun setMigrationSources(sources: List) { + 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?) { + 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 + }) + } + } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/migration/MangaAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/MangaAdapter.kt index 5cf41f0cd6..93b7050791 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/migration/MangaAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/MangaAdapter.kt @@ -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>(null, controller) { +class MangaAdapter(listener: Any) : + FlexibleAdapter>(null, listener) { private var items: List>? = null diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/migration/SourceAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/SourceAdapter.kt index a0b4185174..3be604dd7a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/migration/SourceAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/SourceAdapter.kt @@ -8,8 +8,8 @@ import eu.davidea.flexibleadapter.items.IFlexible * * @param controller instance of [MigrationController]. */ -class SourceAdapter(val controller: MigrationController) : - FlexibleAdapter>(null, controller, true) { +class SourceAdapter(val allClickListener: OnAllClickListener) : + FlexibleAdapter>(null, allClickListener, true) { private var items: List>? = 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. */ diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/source/SourceController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/source/SourceController.kt index 55e6bc84fd..59c6e337a7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/source/SourceController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/source/SourceController.kt @@ -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 setBottomPadding() { + fun updateTitleAndMenu() { + setTitle() + activity?.invalidateOptionsMenu() + } + + fun setBottomSheetTabs(progress: Float) { + val bottomSheet = ext_bottom_sheet ?: return val bottomBar = activity?.bottom_nav ?: return - ext_bottom_sheet.updatePaddingRelative( - bottom = - if (ext_bottom_sheet.sheetBehavior.isExpanded()) 0 else - max( - (-bottomBar.translationY + bottomBar.height).toInt(), - this@SourceController.view?.rootWindowInsets?.getBottomGestureInsets() - ?: 0 - ) + ext_bottom_sheet.tabs.updateLayoutParams { + 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 ?: return + val pad = bottomBar.translationY - bottomBar.height + val padding = max( + (-pad).toInt(), + if (ext_bottom_sheet.sheetBehavior.isExpanded()) 0 else + this@SourceController.view?.rootWindowInsets?.getBottomGestureInsets() + ?: 0 + ) + ext_bottom_sheet.updatePaddingRelative( + bottom = padding + ) shadow2.translationY = pad - ext_bottom_sheet.sheetBehavior?.peekHeight = 48.spToPx + ext_bottom_sheet.paddingBottom - ext_bottom_sheet.fast_scroller.updateLayoutParams { + ext_bottom_sheet.sheetBehavior?.peekHeight = 60.dpToPx + padding + ext_bottom_sheet.extensionFrameLayout.fast_scroller.updateLayoutParams { + bottomMargin = -pad.toInt() + } + ext_bottom_sheet.migrationFrameLayout.fast_scroller.updateLayoutParams { bottomMargin = -pad.toInt() } } @@ -247,7 +309,9 @@ class SourceController : override fun handleSheetBack(): Boolean { if (!ext_bottom_sheet.sheetBehavior.isCollapsed()) { - ext_bottom_sheet.sheetBehavior?.collapse() + 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,21 +434,25 @@ class SourceController : override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { if (onRoot) (activity as? MainActivity)?.setDismissIcon(showingExtensions) if (showingExtensions) { - // Inflate menu - inflater.inflate(R.menu.extension_main, menu) + if (ext_bottom_sheet.tabs.selectedTabPosition == 0) { + // Inflate menu + inflater.inflate(R.menu.extension_main, menu) - // Initialize search option. - val searchItem = menu.findItem(R.id.action_search) - val searchView = searchItem.actionView as SearchView + // Initialize search option. + val searchItem = menu.findItem(R.id.action_search) + val searchView = searchItem.actionView as SearchView - // Change hint to show global search. - searchView.queryHint = view?.context?.getString(R.string.search_extensions) + // Change hint to show global search. + searchView.queryHint = view?.context?.getString(R.string.search_extensions) - // Create query listener which opens the global search view. - setOnQueryTextChangeListener(searchView) { - extQuery = it ?: "" - ext_bottom_sheet.drawExtensions() - true + // Create query listener which opens the global search view. + setOnQueryTextChangeListener(searchView) { + extQuery = it ?: "" + ext_bottom_sheet.drawExtensions() + true + } + } else { + inflater.inflate(R.menu.migration_main, menu) } } else { // Inflate 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/" + } } diff --git a/app/src/main/res/drawable/ic_browse_24dp.xml b/app/src/main/res/drawable/ic_browse_24dp.xml index 68aa814795..21f4f417a2 100644 --- a/app/src/main/res/drawable/ic_browse_24dp.xml +++ b/app/src/main/res/drawable/ic_browse_24dp.xml @@ -1,8 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:tint="?attr/actionBarTintColor" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> diff --git a/app/src/main/res/drawable/ic_pin_24dp.xml b/app/src/main/res/drawable/ic_pin_24dp.xml index fe0e5245a2..1ac6a56c96 100644 --- a/app/src/main/res/drawable/ic_pin_24dp.xml +++ b/app/src/main/res/drawable/ic_pin_24dp.xml @@ -1,6 +1,7 @@ diff --git a/app/src/main/res/layout/extensions_bottom_sheet.xml b/app/src/main/res/layout/extensions_bottom_sheet.xml index b496c12fe2..99d6785b87 100644 --- a/app/src/main/res/layout/extensions_bottom_sheet.xml +++ b/app/src/main/res/layout/extensions_bottom_sheet.xml @@ -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"> - + 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" /> - - - - - - - + android:layout_height="match_parent" /> \ No newline at end of file diff --git a/app/src/main/res/layout/recycler_with_scroller.xml b/app/src/main/res/layout/recycler_with_scroller.xml new file mode 100644 index 0000000000..5c1aef23b9 --- /dev/null +++ b/app/src/main/res/layout/recycler_with_scroller.xml @@ -0,0 +1,24 @@ + + + + + + + + diff --git a/app/src/main/res/menu/migration_main.xml b/app/src/main/res/menu/migration_main.xml new file mode 100644 index 0000000000..106bb80330 --- /dev/null +++ b/app/src/main/res/menu/migration_main.xml @@ -0,0 +1,15 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bf14f4970c..2720161d2e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -620,6 +620,7 @@ Migration sources changed You can also migrate by selecting manga in your library + Source migration guide Version