mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-11-10 05:25:04 +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.ui.main.MainActivity
|
||||||
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
|
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
|
||||||
import eu.kanade.tachiyomi.util.lang.chop
|
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.notification
|
||||||
import eu.kanade.tachiyomi.util.system.notificationManager
|
import eu.kanade.tachiyomi.util.system.notificationManager
|
||||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import rx.Subscription
|
import rx.Subscription
|
||||||
import rx.schedulers.Schedulers
|
import rx.schedulers.Schedulers
|
||||||
@ -98,6 +99,13 @@ class LibraryUpdateService(
|
|||||||
|
|
||||||
private var job:Job? = null
|
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.
|
* Cached progress notification to avoid creating a lot.
|
||||||
*/
|
*/
|
||||||
@ -127,11 +135,8 @@ class LibraryUpdateService(
|
|||||||
*/
|
*/
|
||||||
const val KEY_CATEGORY = "category"
|
const val KEY_CATEGORY = "category"
|
||||||
|
|
||||||
private val mangaToUpdate = mutableListOf<LibraryManga>()
|
fun categoryInQueue(id: Int?) = instance?.categoryIds?.contains(id) ?: false
|
||||||
|
private var instance: LibraryUpdateService? = null
|
||||||
private val categoryIds = mutableSetOf<Int>()
|
|
||||||
|
|
||||||
fun categoryInQueue(id: Int?) = categoryIds.contains(id)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Key that defines what should be updated.
|
* Key that defines what should be updated.
|
||||||
@ -141,11 +146,10 @@ class LibraryUpdateService(
|
|||||||
/**
|
/**
|
||||||
* Returns the status of the service.
|
* Returns the status of the service.
|
||||||
*
|
*
|
||||||
* @param context the application context.
|
|
||||||
* @return true if the service is running, false otherwise.
|
* @return true if the service is running, false otherwise.
|
||||||
*/
|
*/
|
||||||
fun isRunning(context: Context): Boolean {
|
fun isRunning(): Boolean {
|
||||||
return context.isServiceRunning(LibraryUpdateService::class.java)
|
return instance != null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -157,12 +161,11 @@ class LibraryUpdateService(
|
|||||||
* @param target defines what should be updated.
|
* @param target defines what should be updated.
|
||||||
*/
|
*/
|
||||||
fun start(context: Context, category: Category? = null, target: Target = Target.CHAPTERS) {
|
fun start(context: Context, category: Category? = null, target: Target = Target.CHAPTERS) {
|
||||||
if (!isRunning(context)) {
|
if (!isRunning()) {
|
||||||
val intent = Intent(context, LibraryUpdateService::class.java).apply {
|
val intent = Intent(context, LibraryUpdateService::class.java).apply {
|
||||||
putExtra(KEY_TARGET, target)
|
putExtra(KEY_TARGET, target)
|
||||||
category?.id?.let { id ->
|
category?.id?.let { id ->
|
||||||
putExtra(KEY_CATEGORY, id)
|
putExtra(KEY_CATEGORY, id)
|
||||||
categoryIds.add(id)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||||
@ -173,22 +176,11 @@ class LibraryUpdateService(
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (target == Target.CHAPTERS) category?.id?.let {
|
if (target == Target.CHAPTERS) category?.id?.let {
|
||||||
categoryIds.add(it)
|
instance?.addCategory(it)
|
||||||
val preferences: PreferencesHelper = Injekt.get()
|
|
||||||
val selectedScheme = preferences.libraryUpdatePrioritization().getOrDefault()
|
|
||||||
addManga(getMangaToUpdate(it, target).sortedWith(
|
|
||||||
rankingScheme[selectedScheme]
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addManga(mangaToAdd: List<LibraryManga>) {
|
|
||||||
for (manga in mangaToAdd) {
|
|
||||||
if (mangaToUpdate.none { it.id == manga.id }) mangaToUpdate.add(manga)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stops the service.
|
* Stops the service.
|
||||||
*
|
*
|
||||||
@ -198,40 +190,6 @@ class LibraryUpdateService(
|
|||||||
context.stopService(Intent(context, LibraryUpdateService::class.java))
|
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
|
private var listener:LibraryServiceListener? = null
|
||||||
|
|
||||||
fun setListener(listener: LibraryServiceListener) {
|
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
|
* Method called when the service is created. It injects dagger dependencies and acquire
|
||||||
* the wake lock.
|
* the wake lock.
|
||||||
@ -255,22 +262,19 @@ class LibraryUpdateService(
|
|||||||
wakeLock.acquire(TimeUnit.MINUTES.toMillis(30))
|
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
|
* Method called when the service is destroyed. It destroys subscriptions and releases the wake
|
||||||
* lock.
|
* lock.
|
||||||
*/
|
*/
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
|
job?.cancel()
|
||||||
|
if (instance == this)
|
||||||
|
instance = null
|
||||||
subscription?.unsubscribe()
|
subscription?.unsubscribe()
|
||||||
mangaToUpdate.clear()
|
|
||||||
categoryIds.clear()
|
|
||||||
if (wakeLock.isHeld) {
|
if (wakeLock.isHeld) {
|
||||||
wakeLock.release()
|
wakeLock.release()
|
||||||
}
|
}
|
||||||
|
listener?.onUpdateManga(LibraryManga())
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,23 +295,22 @@ class LibraryUpdateService(
|
|||||||
*/
|
*/
|
||||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
if (intent == null) return START_NOT_STICKY
|
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.
|
// Unsubscribe from any previous subscription if needed.
|
||||||
job?.cancel()
|
|
||||||
subscription?.unsubscribe()
|
subscription?.unsubscribe()
|
||||||
|
instance = this
|
||||||
|
|
||||||
val selectedScheme = preferences.libraryUpdatePrioritization().getOrDefault()
|
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) {
|
if (target == Target.CHAPTERS) {
|
||||||
updateChapters(
|
updateChapters(mangaList, startId)
|
||||||
getMangaToUpdate(intent, target).sortedWith(rankingScheme[selectedScheme]), startId
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Update either chapter list or manga details.
|
// 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 {
|
subscription = Observable.defer {
|
||||||
when (target) {
|
when (target) {
|
||||||
Target.DETAILS -> updateDetails(mangaList)
|
Target.DETAILS -> updateDetails(mangaList)
|
||||||
@ -324,25 +327,26 @@ class LibraryUpdateService(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun updateChapters(mangaToAdd: List<LibraryManga>, startId: Int) {
|
private fun updateChapters(mangaToAdd: List<LibraryManga>, startId: Int) {
|
||||||
addManga(mangaToAdd)
|
|
||||||
|
|
||||||
val handler = CoroutineExceptionHandler { _, exception ->
|
val handler = CoroutineExceptionHandler { _, exception ->
|
||||||
Timber.e(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)
|
stopSelf(startId)
|
||||||
}
|
}
|
||||||
job = GlobalScope.launch(handler) {
|
job = GlobalScope.launch(handler) {
|
||||||
updateChaptersJob()
|
updateChaptersJob(mangaToAdd)
|
||||||
mangaToUpdate.clear()
|
|
||||||
categoryIds.clear()
|
|
||||||
stopSelf(startId)
|
|
||||||
}
|
}
|
||||||
|
job?.invokeOnCompletion { stopSelf(startId) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateChaptersJob() {
|
private suspend fun updateChaptersJob(mangaToAdd: List<LibraryManga>) {
|
||||||
// Initialize the variables holding the progress of the updates.
|
|
||||||
var count = 0
|
|
||||||
// List containing new updates
|
|
||||||
val newUpdates = ArrayList<Pair<LibraryManga, Array<Chapter>>>()
|
|
||||||
// list containing failed updates
|
// list containing failed updates
|
||||||
val failedUpdates = ArrayList<Manga>()
|
val failedUpdates = ArrayList<Manga>()
|
||||||
// List containing categories that get included in downloads.
|
// List containing categories that get included in downloads.
|
||||||
@ -351,28 +355,37 @@ class LibraryUpdateService(
|
|||||||
val downloadNew = preferences.downloadNew().getOrDefault()
|
val downloadNew = preferences.downloadNew().getOrDefault()
|
||||||
// Boolean to determine if DownloadManager has downloads
|
// Boolean to determine if DownloadManager has downloads
|
||||||
var hasDownloads = false
|
var hasDownloads = false
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
// Initialize the variables holding the progress of the updates.
|
||||||
|
var count = 0
|
||||||
|
|
||||||
while (count < mangaToUpdate.size) {
|
mangaToUpdate.addAll(mangaToAdd)
|
||||||
if (job?.isCancelled == true || job == null) break
|
while (count < mangaToUpdate.size) {
|
||||||
val manga = mangaToUpdate[count]
|
if (job?.isCancelled == true) break
|
||||||
showProgressNotification(manga, count++, mangaToUpdate.size)
|
val manga = mangaToUpdate[count]
|
||||||
val source = sourceManager.get(manga.source) as? HttpSource ?: continue
|
showProgressNotification(manga, count++, mangaToUpdate.size)
|
||||||
val fetchedChapters = try { source.fetchChapterList(manga).toBlocking().single() }
|
val source = sourceManager.get(manga.source) as? HttpSource ?: continue
|
||||||
catch(e: java.lang.Exception) {
|
val fetchedChapters = try {
|
||||||
failedUpdates.add(manga)
|
source.fetchChapterList(manga).toBlocking().single()
|
||||||
emptyList<SChapter>()
|
} catch (e: java.lang.Exception) {
|
||||||
}
|
failedUpdates.add(manga)
|
||||||
if (fetchedChapters.isNotEmpty()) {
|
emptyList<SChapter>()
|
||||||
val newChapters = syncChaptersWithSource(db, fetchedChapters, manga, source)
|
} ?: emptyList()
|
||||||
if (newChapters.first.isNotEmpty()) {
|
if (fetchedChapters.isNotEmpty()) {
|
||||||
if (downloadNew && (categoriesToDownload.isEmpty() || manga.category in categoriesToDownload)) {
|
val newChapters = syncChaptersWithSource(db, fetchedChapters, manga, source)
|
||||||
downloadChapters(manga, newChapters.first.sortedBy { it.chapter_number })
|
if (newChapters.first.isNotEmpty()) {
|
||||||
hasDownloads = true
|
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()) {
|
if (newUpdates.isNotEmpty()) {
|
||||||
|
@ -120,7 +120,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
|
|||||||
resources.getString(
|
resources.getString(
|
||||||
when {
|
when {
|
||||||
inQueue -> R.string.category_already_in_queue
|
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
|
else -> R.string.updating_category_x
|
||||||
}, category.name))
|
}, category.name))
|
||||||
if (!inQueue)
|
if (!inQueue)
|
||||||
|
@ -12,15 +12,10 @@ import android.view.MenuItem
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
import android.widget.Spinner
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.appcompat.view.ActionMode
|
import androidx.appcompat.view.ActionMode
|
||||||
import androidx.appcompat.widget.SearchView
|
import androidx.appcompat.widget.SearchView
|
||||||
import androidx.core.graphics.drawable.DrawableCompat
|
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 androidx.viewpager.widget.ViewPager
|
||||||
import com.afollestad.materialdialogs.MaterialDialog
|
import com.afollestad.materialdialogs.MaterialDialog
|
||||||
import com.bluelinelabs.conductor.ControllerChangeHandler
|
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.google.android.material.tabs.TabLayout
|
||||||
import com.jakewharton.rxrelay.BehaviorRelay
|
import com.jakewharton.rxrelay.BehaviorRelay
|
||||||
import com.jakewharton.rxrelay.PublishRelay
|
import com.jakewharton.rxrelay.PublishRelay
|
||||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
|
||||||
import eu.davidea.flexibleadapter.SelectableAdapter
|
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.database.models.Category
|
import eu.kanade.tachiyomi.data.database.models.Category
|
||||||
import eu.kanade.tachiyomi.data.database.models.LibraryManga
|
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.MigrationListController
|
||||||
import eu.kanade.tachiyomi.ui.migration.manga.process.MigrationProcedureConfig
|
import eu.kanade.tachiyomi.ui.migration.manga.process.MigrationProcedureConfig
|
||||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
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.getResourceColor
|
||||||
import eu.kanade.tachiyomi.util.system.launchUI
|
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.setOnQueryTextChangeListener
|
||||||
import eu.kanade.tachiyomi.util.view.snack
|
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.filter_bottom_sheet.*
|
||||||
import kotlinx.android.synthetic.main.library_controller.*
|
import kotlinx.android.synthetic.main.library_controller.*
|
||||||
import kotlinx.android.synthetic.main.main_activity.*
|
import kotlinx.android.synthetic.main.main_activity.*
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import rx.Subscription
|
import rx.Subscription
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import java.util.Locale
|
|
||||||
import kotlin.math.min
|
|
||||||
|
|
||||||
class LibraryController(
|
open class LibraryController(
|
||||||
bundle: Bundle? = null,
|
bundle: Bundle? = null,
|
||||||
private val preferences: PreferencesHelper = Injekt.get()
|
protected val preferences: PreferencesHelper = Injekt.get()
|
||||||
) : BaseController(bundle), TabbedController,
|
) : BaseController(bundle), TabbedController,
|
||||||
ActionMode.Callback,
|
ActionMode.Callback,
|
||||||
ChangeMangaCategoriesDialog.Listener,
|
ChangeMangaCategoriesDialog.Listener,
|
||||||
MigrationInterface,
|
MigrationInterface,
|
||||||
DownloadServiceListener,
|
DownloadServiceListener,
|
||||||
LibraryServiceListener,
|
LibraryServiceListener {
|
||||||
FlexibleAdapter.OnItemClickListener,
|
|
||||||
FlexibleAdapter.OnItemLongClickListener,
|
|
||||||
FlexibleAdapter.OnItemMoveListener,
|
|
||||||
LibraryCategoryAdapter.LibraryListener{
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Position of the active category.
|
* Position of the active category.
|
||||||
*/
|
*/
|
||||||
var activeCategory: Int = preferences.lastUsedCategory().getOrDefault()
|
protected var activeCategory: Int = preferences.lastUsedCategory().getOrDefault()
|
||||||
private set
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Action mode for selections.
|
* Action mode for selections.
|
||||||
@ -107,7 +85,7 @@ class LibraryController(
|
|||||||
/**
|
/**
|
||||||
* Library search query.
|
* Library search query.
|
||||||
*/
|
*/
|
||||||
private var query = ""
|
protected var query = ""
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Currently selected mangas.
|
* Currently selected mangas.
|
||||||
@ -154,16 +132,11 @@ class LibraryController(
|
|||||||
* Adapter of the view pager.
|
* Adapter of the view pager.
|
||||||
*/
|
*/
|
||||||
private var pagerAdapter: LibraryAdapter? = null
|
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.
|
* 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
|
private var tabsVisibilitySubscription: Subscription? = null
|
||||||
|
|
||||||
@ -171,56 +144,24 @@ class LibraryController(
|
|||||||
|
|
||||||
var snack: Snackbar? = null
|
var snack: Snackbar? = null
|
||||||
|
|
||||||
var presenter = LibraryPresenter(this)
|
lateinit var presenter:LibraryPresenter
|
||||||
private set
|
private set
|
||||||
|
|
||||||
private var justStarted = true
|
protected var justStarted = true
|
||||||
|
|
||||||
private var updateScroll = true
|
var libraryLayout:Int = preferences.libraryLayout().getOrDefault()
|
||||||
|
|
||||||
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()
|
|
||||||
|
|
||||||
private var usePager: Boolean = !preferences.libraryAsSingleList().getOrDefault()
|
private var usePager: Boolean = !preferences.libraryAsSingleList().getOrDefault()
|
||||||
|
|
||||||
|
open fun contentView():View = pager_layout
|
||||||
|
|
||||||
init {
|
init {
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
retainViewMode = RetainViewMode.RETAIN_DETACH
|
retainViewMode = RetainViewMode.RETAIN_DETACH
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getTitle(): String? {
|
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 {
|
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View {
|
||||||
@ -230,64 +171,17 @@ class LibraryController(
|
|||||||
override fun onViewCreated(view: View) {
|
override fun onViewCreated(view: View) {
|
||||||
super.onViewCreated(view)
|
super.onViewCreated(view)
|
||||||
mangaPerRow = getColumnsPreferenceForCurrentOrientation().getOrDefault()
|
mangaPerRow = getColumnsPreferenceForCurrentOrientation().getOrDefault()
|
||||||
|
if (!::presenter.isInitialized)
|
||||||
|
presenter = LibraryPresenter(this)
|
||||||
|
|
||||||
if (usePager) {
|
layoutView(view)
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (selectedMangas.isNotEmpty()) {
|
if (selectedMangas.isNotEmpty()) {
|
||||||
createActionModeIfNeeded()
|
createActionModeIfNeeded()
|
||||||
}
|
}
|
||||||
|
|
||||||
//bottom_sheet.onCreate(pager_layout)
|
//bottom_sheet.onCreate(pager_layout)
|
||||||
bottom_sheet.onCreate(if (usePager) pager_layout else recycler_layout)
|
bottom_sheet.onCreate(contentView())
|
||||||
|
|
||||||
bottom_sheet.onGroupClicked = {
|
bottom_sheet.onGroupClicked = {
|
||||||
when (it) {
|
when (it) {
|
||||||
@ -305,50 +199,53 @@ class LibraryController(
|
|||||||
router.pushController(DownloadController().withFadeTransaction())
|
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()) {
|
if (presenter.isDownloading()) {
|
||||||
fab.scaleY = 1f
|
fab.scaleY = 1f
|
||||||
fab.scaleX = 1f
|
fab.scaleX = 1f
|
||||||
fab.isClickable = true
|
fab.isClickable = true
|
||||||
fab.isFocusable = true
|
fab.isFocusable = true
|
||||||
}
|
}
|
||||||
|
|
||||||
presenter.onRestore()
|
presenter.onRestore()
|
||||||
val library = presenter.getAllManga()
|
val library = presenter.getAllManga()
|
||||||
if (library != null) presenter.updateViewBlocking() //onNextLibraryUpdate(presenter.categories, library)
|
if (library != null) presenter.updateViewBlocking() //onNextLibraryUpdate(presenter.categories, library)
|
||||||
else {
|
else {
|
||||||
library_pager.alpha = 0f
|
contentView().alpha = 0f
|
||||||
recycler_layout.alpha = 0f
|
|
||||||
presenter.getLibraryBlocking()
|
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) {
|
override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) {
|
||||||
super.onChangeStarted(handler, type)
|
super.onChangeStarted(handler, type)
|
||||||
if (type.isEnter) {
|
if (type.isEnter) {
|
||||||
if (!usePager)
|
if (library_pager != null)
|
||||||
(activity as MainActivity).supportActionBar?.setDisplayShowCustomEnabled(true)
|
|
||||||
else
|
|
||||||
activity?.tabs?.setupWithViewPager(library_pager)
|
activity?.tabs?.setupWithViewPager(library_pager)
|
||||||
presenter.getLibrary()
|
presenter.getLibrary()
|
||||||
DownloadService.addListener(this)
|
DownloadService.addListener(this)
|
||||||
DownloadService.callListeners()
|
DownloadService.callListeners()
|
||||||
LibraryUpdateService.setListener(this)
|
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) {
|
override fun onActivityResumed(activity: Activity) {
|
||||||
@ -365,7 +262,6 @@ class LibraryController(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
(activity as MainActivity).supportActionBar?.setDisplayShowCustomEnabled(false)
|
|
||||||
presenter.onDestroy()
|
presenter.onDestroy()
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
@ -393,7 +289,7 @@ class LibraryController(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onUpdateManga(manga: LibraryManga) {
|
override fun onUpdateManga(manga: LibraryManga) {
|
||||||
presenter.updateManga(manga)
|
if (manga.id != null) presenter.updateManga(manga)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDetach(view: View) {
|
override fun onDetach(view: View) {
|
||||||
@ -424,56 +320,7 @@ class LibraryController(
|
|||||||
tabsVisibilitySubscription = null
|
tabsVisibilitySubscription = null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onNextLibraryUpdate(mangaMap: List<LibraryItem>, freshStart: Boolean = false) {
|
open 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
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onNextLibraryUpdate(categories: List<Category>, mangaMap: Map<Int, List<LibraryItem>>,
|
fun onNextLibraryUpdate(categories: List<Category>, mangaMap: Map<Int, List<LibraryItem>>,
|
||||||
freshStart: Boolean = false) {
|
freshStart: Boolean = false) {
|
||||||
@ -523,8 +370,8 @@ class LibraryController(
|
|||||||
}
|
}
|
||||||
else if (!freshStart) {
|
else if (!freshStart) {
|
||||||
justStarted = false
|
justStarted = false
|
||||||
if (library_pager.alpha == 0f)
|
if (pager_layout.alpha == 0f)
|
||||||
library_pager.animate().alpha(1f).setDuration(500).start()
|
pager_layout.animate().alpha(1f).setDuration(500).start()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -563,10 +410,8 @@ class LibraryController(
|
|||||||
destroyActionModeIfNeeded()
|
destroyActionModeIfNeeded()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onCatSortChanged(id: Int? = null) {
|
open fun onCatSortChanged(id: Int? = null) {
|
||||||
val catId =
|
val catId = (id ?: pagerAdapter?.categories?.getOrNull(library_pager.currentItem)?.id)
|
||||||
(if (usePager)(id ?: pagerAdapter?.categories?.getOrNull(library_pager.currentItem)?.id)
|
|
||||||
else (id ?: presenter.categories.find { it.order == activeCategory }?.id))
|
|
||||||
?: return
|
?: return
|
||||||
presenter.requestCatSortUpdate(catId)
|
presenter.requestCatSortUpdate(catId)
|
||||||
}
|
}
|
||||||
@ -574,26 +419,15 @@ class LibraryController(
|
|||||||
/**
|
/**
|
||||||
* Reattaches the adapter to the view pager to recreate fragments
|
* Reattaches the adapter to the view pager to recreate fragments
|
||||||
*/
|
*/
|
||||||
private fun reattachAdapter() {
|
protected open fun reattachAdapter() {
|
||||||
if (usePager) {
|
val adapter = pagerAdapter ?: return
|
||||||
val adapter = pagerAdapter ?: return
|
|
||||||
|
|
||||||
val position = library_pager.currentItem
|
val position = library_pager.currentItem
|
||||||
|
|
||||||
adapter.recycle = false
|
adapter.recycle = false
|
||||||
library_pager.adapter = adapter
|
library_pager.adapter = adapter
|
||||||
library_pager.currentItem = position
|
library_pager.currentItem = position
|
||||||
adapter.recycle = true
|
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -643,8 +477,6 @@ class LibraryController(
|
|||||||
setOnQueryTextChangeListener(searchView) {
|
setOnQueryTextChangeListener(searchView) {
|
||||||
query = it ?: ""
|
query = it ?: ""
|
||||||
searchRelay.call(query)
|
searchRelay.call(query)
|
||||||
adapter.setFilter(it)
|
|
||||||
adapter.performFilter()
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
searchItem.fixExpand(onExpand = { invalidateMenuOnExpand() })
|
searchItem.fixExpand(onExpand = { invalidateMenuOnExpand() })
|
||||||
@ -778,11 +610,6 @@ class LibraryController(
|
|||||||
override fun onDestroyActionMode(mode: ActionMode?) {
|
override fun onDestroyActionMode(mode: ActionMode?) {
|
||||||
// Clear all the manga selections and notify child views.
|
// Clear all the manga selections and notify child views.
|
||||||
selectedMangas.clear()
|
selectedMangas.clear()
|
||||||
adapter.mode = SelectableAdapter.Mode.SINGLE
|
|
||||||
adapter.clearSelection()
|
|
||||||
adapter.notifyDataSetChanged()
|
|
||||||
lastClickPosition = -1
|
|
||||||
adapter.isLongPressDragEnabled = canDrag()
|
|
||||||
selectionRelay.call(LibrarySelectionEvent.Cleared())
|
selectionRelay.call(LibrarySelectionEvent.Cleared())
|
||||||
actionMode = null
|
actionMode = null
|
||||||
}
|
}
|
||||||
@ -797,36 +624,14 @@ class LibraryController(
|
|||||||
* @param manga the manga whose selection has changed.
|
* @param manga the manga whose selection has changed.
|
||||||
* @param selected whether it's now selected or not.
|
* @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 (selected) {
|
||||||
if (selectedMangas.add(manga)) {
|
if (selectedMangas.add(manga)) {
|
||||||
if (usePager) selectionRelay.call(LibrarySelectionEvent.Selected(manga))
|
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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (selectedMangas.remove(manga)) {
|
if (selectedMangas.remove(manga)) {
|
||||||
if (usePager) selectionRelay.call(LibrarySelectionEvent.Unselected(manga))
|
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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -878,16 +683,6 @@ class LibraryController(
|
|||||||
destroyActionModeIfNeeded()
|
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
|
/// Method for the category view
|
||||||
fun startReading(manga: Manga) {
|
fun startReading(manga: Manga) {
|
||||||
val activity = activity ?: return
|
val activity = activity ?: return
|
||||||
@ -896,147 +691,4 @@ class LibraryController(
|
|||||||
destroyActionModeIfNeeded()
|
destroyActionModeIfNeeded()
|
||||||
startActivity(intent)
|
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.database.models.Category
|
||||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateService
|
import eu.kanade.tachiyomi.data.library.LibraryUpdateService
|
||||||
import eu.kanade.tachiyomi.util.system.getResourceColor
|
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.invisible
|
||||||
import eu.kanade.tachiyomi.util.view.visible
|
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>() {
|
class LibraryHeaderItem(val category: Category) : AbstractHeaderItem<LibraryHeaderItem.Holder>() {
|
||||||
|
|
||||||
@ -63,7 +63,7 @@ class LibraryHeaderItem(val category: Category) : AbstractHeaderItem<LibraryHead
|
|||||||
return -(category.id!!)
|
return -(category.id!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
class Holder(view: View, private val adapter: LibraryCategoryAdapter) :
|
class Holder(val view: View, private val adapter: LibraryCategoryAdapter) :
|
||||||
FlexibleViewHolder(view, adapter, true) {
|
FlexibleViewHolder(view, adapter, true) {
|
||||||
|
|
||||||
private val sectionText: TextView = view.findViewById(R.id.category_title)
|
private val sectionText: TextView = view.findViewById(R.id.category_title)
|
||||||
@ -92,15 +92,15 @@ class LibraryHeaderItem(val category: Category) : AbstractHeaderItem<LibraryHead
|
|||||||
|
|
||||||
when {
|
when {
|
||||||
item.category.id == -1 -> {
|
item.category.id == -1 -> {
|
||||||
|
catProgress.alpha = 1f
|
||||||
updateButton.invisible()
|
updateButton.invisible()
|
||||||
catProgress.gone()
|
|
||||||
}
|
}
|
||||||
LibraryUpdateService.categoryInQueue(item.category.id) -> {
|
LibraryUpdateService.categoryInQueue(item.category.id) -> {
|
||||||
catProgress.visible()
|
catProgress.alpha = 1f
|
||||||
updateButton.invisible()
|
updateButton.invisible()
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
catProgress.gone()
|
catProgress.alpha = 0f
|
||||||
updateButton.visible()
|
updateButton.visible()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -108,16 +108,15 @@ class LibraryHeaderItem(val category: Category) : AbstractHeaderItem<LibraryHead
|
|||||||
|
|
||||||
private fun addCategoryToUpdate() {
|
private fun addCategoryToUpdate() {
|
||||||
if (adapter.libraryListener.updateCategory(adapterPosition)) {
|
if (adapter.libraryListener.updateCategory(adapterPosition)) {
|
||||||
catProgress.visible()
|
catProgress.alpha = 1f
|
||||||
updateButton.invisible()
|
updateButton.invisible()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showCatSortOptions() {
|
private fun showCatSortOptions() {
|
||||||
val category =
|
val category =
|
||||||
(adapter.getItem(adapterPosition) as? LibraryHeaderItem)?.category ?: return
|
(adapter.getItem(adapterPosition) as? LibraryHeaderItem)?.category ?: return
|
||||||
// Create a PopupMenu, giving it the clicked view for an anchor
|
// 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
|
// Inflate our menu resource into the PopupMenu's Menu
|
||||||
popup.menuInflater.inflate(
|
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()
|
updateTitle()
|
||||||
val shadow2:View = (pagerView.parent as ViewGroup).findViewById(R.id.shadow2)
|
val shadow2:View = (pagerView.parent as ViewGroup).findViewById(R.id.shadow2)
|
||||||
val shadow:View = (pagerView.parent as ViewGroup).findViewById(R.id.shadow)
|
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 coordLayout:View = (pagerView.parent as ViewGroup).findViewById(R.id.snackbar_layout)
|
||||||
val phoneLandscape = (isLandscape() && !isTablet())
|
val phoneLandscape = (isLandscape() && !isTablet())
|
||||||
if (phoneLandscape)
|
if (phoneLandscape)
|
||||||
@ -167,7 +167,7 @@ class SortFilterBottomSheet @JvmOverloads constructor(context: Context, attrs: A
|
|||||||
}
|
}
|
||||||
if (sheetBehavior?.state == BottomSheetBehavior.STATE_COLLAPSED) {
|
if (sheetBehavior?.state == BottomSheetBehavior.STATE_COLLAPSED) {
|
||||||
val height = context.resources.getDimensionPixelSize(R.dimen.rounder_radius)
|
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)
|
bottomMargin = if (phoneLandscape) 0 else (top_bar.height - height)
|
||||||
}
|
}
|
||||||
pager?.setPadding(0, 0, 0, if (phoneLandscape) 0 else
|
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.download.DownloadController
|
||||||
import eu.kanade.tachiyomi.ui.extension.ExtensionController
|
import eu.kanade.tachiyomi.ui.extension.ExtensionController
|
||||||
import eu.kanade.tachiyomi.ui.library.LibraryController
|
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.manga.MangaController
|
||||||
import eu.kanade.tachiyomi.ui.recent_updates.RecentChaptersController
|
import eu.kanade.tachiyomi.ui.recent_updates.RecentChaptersController
|
||||||
import eu.kanade.tachiyomi.ui.recently_read.RecentlyReadController
|
import eu.kanade.tachiyomi.ui.recently_read.RecentlyReadController
|
||||||
@ -131,7 +132,11 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
|
|||||||
val currentRoot = router.backstack.firstOrNull()
|
val currentRoot = router.backstack.firstOrNull()
|
||||||
if (currentRoot?.tag()?.toIntOrNull() != id) {
|
if (currentRoot?.tag()?.toIntOrNull() != id) {
|
||||||
when (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 -> {
|
R.id.nav_recents -> {
|
||||||
if (preferences.showRecentUpdates().getOrDefault())
|
if (preferences.showRecentUpdates().getOrDefault())
|
||||||
setRoot(RecentChaptersController(), id)
|
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.main.MainActivity
|
||||||
import eu.kanade.tachiyomi.ui.manga.MangaController
|
import eu.kanade.tachiyomi.ui.manga.MangaController
|
||||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
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.system.notificationManager
|
||||||
import eu.kanade.tachiyomi.util.view.RecyclerWindowInsetsListener
|
import eu.kanade.tachiyomi.util.view.RecyclerWindowInsetsListener
|
||||||
import eu.kanade.tachiyomi.util.view.snack
|
import eu.kanade.tachiyomi.util.view.snack
|
||||||
import eu.kanade.tachiyomi.ui.recently_read.RecentlyReadController
|
|
||||||
import kotlinx.android.synthetic.main.recent_chapters_controller.*
|
import kotlinx.android.synthetic.main.recent_chapters_controller.*
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import uy.kohesive.injekt.Injekt
|
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.setDistanceToTriggerSync((2 * 64 * view.resources.displayMetrics.density).toInt())
|
||||||
swipe_refresh.refreshes().subscribeUntilDestroy {
|
swipe_refresh.refreshes().subscribeUntilDestroy {
|
||||||
if (!LibraryUpdateService.isRunning(view.context)) {
|
if (!LibraryUpdateService.isRunning()) {
|
||||||
LibraryUpdateService.start(view.context)
|
LibraryUpdateService.start(view.context)
|
||||||
view.snack(R.string.updating_library)
|
view.snack(R.string.updating_library)
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@ package eu.kanade.tachiyomi.ui.setting
|
|||||||
|
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.preference.PreferenceScreen
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.preference.PreferenceScreen
|
||||||
import com.afollestad.materialdialogs.MaterialDialog
|
import com.afollestad.materialdialogs.MaterialDialog
|
||||||
import com.bluelinelabs.conductor.RouterTransaction
|
import com.bluelinelabs.conductor.RouterTransaction
|
||||||
import com.bluelinelabs.conductor.changehandler.FadeChangeHandler
|
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.download.DownloadManager
|
||||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateService
|
import eu.kanade.tachiyomi.data.library.LibraryUpdateService
|
||||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateService.Target
|
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.network.NetworkHelper
|
||||||
import eu.kanade.tachiyomi.source.SourceManager
|
import eu.kanade.tachiyomi.source.SourceManager
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||||
import eu.kanade.tachiyomi.ui.library.LibraryController
|
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.launchUI
|
||||||
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
import kotlinx.coroutines.CoroutineStart
|
import kotlinx.coroutines.CoroutineStart
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import rx.android.schedulers.AndroidSchedulers
|
import rx.android.schedulers.AndroidSchedulers
|
||||||
import rx.schedulers.Schedulers
|
import rx.schedulers.Schedulers
|
||||||
@ -154,7 +156,11 @@ class SettingsAdvancedController : SettingsController() {
|
|||||||
|
|
||||||
private fun clearDatabase() {
|
private fun clearDatabase() {
|
||||||
// Avoid weird behavior by going back to the library.
|
// 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.backstack.drop(1)
|
||||||
|
|
||||||
router.setBackstack(newBackstack, FadeChangeHandler())
|
router.setBackstack(newBackstack, FadeChangeHandler())
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
android:gravity="center|start"
|
android:gravity="center|start"
|
||||||
android:inputType="none"
|
android:inputType="none"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:textColor="?attr/actionBarTintColor"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@id/update_button"
|
app:layout_constraintBottom_toBottomOf="@id/update_button"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintWidth_default="wrap"
|
app:layout_constraintWidth_default="wrap"
|
||||||
@ -32,6 +31,7 @@
|
|||||||
android:background="@drawable/square_ripple"
|
android:background="@drawable/square_ripple"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:layout_marginEnd="10dp"
|
android:layout_marginEnd="10dp"
|
||||||
|
android:drawableTint="@color/gray_button"
|
||||||
android:drawableEnd="@drawable/ic_sort_white_24dp"
|
android:drawableEnd="@drawable/ic_sort_white_24dp"
|
||||||
android:drawablePadding="6dp"
|
android:drawablePadding="6dp"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
@ -65,10 +65,15 @@
|
|||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/cat_progress"
|
android:id="@+id/cat_progress"
|
||||||
android:layout_width="wrap_content"
|
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"
|
android:layout_marginStart="0dp"
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/category_title"
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
app:layout_constraintStart_toEndOf="@+id/category_title"
|
app:layout_constraintBottom_toBottomOf="@+id/update_button"
|
||||||
app:layout_constraintTop_toTopOf="@+id/category_title" />
|
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>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -6,16 +6,8 @@
|
|||||||
android:id="@+id/library_layout"
|
android:id="@+id/library_layout"
|
||||||
android:layout_height="match_parent">
|
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
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
android:id="@+id/pager_layout"
|
android:id="@+id/pager_layout"
|
||||||
android:visibility="gone"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
@ -25,15 +17,6 @@
|
|||||||
android:layout_height="match_parent" />
|
android:layout_height="match_parent" />
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</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
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
android:id="@+id/snackbar_layout"
|
android:id="@+id/snackbar_layout"
|
||||||
android:layout_width="match_parent"
|
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:layout_height="wrap_content"
|
||||||
android:gravity="start"
|
android:gravity="start"
|
||||||
android:textSize="20sp"
|
android:textSize="20sp"
|
||||||
|
android:textColor="?attr/actionBarTintColor"
|
||||||
tools:text="Title"
|
tools:text="Title"
|
||||||
tools:background="?attr/colorPrimary"/>
|
tools:background="?attr/colorPrimary"/>
|
@ -21,7 +21,7 @@
|
|||||||
</declare-styleable>
|
</declare-styleable>
|
||||||
|
|
||||||
<attr name="navigation_view_theme" format="reference"/>
|
<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="tabBarIconColor" format="reference|integer"/>
|
||||||
<attr name="tabBarIconInactive" format="reference|integer"/>
|
<attr name="tabBarIconInactive" format="reference|integer"/>
|
||||||
<attr name="selectable_list_drawable" format="reference|integer" />
|
<attr name="selectable_list_drawable" format="reference|integer" />
|
||||||
|
Loading…
Reference in New Issue
Block a user