From 49d95e2dd0aa0c74c15065bda6c835bf869b52d2 Mon Sep 17 00:00:00 2001 From: Jay Date: Wed, 19 Feb 2020 21:29:45 -0800 Subject: [PATCH] Fixed Update notifcation + Moved single list library to extended class Fixing progress bar for category header and stop spinning after update --- .../data/library/LibraryUpdateService.kt | 217 +++++---- .../ui/library/LibraryCategoryView.kt | 2 +- .../tachiyomi/ui/library/LibraryController.kt | 460 +++--------------- .../tachiyomi/ui/library/LibraryHeaderItem.kt | 15 +- .../ui/library/LibraryListController.kt | 444 +++++++++++++++++ .../library/filter/SortFilterBottomSheet.kt | 4 +- .../kanade/tachiyomi/ui/main/MainActivity.kt | 7 +- .../RecentChaptersController.kt | 4 +- .../ui/setting/SettingsAdvancedController.kt | 12 +- .../layout/library_category_header_item.xml | 15 +- .../main/res/layout/library_controller.xml | 17 - .../res/layout/library_list_controller.xml | 69 +++ .../res/layout/library_spinner_textview.xml | 1 + app/src/main/res/values/attrs.xml | 2 +- 14 files changed, 723 insertions(+), 546 deletions(-) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryListController.kt create mode 100644 app/src/main/res/layout/library_list_controller.xml diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt index b1624cc18f..19dc598932 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt @@ -37,13 +37,14 @@ import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.ui.main.MainActivity import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource import eu.kanade.tachiyomi.util.lang.chop -import eu.kanade.tachiyomi.util.system.isServiceRunning import eu.kanade.tachiyomi.util.system.notification import eu.kanade.tachiyomi.util.system.notificationManager import kotlinx.coroutines.CoroutineExceptionHandler +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.Job import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import rx.Observable import rx.Subscription import rx.schedulers.Schedulers @@ -98,6 +99,13 @@ class LibraryUpdateService( private var job:Job? = null + private val mangaToUpdate = mutableListOf() + + private val categoryIds = mutableSetOf() + + // List containing new updates + private val newUpdates = ArrayList>>() + /** * Cached progress notification to avoid creating a lot. */ @@ -127,11 +135,8 @@ class LibraryUpdateService( */ const val KEY_CATEGORY = "category" - private val mangaToUpdate = mutableListOf() - - private val categoryIds = mutableSetOf() - - fun categoryInQueue(id: Int?) = categoryIds.contains(id) + fun categoryInQueue(id: Int?) = instance?.categoryIds?.contains(id) ?: false + private var instance: LibraryUpdateService? = null /** * Key that defines what should be updated. @@ -141,11 +146,10 @@ class LibraryUpdateService( /** * Returns the status of the service. * - * @param context the application context. * @return true if the service is running, false otherwise. */ - fun isRunning(context: Context): Boolean { - return context.isServiceRunning(LibraryUpdateService::class.java) + fun isRunning(): Boolean { + return instance != null } /** @@ -157,12 +161,11 @@ class LibraryUpdateService( * @param target defines what should be updated. */ fun start(context: Context, category: Category? = null, target: Target = Target.CHAPTERS) { - if (!isRunning(context)) { + if (!isRunning()) { val intent = Intent(context, LibraryUpdateService::class.java).apply { putExtra(KEY_TARGET, target) category?.id?.let { id -> putExtra(KEY_CATEGORY, id) - categoryIds.add(id) } } if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { @@ -173,22 +176,11 @@ class LibraryUpdateService( } else { if (target == Target.CHAPTERS) category?.id?.let { - categoryIds.add(it) - val preferences: PreferencesHelper = Injekt.get() - val selectedScheme = preferences.libraryUpdatePrioritization().getOrDefault() - addManga(getMangaToUpdate(it, target).sortedWith( - rankingScheme[selectedScheme] - )) + instance?.addCategory(it) } } } - private fun addManga(mangaToAdd: List) { - for (manga in mangaToAdd) { - if (mangaToUpdate.none { it.id == manga.id }) mangaToUpdate.add(manga) - } - } - /** * Stops the service. * @@ -198,40 +190,6 @@ class LibraryUpdateService( context.stopService(Intent(context, LibraryUpdateService::class.java)) } - /** - * Returns the list of manga to be updated. - * - * @param intent the update intent. - * @param target the target to update. - * @return a list of manga to update - */ - private fun getMangaToUpdate(categoryId: Int, target: Target): List { - val preferences: PreferencesHelper = Injekt.get() - val db: DatabaseHelper = Injekt.get() - var listToUpdate = if (categoryId != -1) - db.getLibraryMangas().executeAsBlocking().filter { it.category == categoryId } - else { - val categoriesToUpdate = preferences.libraryUpdateCategories().getOrDefault().map(String::toInt) - categoryIds.addAll(categoriesToUpdate) - if (categoriesToUpdate.isNotEmpty()) - db.getLibraryMangas().executeAsBlocking() - .filter { it.category in categoriesToUpdate } - .distinctBy { it.id } - else - db.getLibraryMangas().executeAsBlocking().distinctBy { it.id } - } - if (target == Target.CHAPTERS && preferences.updateOnlyNonCompleted()) { - listToUpdate = listToUpdate.filter { it.status != SManga.COMPLETED } - } - - return listToUpdate - } - - private fun getMangaToUpdate(intent: Intent, target: Target): List { - val categoryId = intent.getIntExtra(KEY_CATEGORY, -1) - return getMangaToUpdate(categoryId, target) - } - private var listener:LibraryServiceListener? = null fun setListener(listener: LibraryServiceListener) { @@ -243,6 +201,55 @@ class LibraryUpdateService( } } + private fun addManga(mangaToAdd: List) { + for (manga in mangaToAdd) { + if (mangaToUpdate.none { it.id == manga.id }) mangaToUpdate.add(manga) + } + } + + private fun addCategory(categoryId: Int) { + val selectedScheme = preferences.libraryUpdatePrioritization().getOrDefault() + val mangas = + getMangaToUpdate(categoryId, Target.CHAPTERS).sortedWith( + rankingScheme[selectedScheme]) + categoryIds.add(categoryId) + addManga(mangas) + } + + /** + * Returns the list of manga to be updated. + * + * @param intent the update intent. + * @param target the target to update. + * @return a list of manga to update + */ + private fun getMangaToUpdate(categoryId: Int, target: Target): List { + var listToUpdate = if (categoryId != -1) { + categoryIds.add(categoryId) + db.getLibraryMangas().executeAsBlocking().filter { it.category == categoryId } + } + else { + val categoriesToUpdate = preferences.libraryUpdateCategories().getOrDefault().map(String::toInt) + categoryIds.addAll(categoriesToUpdate) + if (categoriesToUpdate.isNotEmpty()) + db.getLibraryMangas().executeAsBlocking() + .filter { it.category in categoriesToUpdate } + .distinctBy { it.id } + else + db.getLibraryMangas().executeAsBlocking().distinctBy { it.id } + } + if (target == Target.CHAPTERS && preferences.updateOnlyNonCompleted()) { + listToUpdate = listToUpdate.filter { it.status != SManga.COMPLETED } + } + + return listToUpdate + } + + private fun getMangaToUpdate(intent: Intent, target: Target): List { + val categoryId = intent.getIntExtra(KEY_CATEGORY, -1) + return getMangaToUpdate(categoryId, target) + } + /** * Method called when the service is created. It injects dagger dependencies and acquire * the wake lock. @@ -255,22 +262,19 @@ class LibraryUpdateService( wakeLock.acquire(TimeUnit.MINUTES.toMillis(30)) } - override fun stopService(name: Intent?): Boolean { - job?.cancel() - return super.stopService(name) - } - /** * Method called when the service is destroyed. It destroys subscriptions and releases the wake * lock. */ override fun onDestroy() { + job?.cancel() + if (instance == this) + instance = null subscription?.unsubscribe() - mangaToUpdate.clear() - categoryIds.clear() if (wakeLock.isHeld) { wakeLock.release() } + listener?.onUpdateManga(LibraryManga()) super.onDestroy() } @@ -291,23 +295,22 @@ class LibraryUpdateService( */ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { if (intent == null) return START_NOT_STICKY - val target = intent.getSerializableExtra(KEY_TARGET) as? Target ?: return START_NOT_STICKY + val target = intent.getSerializableExtra(KEY_TARGET) as? Target + ?: return START_NOT_STICKY // Unsubscribe from any previous subscription if needed. - job?.cancel() subscription?.unsubscribe() + instance = this val selectedScheme = preferences.libraryUpdatePrioritization().getOrDefault() + val mangaList = + getMangaToUpdate(intent, target).sortedWith(rankingScheme[selectedScheme]) + // Update favorite manga. Destroy service when completed or in case of an error. if (target == Target.CHAPTERS) { - updateChapters( - getMangaToUpdate(intent, target).sortedWith(rankingScheme[selectedScheme]), startId - ) + updateChapters(mangaList, startId) } else { // Update either chapter list or manga details. - // Update favorite manga. Destroy service when completed or in case of an error. - val mangaList = - getMangaToUpdate(intent, target).sortedWith(rankingScheme[selectedScheme]) subscription = Observable.defer { when (target) { Target.DETAILS -> updateDetails(mangaList) @@ -324,25 +327,26 @@ class LibraryUpdateService( } private fun updateChapters(mangaToAdd: List, startId: Int) { - addManga(mangaToAdd) - val handler = CoroutineExceptionHandler { _, exception -> Timber.e(exception) + + // Boolean to determine if user wants to automatically download new chapters. + val downloadNew = preferences.downloadNew().getOrDefault() + if (newUpdates.isNotEmpty()) { + showResultNotification(newUpdates) + if (downloadNew && downloadManager.queue.isNotEmpty()) { + DownloadService.start(this) + } + } stopSelf(startId) } job = GlobalScope.launch(handler) { - updateChaptersJob() - mangaToUpdate.clear() - categoryIds.clear() - stopSelf(startId) + updateChaptersJob(mangaToAdd) } + job?.invokeOnCompletion { stopSelf(startId) } } - private fun updateChaptersJob() { - // Initialize the variables holding the progress of the updates. - var count = 0 - // List containing new updates - val newUpdates = ArrayList>>() + private suspend fun updateChaptersJob(mangaToAdd: List) { // list containing failed updates val failedUpdates = ArrayList() // List containing categories that get included in downloads. @@ -351,28 +355,37 @@ class LibraryUpdateService( val downloadNew = preferences.downloadNew().getOrDefault() // Boolean to determine if DownloadManager has downloads var hasDownloads = false + withContext(Dispatchers.IO) { + // Initialize the variables holding the progress of the updates. + var count = 0 - while (count < mangaToUpdate.size) { - if (job?.isCancelled == true || job == null) break - val manga = mangaToUpdate[count] - showProgressNotification(manga, count++, mangaToUpdate.size) - val source = sourceManager.get(manga.source) as? HttpSource ?: continue - val fetchedChapters = try { source.fetchChapterList(manga).toBlocking().single() } - catch(e: java.lang.Exception) { - failedUpdates.add(manga) - emptyList() - } - if (fetchedChapters.isNotEmpty()) { - val newChapters = syncChaptersWithSource(db, fetchedChapters, manga, source) - if (newChapters.first.isNotEmpty()) { - if (downloadNew && (categoriesToDownload.isEmpty() || manga.category in categoriesToDownload)) { - downloadChapters(manga, newChapters.first.sortedBy { it.chapter_number }) - hasDownloads = true + mangaToUpdate.addAll(mangaToAdd) + while (count < mangaToUpdate.size) { + if (job?.isCancelled == true) break + val manga = mangaToUpdate[count] + showProgressNotification(manga, count++, mangaToUpdate.size) + val source = sourceManager.get(manga.source) as? HttpSource ?: continue + val fetchedChapters = try { + source.fetchChapterList(manga).toBlocking().single() + } catch (e: java.lang.Exception) { + failedUpdates.add(manga) + emptyList() + } ?: emptyList() + if (fetchedChapters.isNotEmpty()) { + val newChapters = syncChaptersWithSource(db, fetchedChapters, manga, source) + if (newChapters.first.isNotEmpty()) { + if (downloadNew && (categoriesToDownload.isEmpty() || manga.category in categoriesToDownload)) { + downloadChapters( + manga, + newChapters.first.sortedBy { it.chapter_number }) + hasDownloads = true + } + newUpdates.add(manga to newChapters.first.sortedBy { it.chapter_number }.toTypedArray()) } - newUpdates.add(manga to newChapters.first.sortedBy { it.chapter_number }.toTypedArray()) + if (newChapters.first.size + newChapters.second.size > 0) listener?.onUpdateManga( + manga + ) } - if (newChapters.first.size + newChapters.second.size > 0) - listener?.onUpdateManga(manga) } } if (newUpdates.isNotEmpty()) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryView.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryView.kt index 031016c3c2..e0b9713646 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryView.kt @@ -120,7 +120,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att resources.getString( when { inQueue -> R.string.category_already_in_queue - LibraryUpdateService.isRunning(context) -> R.string.adding_category_to_queue + LibraryUpdateService.isRunning() -> R.string.adding_category_to_queue else -> R.string.updating_category_x }, category.name)) if (!inQueue) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt index 167d424115..f22aa1ec17 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt @@ -12,15 +12,10 @@ import android.view.MenuItem import android.view.View import android.view.ViewGroup import android.view.inputmethod.InputMethodManager -import android.widget.Spinner import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.view.ActionMode import androidx.appcompat.widget.SearchView import androidx.core.graphics.drawable.DrawableCompat -import androidx.core.math.MathUtils.clamp -import androidx.recyclerview.widget.GridLayoutManager -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView import androidx.viewpager.widget.ViewPager import com.afollestad.materialdialogs.MaterialDialog import com.bluelinelabs.conductor.ControllerChangeHandler @@ -32,8 +27,6 @@ import com.google.android.material.snackbar.Snackbar import com.google.android.material.tabs.TabLayout import com.jakewharton.rxrelay.BehaviorRelay import com.jakewharton.rxrelay.PublishRelay -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.SelectableAdapter import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.models.Category import eu.kanade.tachiyomi.data.database.models.LibraryManga @@ -58,46 +51,31 @@ import eu.kanade.tachiyomi.ui.migration.manga.design.PreMigrationController import eu.kanade.tachiyomi.ui.migration.manga.process.MigrationListController import eu.kanade.tachiyomi.ui.migration.manga.process.MigrationProcedureConfig import eu.kanade.tachiyomi.ui.reader.ReaderActivity -import eu.kanade.tachiyomi.util.system.dpToPx import eu.kanade.tachiyomi.util.system.getResourceColor import eu.kanade.tachiyomi.util.system.launchUI -import eu.kanade.tachiyomi.util.view.gone -import eu.kanade.tachiyomi.util.view.inflate import eu.kanade.tachiyomi.util.view.setOnQueryTextChangeListener import eu.kanade.tachiyomi.util.view.snack -import eu.kanade.tachiyomi.util.view.updatePaddingRelative -import eu.kanade.tachiyomi.util.view.visible -import eu.kanade.tachiyomi.widget.AutofitRecyclerView -import eu.kanade.tachiyomi.widget.IgnoreFirstSpinnerListener import kotlinx.android.synthetic.main.filter_bottom_sheet.* import kotlinx.android.synthetic.main.library_controller.* import kotlinx.android.synthetic.main.main_activity.* -import kotlinx.coroutines.delay import rx.Subscription import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -import java.util.Locale -import kotlin.math.min -class LibraryController( +open class LibraryController( bundle: Bundle? = null, - private val preferences: PreferencesHelper = Injekt.get() + protected val preferences: PreferencesHelper = Injekt.get() ) : BaseController(bundle), TabbedController, ActionMode.Callback, ChangeMangaCategoriesDialog.Listener, MigrationInterface, DownloadServiceListener, - LibraryServiceListener, - FlexibleAdapter.OnItemClickListener, - FlexibleAdapter.OnItemLongClickListener, - FlexibleAdapter.OnItemMoveListener, - LibraryCategoryAdapter.LibraryListener{ + LibraryServiceListener { /** * Position of the active category. */ - var activeCategory: Int = preferences.lastUsedCategory().getOrDefault() - private set + protected var activeCategory: Int = preferences.lastUsedCategory().getOrDefault() /** * Action mode for selections. @@ -107,7 +85,7 @@ class LibraryController( /** * Library search query. */ - private var query = "" + protected var query = "" /** * Currently selected mangas. @@ -154,16 +132,11 @@ class LibraryController( * Adapter of the view pager. */ private var pagerAdapter: LibraryAdapter? = null - private lateinit var adapter: LibraryCategoryAdapter - - private lateinit var spinner: Spinner - - private var lastClickPosition = -1 /** * Drawer listener to allow swipe only for closing the drawer. */ - private var tabsVisibilityRelay: BehaviorRelay = BehaviorRelay.create(false) + protected var tabsVisibilityRelay: BehaviorRelay = BehaviorRelay.create(false) private var tabsVisibilitySubscription: Subscription? = null @@ -171,56 +144,24 @@ class LibraryController( var snack: Snackbar? = null - var presenter = LibraryPresenter(this) + lateinit var presenter:LibraryPresenter private set - private var justStarted = true + protected var justStarted = true - private var updateScroll = true - - private var spinnerAdapter: SpinnerAdapter? = null - - private var scrollListener = object : RecyclerView.OnScrollListener () { - override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { - super.onScrolled(recyclerView, dx, dy) - val position = - (recycler.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition() - val order = when (val item = adapter.getItem(position)) { - is LibraryHeaderItem -> item.category.order - is LibraryItem -> presenter.categories.find { it.id == item.manga.category }?.order - else -> null - } - if (order != null && order != activeCategory) { - preferences.lastUsedCategory().set(order) - activeCategory = order - val category = presenter.categories.find { it.order == order } - - bottom_sheet.lastCategory = category - bottom_sheet.updateTitle() - if (spinner.selectedItemPosition != order + 1) { - updateScroll = true - spinner.setSelection(order + 1, true) - } - } - } - } - - /** - * Recycler view of the list of manga. - */ - private lateinit var recycler: RecyclerView - - var libraryLayout = preferences.libraryLayout().getOrDefault() + var libraryLayout:Int = preferences.libraryLayout().getOrDefault() private var usePager: Boolean = !preferences.libraryAsSingleList().getOrDefault() + open fun contentView():View = pager_layout + init { setHasOptionsMenu(true) retainViewMode = RetainViewMode.RETAIN_DETACH } override fun getTitle(): String? { - return if (usePager) resources?.getString(R.string.label_library) else null + return resources?.getString(R.string.label_library) } override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { @@ -230,64 +171,17 @@ class LibraryController( override fun onViewCreated(view: View) { super.onViewCreated(view) mangaPerRow = getColumnsPreferenceForCurrentOrientation().getOrDefault() + if (!::presenter.isInitialized) + presenter = LibraryPresenter(this) - if (usePager) { - pager_layout.visible() - fast_scroller.gone() - pagerAdapter = LibraryAdapter(this) - library_pager.adapter = pagerAdapter - library_pager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener { - override fun onPageSelected(position: Int) { - preferences.lastUsedCategory().set(position) - activeCategory = position - bottom_sheet.lastCategory = pagerAdapter?.categories?.getOrNull(position) - if (preferences.librarySortingMode().getOrDefault() == LibrarySort.DRAG_AND_DROP) bottom_sheet.updateTitle() - } - - override fun onPageScrolled( - position: Int, positionOffset: Float, positionOffsetPixels: Int - ) { - } - - override fun onPageScrollStateChanged(state: Int) {} - }) - } - else { - adapter = LibraryCategoryAdapter(this) - recycler = (recycler_layout.inflate(R.layout.library_grid_recycler) as - AutofitRecyclerView).apply { - spanCount = if (libraryLayout == 0) 1 else mangaPerRow - manager.spanSizeLookup = (object : GridLayoutManager.SpanSizeLookup() { - override fun getSpanSize(position: Int): Int { - if (libraryLayout == 0) return 1 - val item = this@LibraryController.adapter.getItem(position) - return if (item is LibraryHeaderItem) manager.spanCount else 1 - } - }) - } - - recycler.setHasFixedSize(true) - recycler.adapter = adapter - recycler_layout.addView(recycler) - adapter.fastScroller = fast_scroller - recycler.addOnScrollListener(scrollListener) - - spinner = ReSpinner(view.context) - (activity as MainActivity).supportActionBar?.customView = spinner - (activity as MainActivity).supportActionBar?.setDisplayShowCustomEnabled(true) - spinnerAdapter = SpinnerAdapter(view.context, R.layout.library_spinner_textview, - arrayOf(resources!!.getString(R.string.label_library))) - spinnerAdapter?.setDropDownViewResource(R.layout.library_spinner_entry_text) - spinner.adapter = spinnerAdapter - } - + layoutView(view) if (selectedMangas.isNotEmpty()) { createActionModeIfNeeded() } //bottom_sheet.onCreate(pager_layout) - bottom_sheet.onCreate(if (usePager) pager_layout else recycler_layout) + bottom_sheet.onCreate(contentView()) bottom_sheet.onGroupClicked = { when (it) { @@ -305,50 +199,53 @@ class LibraryController( router.pushController(DownloadController().withFadeTransaction()) } - val config = resources?.configuration - val phoneLandscape = (config?.orientation == Configuration.ORIENTATION_LANDSCAPE && - (config.screenLayout.and(Configuration.SCREENLAYOUT_SIZE_MASK)) < - Configuration.SCREENLAYOUT_SIZE_LARGE) - - // pad the recycler if the filter bottom sheet is visible - if (!usePager && !phoneLandscape) { - val height = view.context.resources.getDimensionPixelSize(R.dimen.rounder_radius) + 4.dpToPx - recycler.updatePaddingRelative(bottom = height) - } - if (presenter.isDownloading()) { fab.scaleY = 1f fab.scaleX = 1f fab.isClickable = true fab.isFocusable = true } + presenter.onRestore() val library = presenter.getAllManga() if (library != null) presenter.updateViewBlocking() //onNextLibraryUpdate(presenter.categories, library) else { - library_pager.alpha = 0f - recycler_layout.alpha = 0f + contentView().alpha = 0f presenter.getLibraryBlocking() } } + + open fun layoutView(view: View) { + pagerAdapter = LibraryAdapter(this) + library_pager.adapter = pagerAdapter + library_pager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener { + override fun onPageSelected(position: Int) { + preferences.lastUsedCategory().set(position) + activeCategory = position + bottom_sheet.lastCategory = pagerAdapter?.categories?.getOrNull(position) + if (preferences.librarySortingMode().getOrDefault() == LibrarySort.DRAG_AND_DROP) bottom_sheet.updateTitle() + } + + override fun onPageScrolled( + position: Int, positionOffset: Float, positionOffsetPixels: Int + ) { + } + + override fun onPageScrollStateChanged(state: Int) {} + }) + } + override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) { super.onChangeStarted(handler, type) if (type.isEnter) { - if (!usePager) - (activity as MainActivity).supportActionBar?.setDisplayShowCustomEnabled(true) - else + if (library_pager != null) activity?.tabs?.setupWithViewPager(library_pager) presenter.getLibrary() DownloadService.addListener(this) DownloadService.callListeners() LibraryUpdateService.setListener(this) } - else if (type == ControllerChangeType.PUSH_EXIT) { - (activity as MainActivity).toolbar.menu.findItem(R.id - .action_search)?.collapseActionView() - (activity as MainActivity).supportActionBar?.setDisplayShowCustomEnabled(false) - } } override fun onActivityResumed(activity: Activity) { @@ -365,7 +262,6 @@ class LibraryController( } override fun onDestroy() { - (activity as MainActivity).supportActionBar?.setDisplayShowCustomEnabled(false) presenter.onDestroy() super.onDestroy() } @@ -393,7 +289,7 @@ class LibraryController( } override fun onUpdateManga(manga: LibraryManga) { - presenter.updateManga(manga) + if (manga.id != null) presenter.updateManga(manga) } override fun onDetach(view: View) { @@ -424,56 +320,7 @@ class LibraryController( tabsVisibilitySubscription = null } - fun onNextLibraryUpdate(mangaMap: List, freshStart: Boolean = false) { - if (mangaMap.isNotEmpty()) { - empty_view.hide() - } else { - empty_view.show(R.drawable.ic_book_black_128dp, R.string.information_empty_library) - } - adapter.setItems(mangaMap) - - - spinner.onItemSelectedListener = null - spinnerAdapter = SpinnerAdapter(view!!.context, R.layout.library_spinner_textview, - presenter.categories.map { it.name }.toTypedArray()) - spinnerAdapter?.setDropDownViewResource(R.layout.library_spinner_entry_text) - spinner.adapter = spinnerAdapter - - - spinner.setSelection(min(presenter.categories.size - 1, activeCategory + 1)) - if (!freshStart) { - justStarted = false - if (recycler_layout.alpha == 0f) - recycler_layout.animate().alpha(1f).setDuration(500).start() - - - }else { - val position = if (freshStart) adapter.indexOf(activeCategory) else null - if (position != null) - (recycler.layoutManager as LinearLayoutManager) - .scrollToPositionWithOffset(position, (-30).dpToPx) - } - adapter.isLongPressDragEnabled = canDrag() - tabsVisibilityRelay.call(false) - - bottom_sheet.lastCategory = presenter.categories[clamp(activeCategory, - 0, - presenter.categories.size - 1)] - bottom_sheet.updateTitle() - updateScroll = false - spinner.onItemSelectedListener = IgnoreFirstSpinnerListener { pos -> - if (updateScroll) { - updateScroll = false - return@IgnoreFirstSpinnerListener - } - val headerPosition = adapter.indexOf(pos - 1) - if (headerPosition > -1) { - (recycler.layoutManager as LinearLayoutManager).scrollToPositionWithOffset( - headerPosition, (-30).dpToPx - ) - } - } - } + open fun onNextLibraryUpdate(mangaMap: List, freshStart: Boolean = false) { } fun onNextLibraryUpdate(categories: List, mangaMap: Map>, freshStart: Boolean = false) { @@ -523,8 +370,8 @@ class LibraryController( } else if (!freshStart) { justStarted = false - if (library_pager.alpha == 0f) - library_pager.animate().alpha(1f).setDuration(500).start() + if (pager_layout.alpha == 0f) + pager_layout.animate().alpha(1f).setDuration(500).start() } } @@ -563,10 +410,8 @@ class LibraryController( destroyActionModeIfNeeded() } - fun onCatSortChanged(id: Int? = null) { - val catId = - (if (usePager)(id ?: pagerAdapter?.categories?.getOrNull(library_pager.currentItem)?.id) - else (id ?: presenter.categories.find { it.order == activeCategory }?.id)) + open fun onCatSortChanged(id: Int? = null) { + val catId = (id ?: pagerAdapter?.categories?.getOrNull(library_pager.currentItem)?.id) ?: return presenter.requestCatSortUpdate(catId) } @@ -574,26 +419,15 @@ class LibraryController( /** * Reattaches the adapter to the view pager to recreate fragments */ - private fun reattachAdapter() { - if (usePager) { - val adapter = pagerAdapter ?: return + protected open fun reattachAdapter() { + val adapter = pagerAdapter ?: return - val position = library_pager.currentItem + val position = library_pager.currentItem - adapter.recycle = false - library_pager.adapter = adapter - library_pager.currentItem = position - adapter.recycle = true - } - else { - val position = - (recycler.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition() - libraryLayout = preferences.libraryLayout().getOrDefault() - recycler.adapter = adapter - (recycler as? AutofitRecyclerView)?.spanCount = if (libraryLayout == 0) 1 else mangaPerRow - - (recycler.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(position, 0) - } + adapter.recycle = false + library_pager.adapter = adapter + library_pager.currentItem = position + adapter.recycle = true } /** @@ -643,8 +477,6 @@ class LibraryController( setOnQueryTextChangeListener(searchView) { query = it ?: "" searchRelay.call(query) - adapter.setFilter(it) - adapter.performFilter() true } searchItem.fixExpand(onExpand = { invalidateMenuOnExpand() }) @@ -778,11 +610,6 @@ class LibraryController( override fun onDestroyActionMode(mode: ActionMode?) { // Clear all the manga selections and notify child views. selectedMangas.clear() - adapter.mode = SelectableAdapter.Mode.SINGLE - adapter.clearSelection() - adapter.notifyDataSetChanged() - lastClickPosition = -1 - adapter.isLongPressDragEnabled = canDrag() selectionRelay.call(LibrarySelectionEvent.Cleared()) actionMode = null } @@ -797,36 +624,14 @@ class LibraryController( * @param manga the manga whose selection has changed. * @param selected whether it's now selected or not. */ - fun setSelection(manga: Manga, selected: Boolean) { + open fun setSelection(manga: Manga, selected: Boolean) { if (selected) { if (selectedMangas.add(manga)) { - if (usePager) selectionRelay.call(LibrarySelectionEvent.Selected(manga)) - else { - val position = adapter.indexOf(manga) - if (adapter.mode != SelectableAdapter.Mode.MULTI) { - adapter.mode = SelectableAdapter.Mode.MULTI - } - launchUI { - delay(100) - adapter.isLongPressDragEnabled = false - } - adapter.toggleSelection(position) - (recycler.findViewHolderForItemId(manga.id!!) as? LibraryHolder)?.toggleActivation() - } + selectionRelay.call(LibrarySelectionEvent.Selected(manga)) } } else { if (selectedMangas.remove(manga)) { - if (usePager) selectionRelay.call(LibrarySelectionEvent.Unselected(manga)) - else { - val position = adapter.indexOf(manga) - lastClickPosition = -1 - if (selectedMangas.isEmpty()) { - adapter.mode = SelectableAdapter.Mode.SINGLE - adapter.isLongPressDragEnabled = canDrag() - } - adapter.toggleSelection(position) - (recycler.findViewHolderForItemId(manga.id!!) as? LibraryHolder)?.toggleActivation() - } + selectionRelay.call(LibrarySelectionEvent.Unselected(manga)) } } } @@ -878,16 +683,6 @@ class LibraryController( destroyActionModeIfNeeded() } - /// Method for single list - override fun startReading(position: Int) { - if (adapter.mode == SelectableAdapter.Mode.MULTI) { - toggleSelection(position) - return - } - val manga = (adapter.getItem(position) as? LibraryItem)?.manga ?: return - startReading(manga) - } - /// Method for the category view fun startReading(manga: Manga) { val activity = activity ?: return @@ -896,147 +691,4 @@ class LibraryController( destroyActionModeIfNeeded() startActivity(intent) } - - override fun canDrag(): Boolean { - val filterOff = preferences.filterCompleted().getOrDefault() + - preferences.filterTracked().getOrDefault() + - preferences.filterUnread().getOrDefault() + - preferences.filterMangaType().getOrDefault() + - preferences.filterCompleted().getOrDefault() == 0 && - !preferences.hideCategories().getOrDefault() - return filterOff && adapter.mode != SelectableAdapter.Mode.MULTI - } - - /** - * Called when a manga is clicked. - * - * @param position the position of the element clicked. - * @return true if the item should be selected, false otherwise. - */ - override fun onItemClick(view: View?, position: Int): Boolean { - // If the action mode is created and the position is valid, toggle the selection. - val item = adapter.getItem(position) as? LibraryItem ?: return false - return if (adapter.mode == SelectableAdapter.Mode.MULTI) { - lastClickPosition = position - toggleSelection(position) - true - } else { - openManga(item.manga, null) - false - } - } - - /** - * Called when a manga is long clicked. - * - * @param position the position of the element clicked. - */ - override fun onItemLongClick(position: Int) { - createActionModeIfNeeded() - when { - lastClickPosition == -1 -> setSelection(position) - lastClickPosition > position -> for (i in position until lastClickPosition) - setSelection(i) - lastClickPosition < position -> for (i in lastClickPosition + 1..position) - setSelection(i) - else -> setSelection(position) - } - lastClickPosition = position - } - - override fun onActionStateChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) { - val position = viewHolder?.adapterPosition ?: return - if (actionState == 2) onItemLongClick(position) - } - - /** - * Tells the presenter to toggle the selection for the given position. - * - * @param position the position to toggle. - */ - private fun toggleSelection(position: Int) { - val item = adapter.getItem(position) as? LibraryItem ?: return - - setSelection(item.manga, !adapter.isSelected(position)) - invalidateActionMode() - } - - - /** - * Tells the presenter to set the selection for the given position. - * - * @param position the position to toggle. - */ - private fun setSelection(position: Int) { - val item = adapter.getItem(position) as? LibraryItem ?: return - - setSelection(item.manga, true) - invalidateActionMode() - } - - override fun onItemMove(fromPosition: Int, toPosition: Int) { } - - override fun onItemReleased(position: Int) { - if (adapter.selectedItemCount > 0) return - val item = adapter.getItem(position) as? LibraryItem ?: return - val newHeader = adapter.getSectionHeader(position) as? LibraryHeaderItem - val libraryItems = adapter.getSectionItems(adapter.getSectionHeader(position)) - .filterIsInstance() - val mangaIds = libraryItems.mapNotNull { (it as? LibraryItem)?.manga?.id } - if (newHeader?.category?.id == item.manga.category) { - presenter.rearrangeCategory(item.manga.category, mangaIds) - } else { - if (newHeader?.category?.mangaSort == null) { - presenter.moveMangaToCategory(item, newHeader?.category?.id, mangaIds, true) - } else { - MaterialDialog(activity!!).message(R.string.switch_to_dnd) - .positiveButton(R.string.action_switch) { - presenter.moveMangaToCategory(item, newHeader.category.id, mangaIds, true) - }.negativeButton( - text = resources?.getString( - R.string.keep_current_sort, - resources!!.getString(newHeader.category.sortRes()).toLowerCase - (Locale.getDefault()) - ) - ) { - presenter.moveMangaToCategory( - item, newHeader.category.id, mangaIds, false - ) - } - .cancelOnTouchOutside(false) - .show() - } - } - } - - override fun shouldMoveItem(fromPosition: Int, toPosition: Int): Boolean { - if (adapter.selectedItemCount > 1) - return false - if (adapter.isSelected(fromPosition)) - toggleSelection(fromPosition) - return true - } - - override fun updateCategory(catId: Int): Boolean { - val category = (adapter.getItem(catId) as? LibraryHeaderItem)?.category ?: - return false - val inQueue = LibraryUpdateService.categoryInQueue(category.id) - snack?.dismiss() - snack = snackbar_layout.snack(resources!!.getString( - when { - inQueue -> R.string.category_already_in_queue - LibraryUpdateService.isRunning(view!!.context) -> - R.string.adding_category_to_queue - else -> R.string.updating_category_x - }, category.name)) - if (!inQueue) - LibraryUpdateService.start(view!!.context, category) - return true - - } - - override fun sortCategory(catId: Int, sortBy: Int): String { - presenter.sortCategory(catId, sortBy) - return "" - } } \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryHeaderItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryHeaderItem.kt index 75c2d5e892..dc90dcebbf 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryHeaderItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryHeaderItem.kt @@ -17,9 +17,9 @@ import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.models.Category import eu.kanade.tachiyomi.data.library.LibraryUpdateService import eu.kanade.tachiyomi.util.system.getResourceColor -import eu.kanade.tachiyomi.util.view.gone import eu.kanade.tachiyomi.util.view.invisible import eu.kanade.tachiyomi.util.view.visible +import kotlinx.android.synthetic.main.library_category_header_item.view.* class LibraryHeaderItem(val category: Category) : AbstractHeaderItem() { @@ -63,7 +63,7 @@ class LibraryHeaderItem(val category: Category) : AbstractHeaderItem { + catProgress.alpha = 1f updateButton.invisible() - catProgress.gone() } LibraryUpdateService.categoryInQueue(item.category.id) -> { - catProgress.visible() + catProgress.alpha = 1f updateButton.invisible() } else -> { - catProgress.gone() + catProgress.alpha = 0f updateButton.visible() } } @@ -108,16 +108,15 @@ class LibraryHeaderItem(val category: Category) : AbstractHeaderItem item.category.order + is LibraryItem -> presenter.categories.find { it.id == item.manga.category }?.order + else -> null + } + if (order != null && order != activeCategory) { + preferences.lastUsedCategory().set(order) + activeCategory = order + val category = presenter.categories.find { it.order == order } + + bottom_sheet.lastCategory = category + bottom_sheet.updateTitle() + if (spinner.selectedItemPosition != order + 1) { + updateScroll = true + spinner.setSelection(order + 1, true) + } + } + } + } + + override fun onViewCreated(view: View) { + super.onViewCreated(view) + val config = resources?.configuration + val phoneLandscape = (config?.orientation == Configuration.ORIENTATION_LANDSCAPE && + (config.screenLayout.and(Configuration.SCREENLAYOUT_SIZE_MASK)) < + Configuration.SCREENLAYOUT_SIZE_LARGE) + + // pad the recycler if the filter bottom sheet is visible + if (!phoneLandscape) { + val height = view.context.resources.getDimensionPixelSize(R.dimen.rounder_radius) + 4.dpToPx + recycler.updatePaddingRelative(bottom = height) + } + } + + override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { + return inflater.inflate(R.layout.library_list_controller, container, false) + } + + + override fun layoutView(view: View) { + adapter = LibraryCategoryAdapter(this) + recycler = + (recycler_layout.inflate(R.layout.library_grid_recycler) as AutofitRecyclerView).apply { + spanCount = if (libraryLayout == 0) 1 else mangaPerRow + manager.spanSizeLookup = (object : GridLayoutManager.SpanSizeLookup() { + override fun getSpanSize(position: Int): Int { + if (libraryLayout == 0) return 1 + val item = this@LibraryListController.adapter.getItem(position) + return if (item is LibraryHeaderItem) manager.spanCount else 1 + } + }) + } + + recycler.setHasFixedSize(true) + recycler.adapter = adapter + recycler_layout.addView(recycler, 0) + adapter.fastScroller = fast_scroller + recycler.addOnScrollListener(scrollListener) + + spinner = ReSpinner(view.context) + + val tv = TypedValue() + activity!!.theme.resolveAttribute(R.attr.actionBarTintColor, tv, true) + + spinner.backgroundTintList = ContextCompat.getColorStateList( + view.context, tv.resourceId + ) + (activity as MainActivity).supportActionBar?.customView = spinner + (activity as MainActivity).supportActionBar?.setDisplayShowCustomEnabled(true) + spinnerAdapter = SpinnerAdapter( + view.context, + R.layout.library_spinner_textview, + arrayOf(resources!!.getString(R.string.label_library)) + ) + spinnerAdapter?.setDropDownViewResource(R.layout.library_spinner_entry_text) + spinner.adapter = spinnerAdapter + } + + override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) { + super.onChangeStarted(handler, type) + if (type.isEnter) { + (activity as MainActivity).supportActionBar?.setDisplayShowCustomEnabled(true) + } + else if (type == ControllerChangeType.PUSH_EXIT) { + (activity as MainActivity).toolbar.menu.findItem(R.id + .action_search)?.collapseActionView() + (activity as MainActivity).supportActionBar?.setDisplayShowCustomEnabled(false) + } + } + + override fun onDestroy() { + (activity as MainActivity).supportActionBar?.setDisplayShowCustomEnabled(false) + super.onDestroy() + } + + override fun onNextLibraryUpdate(mangaMap: List, freshStart: Boolean) { + if (mangaMap.isNotEmpty()) { + empty_view?.hide() + } else { + empty_view?.show(R.drawable.ic_book_black_128dp, R.string.information_empty_library) + } + adapter.setItems(mangaMap) + + + spinner.onItemSelectedListener = null + spinnerAdapter = SpinnerAdapter(view!!.context, R.layout.library_spinner_textview, + presenter.categories.map { it.name }.toTypedArray()) + spinnerAdapter?.setDropDownViewResource(R.layout.library_spinner_entry_text) + spinner.adapter = spinnerAdapter + + + spinner.setSelection(min(presenter.categories.size - 1, activeCategory + 1)) + if (!freshStart) { + justStarted = false + if (recycler_layout.alpha == 0f) + recycler_layout.animate().alpha(1f).setDuration(500).start() + + + }else { + val position = if (freshStart) adapter.indexOf(activeCategory) else null + if (position != null) + (recycler.layoutManager as LinearLayoutManager) + .scrollToPositionWithOffset(position, (-30).dpToPx) + } + adapter.isLongPressDragEnabled = canDrag() + tabsVisibilityRelay.call(false) + + bottom_sheet.lastCategory = presenter.categories[MathUtils.clamp( + activeCategory, 0, presenter.categories.size - 1 + )] + bottom_sheet.updateTitle() + updateScroll = false + spinner.onItemSelectedListener = IgnoreFirstSpinnerListener { pos -> + if (updateScroll) { + updateScroll = false + return@IgnoreFirstSpinnerListener + } + val headerPosition = adapter.indexOf(pos - 1) + if (headerPosition > -1) { + (recycler.layoutManager as LinearLayoutManager).scrollToPositionWithOffset( + headerPosition, (-30).dpToPx + ) + } + } + } + + override fun reattachAdapter() { + val position = + (recycler.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition() + libraryLayout = preferences.libraryLayout().getOrDefault() + recycler.adapter = adapter + (recycler as? AutofitRecyclerView)?.spanCount = if (libraryLayout == 0) 1 else mangaPerRow + + (recycler.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(position, 0) + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + super.onCreateOptionsMenu(menu, inflater) + val searchItem = menu.findItem(R.id.action_search) + val searchView = searchItem.actionView as SearchView + setOnQueryTextChangeListener(searchView) { + query = it ?: "" + adapter.setFilter(it) + adapter.performFilter() + true + } + } + + override fun onCatSortChanged(id: Int?) { + val catId = (id ?: presenter.categories.find { it.order == activeCategory }?.id) + ?: return + presenter.requestCatSortUpdate(catId) + } + + override fun onDestroyActionMode(mode: ActionMode?) { + super.onDestroyActionMode(mode) + adapter.mode = SelectableAdapter.Mode.SINGLE + adapter.clearSelection() + adapter.notifyDataSetChanged() + lastClickPosition = -1 + adapter.isLongPressDragEnabled = canDrag() + } + + override fun setSelection(manga: Manga, selected: Boolean) { + if (selected) { + if (selectedMangas.add(manga)) { + val position = adapter.indexOf(manga) + if (adapter.mode != SelectableAdapter.Mode.MULTI) { + adapter.mode = SelectableAdapter.Mode.MULTI + } + launchUI { + delay(100) + adapter.isLongPressDragEnabled = false + } + adapter.toggleSelection(position) + (recycler.findViewHolderForItemId(manga.id!!) as? LibraryHolder)?.toggleActivation() + } + } else { + if (selectedMangas.remove(manga)) { + val position = adapter.indexOf(manga) + lastClickPosition = -1 + if (selectedMangas.isEmpty()) { + adapter.mode = SelectableAdapter.Mode.SINGLE + adapter.isLongPressDragEnabled = canDrag() + } + adapter.toggleSelection(position) + (recycler.findViewHolderForItemId(manga.id!!) as? LibraryHolder)?.toggleActivation() + } + } + } + + /// Method for single list + override fun startReading(position: Int) { + if (adapter.mode == SelectableAdapter.Mode.MULTI) { + toggleSelection(position) + return + } + val manga = (adapter.getItem(position) as? LibraryItem)?.manga ?: return + startReading(manga) + } + + /** + * Tells the presenter to toggle the selection for the given position. + * + * @param position the position to toggle. + */ + private fun toggleSelection(position: Int) { + val item = adapter.getItem(position) as? LibraryItem ?: return + + setSelection(item.manga, !adapter.isSelected(position)) + invalidateActionMode() + } + + + override fun canDrag(): Boolean { + val filterOff = preferences.filterCompleted().getOrDefault() + + preferences.filterTracked().getOrDefault() + + preferences.filterUnread().getOrDefault() + + preferences.filterMangaType().getOrDefault() + + preferences.filterCompleted().getOrDefault() == 0 && + !preferences.hideCategories().getOrDefault() + return filterOff && adapter.mode != SelectableAdapter.Mode.MULTI + } + + /** + * Called when a manga is clicked. + * + * @param position the position of the element clicked. + * @return true if the item should be selected, false otherwise. + */ + override fun onItemClick(view: View?, position: Int): Boolean { + // If the action mode is created and the position is valid, toggle the selection. + val item = adapter.getItem(position) as? LibraryItem ?: return false + return if (adapter.mode == SelectableAdapter.Mode.MULTI) { + lastClickPosition = position + toggleSelection(position) + true + } else { + openManga(item.manga, null) + false + } + } + + /** + * Called when a manga is long clicked. + * + * @param position the position of the element clicked. + */ + override fun onItemLongClick(position: Int) { + createActionModeIfNeeded() + when { + lastClickPosition == -1 -> setSelection(position) + lastClickPosition > position -> for (i in position until lastClickPosition) + setSelection(i) + lastClickPosition < position -> for (i in lastClickPosition + 1..position) + setSelection(i) + else -> setSelection(position) + } + lastClickPosition = position + } + + override fun onActionStateChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) { + val position = viewHolder?.adapterPosition ?: return + if (actionState == 2) onItemLongClick(position) + } + + override fun onUpdateManga(manga: LibraryManga) { + if (manga.id == null) adapter.notifyDataSetChanged() + else super.onUpdateManga(manga) + } + + /** + * Tells the presenter to set the selection for the given position. + * + * @param position the position to toggle. + */ + private fun setSelection(position: Int) { + val item = adapter.getItem(position) as? LibraryItem ?: return + + setSelection(item.manga, true) + invalidateActionMode() + } + + override fun onItemMove(fromPosition: Int, toPosition: Int) { } + + override fun onItemReleased(position: Int) { + if (adapter.selectedItemCount > 0) return + val item = adapter.getItem(position) as? LibraryItem ?: return + val newHeader = adapter.getSectionHeader(position) as? LibraryHeaderItem + val libraryItems = adapter.getSectionItems(adapter.getSectionHeader(position)) + .filterIsInstance() + val mangaIds = libraryItems.mapNotNull { (it as? LibraryItem)?.manga?.id } + if (newHeader?.category?.id == item.manga.category) { + presenter.rearrangeCategory(item.manga.category, mangaIds) + } else { + if (newHeader?.category?.mangaSort == null) { + presenter.moveMangaToCategory(item, newHeader?.category?.id, mangaIds, true) + } else { + MaterialDialog(activity!!).message(R.string.switch_to_dnd) + .positiveButton(R.string.action_switch) { + presenter.moveMangaToCategory(item, newHeader.category.id, mangaIds, true) + }.negativeButton( + text = resources?.getString( + R.string.keep_current_sort, + resources!!.getString(newHeader.category.sortRes()).toLowerCase + (Locale.getDefault()) + ) + ) { + presenter.moveMangaToCategory( + item, newHeader.category.id, mangaIds, false + ) + } + .cancelOnTouchOutside(false) + .show() + } + } + } + + override fun shouldMoveItem(fromPosition: Int, toPosition: Int): Boolean { + if (adapter.selectedItemCount > 1) + return false + if (adapter.isSelected(fromPosition)) + toggleSelection(fromPosition) + return true + } + + override fun updateCategory(catId: Int): Boolean { + val category = (adapter.getItem(catId) as? LibraryHeaderItem)?.category ?: + return false + val inQueue = LibraryUpdateService.categoryInQueue(category.id) + snack?.dismiss() + snack = snackbar_layout.snack(resources!!.getString( + when { + inQueue -> R.string.category_already_in_queue + LibraryUpdateService.isRunning() -> + R.string.adding_category_to_queue + else -> R.string.updating_category_x + }, category.name)) + if (!inQueue) + LibraryUpdateService.start(view!!.context, category) + return true + } + + override fun sortCategory(catId: Int, sortBy: Int): String { + presenter.sortCategory(catId, sortBy) + return "" + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/filter/SortFilterBottomSheet.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/filter/SortFilterBottomSheet.kt index ba7ebee7b7..b2d4cc2f35 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/filter/SortFilterBottomSheet.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/filter/SortFilterBottomSheet.kt @@ -121,7 +121,7 @@ class SortFilterBottomSheet @JvmOverloads constructor(context: Context, attrs: A updateTitle() val shadow2:View = (pagerView.parent as ViewGroup).findViewById(R.id.shadow2) val shadow:View = (pagerView.parent as ViewGroup).findViewById(R.id.shadow) - val fastScroller:View = (pagerView.parent as ViewGroup).findViewById(R.id.fast_scroller) + val fastScroller:View? = (pagerView.parent as ViewGroup).findViewById(R.id.fast_scroller) val coordLayout:View = (pagerView.parent as ViewGroup).findViewById(R.id.snackbar_layout) val phoneLandscape = (isLandscape() && !isTablet()) if (phoneLandscape) @@ -167,7 +167,7 @@ class SortFilterBottomSheet @JvmOverloads constructor(context: Context, attrs: A } if (sheetBehavior?.state == BottomSheetBehavior.STATE_COLLAPSED) { val height = context.resources.getDimensionPixelSize(R.dimen.rounder_radius) - fastScroller.updateLayoutParams { + fastScroller?.updateLayoutParams { bottomMargin = if (phoneLandscape) 0 else (top_bar.height - height) } pager?.setPadding(0, 0, 0, if (phoneLandscape) 0 else diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt index ff4ba3237c..4e1cd7b2ff 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt @@ -47,6 +47,7 @@ import eu.kanade.tachiyomi.ui.catalogue.global_search.CatalogueSearchController import eu.kanade.tachiyomi.ui.download.DownloadController import eu.kanade.tachiyomi.ui.extension.ExtensionController import eu.kanade.tachiyomi.ui.library.LibraryController +import eu.kanade.tachiyomi.ui.library.LibraryListController import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.recent_updates.RecentChaptersController import eu.kanade.tachiyomi.ui.recently_read.RecentlyReadController @@ -131,7 +132,11 @@ open class MainActivity : BaseActivity(), DownloadServiceListener { val currentRoot = router.backstack.firstOrNull() if (currentRoot?.tag()?.toIntOrNull() != id) { when (id) { - R.id.nav_library -> setRoot(LibraryController(), id) + R.id.nav_library -> setRoot( + if (preferences.libraryAsSingleList().getOrDefault()) + LibraryListController() + else + LibraryController(), id) R.id.nav_recents -> { if (preferences.showRecentUpdates().getOrDefault()) setRoot(RecentChaptersController(), id) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/recent_updates/RecentChaptersController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/recent_updates/RecentChaptersController.kt index 298ed09f64..e146b7e8fb 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/recent_updates/RecentChaptersController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/recent_updates/RecentChaptersController.kt @@ -26,10 +26,10 @@ import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.ui.main.MainActivity import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.reader.ReaderActivity +import eu.kanade.tachiyomi.ui.recently_read.RecentlyReadController import eu.kanade.tachiyomi.util.system.notificationManager import eu.kanade.tachiyomi.util.view.RecyclerWindowInsetsListener import eu.kanade.tachiyomi.util.view.snack -import eu.kanade.tachiyomi.ui.recently_read.RecentlyReadController import kotlinx.android.synthetic.main.recent_chapters_controller.* import timber.log.Timber import uy.kohesive.injekt.Injekt @@ -98,7 +98,7 @@ class RecentChaptersController : NucleusController(), swipe_refresh.setDistanceToTriggerSync((2 * 64 * view.resources.displayMetrics.density).toInt()) swipe_refresh.refreshes().subscribeUntilDestroy { - if (!LibraryUpdateService.isRunning(view.context)) { + if (!LibraryUpdateService.isRunning()) { LibraryUpdateService.start(view.context) view.snack(R.string.updating_library) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt index d476f18a0c..b03823115f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt @@ -2,8 +2,8 @@ package eu.kanade.tachiyomi.ui.setting import android.app.Dialog import android.os.Bundle -import androidx.preference.PreferenceScreen import android.widget.Toast +import androidx.preference.PreferenceScreen import com.afollestad.materialdialogs.MaterialDialog import com.bluelinelabs.conductor.RouterTransaction import com.bluelinelabs.conductor.changehandler.FadeChangeHandler @@ -13,17 +13,19 @@ import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.data.library.LibraryUpdateService import eu.kanade.tachiyomi.data.library.LibraryUpdateService.Target +import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.ui.base.controller.DialogController import eu.kanade.tachiyomi.ui.library.LibraryController +import eu.kanade.tachiyomi.ui.library.LibraryListController import eu.kanade.tachiyomi.util.system.launchUI +import eu.kanade.tachiyomi.util.system.toast import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.Job import kotlinx.coroutines.launch -import eu.kanade.tachiyomi.util.system.toast import rx.Observable import rx.android.schedulers.AndroidSchedulers import rx.schedulers.Schedulers @@ -154,7 +156,11 @@ class SettingsAdvancedController : SettingsController() { private fun clearDatabase() { // Avoid weird behavior by going back to the library. - val newBackstack = listOf(RouterTransaction.with(LibraryController())) + + val newBackstack = listOf(RouterTransaction.with( + if (preferences.libraryAsSingleList().getOrDefault()) + LibraryListController() + else + LibraryController())) + router.backstack.drop(1) router.setBackstack(newBackstack, FadeChangeHandler()) diff --git a/app/src/main/res/layout/library_category_header_item.xml b/app/src/main/res/layout/library_category_header_item.xml index 8b844c138c..76790bd747 100644 --- a/app/src/main/res/layout/library_category_header_item.xml +++ b/app/src/main/res/layout/library_category_header_item.xml @@ -16,7 +16,6 @@ android:gravity="center|start" android:inputType="none" android:maxLines="1" - android:textColor="?attr/actionBarTintColor" app:layout_constraintBottom_toBottomOf="@id/update_button" app:layout_constraintStart_toStartOf="parent" app:layout_constraintWidth_default="wrap" @@ -32,6 +31,7 @@ android:background="@drawable/square_ripple" android:clickable="true" android:layout_marginEnd="10dp" + android:drawableTint="@color/gray_button" android:drawableEnd="@drawable/ic_sort_white_24dp" android:drawablePadding="6dp" android:focusable="true" @@ -65,10 +65,15 @@ + app:layout_constraintHorizontal_bias="0.0" + app:layout_constraintBottom_toBottomOf="@+id/update_button" + app:layout_constraintStart_toStartOf="@+id/update_button" + app:layout_constraintEnd_toEndOf="@id/update_button" + app:layout_constraintTop_toTopOf="@+id/update_button" /> \ No newline at end of file diff --git a/app/src/main/res/layout/library_controller.xml b/app/src/main/res/layout/library_controller.xml index ada64bc2eb..25c689ea29 100644 --- a/app/src/main/res/layout/library_controller.xml +++ b/app/src/main/res/layout/library_controller.xml @@ -6,16 +6,8 @@ android:id="@+id/library_layout" android:layout_height="match_parent"> - - - - @@ -25,15 +17,6 @@ android:layout_height="match_parent" /> - - + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/library_spinner_textview.xml b/app/src/main/res/layout/library_spinner_textview.xml index b93f486b5e..fd4fd544f4 100644 --- a/app/src/main/res/layout/library_spinner_textview.xml +++ b/app/src/main/res/layout/library_spinner_textview.xml @@ -7,5 +7,6 @@ android:layout_height="wrap_content" android:gravity="start" android:textSize="20sp" + android:textColor="?attr/actionBarTintColor" tools:text="Title" tools:background="?attr/colorPrimary"/> \ No newline at end of file diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 375dc54412..3004ed6801 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -21,7 +21,7 @@ - +