mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-12-24 03:41:54 +01:00
Fixed Update notifcation + Moved single list library to extended class
Fixing progress bar for category header and stop spinning after update
This commit is contained in:
parent
9768b5ae25
commit
49d95e2dd0
@ -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<LibraryManga>()
|
||||
|
||||
private val categoryIds = mutableSetOf<Int>()
|
||||
|
||||
// List containing new updates
|
||||
private val newUpdates = ArrayList<Pair<LibraryManga, Array<Chapter>>>()
|
||||
|
||||
/**
|
||||
* Cached progress notification to avoid creating a lot.
|
||||
*/
|
||||
@ -127,11 +135,8 @@ class LibraryUpdateService(
|
||||
*/
|
||||
const val KEY_CATEGORY = "category"
|
||||
|
||||
private val mangaToUpdate = mutableListOf<LibraryManga>()
|
||||
|
||||
private val categoryIds = mutableSetOf<Int>()
|
||||
|
||||
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<LibraryManga>) {
|
||||
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<LibraryManga> {
|
||||
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<LibraryManga> {
|
||||
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<LibraryManga>) {
|
||||
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<LibraryManga> {
|
||||
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<LibraryManga> {
|
||||
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<LibraryManga>, 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<Pair<LibraryManga, Array<Chapter>>>()
|
||||
private suspend fun updateChaptersJob(mangaToAdd: List<LibraryManga>) {
|
||||
// list containing failed updates
|
||||
val failedUpdates = ArrayList<Manga>()
|
||||
// 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<SChapter>()
|
||||
}
|
||||
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<SChapter>()
|
||||
} ?: 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()) {
|
||||
|
@ -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)
|
||||
|
@ -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<Boolean> = BehaviorRelay.create(false)
|
||||
protected var tabsVisibilityRelay: BehaviorRelay<Boolean> = 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<LibraryItem>, 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<LibraryItem>, freshStart: Boolean = false) { }
|
||||
|
||||
fun onNextLibraryUpdate(categories: List<Category>, mangaMap: Map<Int, List<LibraryItem>>,
|
||||
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<LibraryItem>()
|
||||
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 ""
|
||||
}
|
||||
}
|
@ -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<LibraryHeaderItem.Holder>() {
|
||||
|
||||
@ -63,7 +63,7 @@ class LibraryHeaderItem(val category: Category) : AbstractHeaderItem<LibraryHead
|
||||
return -(category.id!!)
|
||||
}
|
||||
|
||||
class Holder(view: View, private val adapter: LibraryCategoryAdapter) :
|
||||
class Holder(val view: View, private val adapter: LibraryCategoryAdapter) :
|
||||
FlexibleViewHolder(view, adapter, true) {
|
||||
|
||||
private val sectionText: TextView = view.findViewById(R.id.category_title)
|
||||
@ -92,15 +92,15 @@ class LibraryHeaderItem(val category: Category) : AbstractHeaderItem<LibraryHead
|
||||
|
||||
when {
|
||||
item.category.id == -1 -> {
|
||||
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<LibraryHead
|
||||
|
||||
private fun addCategoryToUpdate() {
|
||||
if (adapter.libraryListener.updateCategory(adapterPosition)) {
|
||||
catProgress.visible()
|
||||
catProgress.alpha = 1f
|
||||
updateButton.invisible()
|
||||
}
|
||||
}
|
||||
|
||||
private fun showCatSortOptions() {
|
||||
val category =
|
||||
(adapter.getItem(adapterPosition) as? LibraryHeaderItem)?.category ?: return
|
||||
// Create a PopupMenu, giving it the clicked view for an anchor
|
||||
val popup = PopupMenu(itemView.context, sortText)
|
||||
val popup = PopupMenu(itemView.context, view.category_sort)
|
||||
|
||||
// Inflate our menu resource into the PopupMenu's Menu
|
||||
popup.menuInflater.inflate(
|
||||
|
@ -0,0 +1,444 @@
|
||||
package eu.kanade.tachiyomi.ui.library
|
||||
|
||||
import android.content.res.Configuration
|
||||
import android.os.Bundle
|
||||
import android.util.TypedValue
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Spinner
|
||||
import androidx.appcompat.view.ActionMode
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.math.MathUtils
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler
|
||||
import com.bluelinelabs.conductor.ControllerChangeType
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.SelectableAdapter
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.LibraryManga
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateService
|
||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
import eu.kanade.tachiyomi.util.system.dpToPx
|
||||
import eu.kanade.tachiyomi.util.system.launchUI
|
||||
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.widget.AutofitRecyclerView
|
||||
import eu.kanade.tachiyomi.widget.IgnoreFirstSpinnerListener
|
||||
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.coroutines.delay
|
||||
import java.util.Locale
|
||||
import kotlin.math.min
|
||||
|
||||
class LibraryListController(bundle: Bundle? = null) : LibraryController(bundle),
|
||||
FlexibleAdapter.OnItemClickListener,
|
||||
FlexibleAdapter.OnItemLongClickListener,
|
||||
FlexibleAdapter.OnItemMoveListener,
|
||||
LibraryCategoryAdapter.LibraryListener{
|
||||
|
||||
private lateinit var adapter: LibraryCategoryAdapter
|
||||
|
||||
private lateinit var spinner: Spinner
|
||||
|
||||
private var lastClickPosition = -1
|
||||
|
||||
private var updateScroll = true
|
||||
|
||||
private var spinnerAdapter: SpinnerAdapter? = null
|
||||
|
||||
/**
|
||||
* Recycler view of the list of manga.
|
||||
*/
|
||||
private lateinit var recycler: RecyclerView
|
||||
|
||||
override fun contentView():View = recycler_layout
|
||||
|
||||
override fun getTitle(): String? {
|
||||
return 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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<LibraryItem>, 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<LibraryItem>()
|
||||
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 ""
|
||||
}
|
||||
}
|
@ -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<CoordinatorLayout.LayoutParams> {
|
||||
fastScroller?.updateLayoutParams<CoordinatorLayout.LayoutParams> {
|
||||
bottomMargin = if (phoneLandscape) 0 else (top_bar.height - height)
|
||||
}
|
||||
pager?.setPadding(0, 0, 0, if (phoneLandscape) 0 else
|
||||
|
@ -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)
|
||||
|
@ -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<RecentChaptersPresenter>(),
|
||||
|
||||
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)
|
||||
}
|
||||
|
@ -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())
|
||||
|
@ -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 @@
|
||||
<ProgressBar
|
||||
android:id="@+id/cat_progress"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
android:indeterminate="true"
|
||||
android:layout_height="30dp"
|
||||
android:alpha="0.0"
|
||||
tools:alpha="1.0"
|
||||
android:layout_marginStart="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/category_title"
|
||||
app:layout_constraintStart_toEndOf="@+id/category_title"
|
||||
app:layout_constraintTop_toTopOf="@+id/category_title" />
|
||||
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" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -6,16 +6,8 @@
|
||||
android:id="@+id/library_layout"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/recycler_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:id="@+id/pager_layout"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
@ -25,15 +17,6 @@
|
||||
android:layout_height="match_parent" />
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
<eu.davidea.fastscroller.FastScroller
|
||||
android:id="@+id/fast_scroller"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="end"
|
||||
app:fastScrollerIgnoreTouchesOutsideHandle="true"
|
||||
app:fastScrollerBubbleEnabled="false"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:id="@+id/snackbar_layout"
|
||||
android:layout_width="match_parent"
|
||||
|
69
app/src/main/res/layout/library_list_controller.xml
Normal file
69
app/src/main/res/layout/library_list_controller.xml
Normal file
@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/library_layout"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/recycler_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<eu.davidea.fastscroller.FastScroller
|
||||
android:id="@+id/fast_scroller"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="end"
|
||||
app:fastScrollerIgnoreTouchesOutsideHandle="true"
|
||||
app:fastScrollerBubbleEnabled="false"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:id="@+id/snackbar_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
|
||||
<eu.kanade.tachiyomi.widget.EmptyView
|
||||
android:id="@+id/empty_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone" />
|
||||
|
||||
<View
|
||||
android:id="@+id/shadow"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="24dp"
|
||||
android:alpha="0.5"
|
||||
android:background="@drawable/shape_gradient_top_shadow"
|
||||
android:paddingBottom="10dp"
|
||||
app:layout_anchorGravity="top"
|
||||
app:layout_anchor="@id/bottom_sheet" />
|
||||
|
||||
<!-- Adding bottom sheet after main content -->
|
||||
<include layout="@layout/filter_bottom_sheet"/>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fab"
|
||||
style="@style/Theme.Widget.FABFixed"
|
||||
android:layout_gravity="center"
|
||||
android:clickable="false"
|
||||
android:focusable="false"
|
||||
android:scaleX="0"
|
||||
android:scaleY="0"
|
||||
app:layout_anchor="@id/bottom_sheet"
|
||||
app:layout_anchorGravity="end|top"
|
||||
app:srcCompat="@drawable/ic_file_download_white_24dp" />
|
||||
|
||||
<View
|
||||
android:id="@+id/shadow2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="8dp"
|
||||
android:layout_gravity="bottom"
|
||||
android:alpha="0.25"
|
||||
android:background="@drawable/shape_gradient_top_shadow" />
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
@ -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"/>
|
@ -21,7 +21,7 @@
|
||||
</declare-styleable>
|
||||
|
||||
<attr name="navigation_view_theme" format="reference"/>
|
||||
<attr name="actionBarTintColor" format="reference|integer"/>
|
||||
<attr name="actionBarTintColor" format="color"/>
|
||||
<attr name="tabBarIconColor" format="reference|integer"/>
|
||||
<attr name="tabBarIconInactive" format="reference|integer"/>
|
||||
<attr name="selectable_list_drawable" format="reference|integer" />
|
||||
|
Loading…
Reference in New Issue
Block a user