Attempting making library as one big list
This commit is contained in:
parent
fc0ab3e878
commit
83441a5ebd
@ -113,6 +113,8 @@ object PreferenceKeys {
|
|||||||
|
|
||||||
const val libraryLayout = "pref_display_library_layout"
|
const val libraryLayout = "pref_display_library_layout"
|
||||||
|
|
||||||
|
const val libraryUsingPager = "library_using_pager"
|
||||||
|
|
||||||
const val lang = "app_language"
|
const val lang = "app_language"
|
||||||
|
|
||||||
const val dateFormat = "app_date_format"
|
const val dateFormat = "app_date_format"
|
||||||
|
@ -11,9 +11,9 @@ import eu.kanade.tachiyomi.R
|
|||||||
import eu.kanade.tachiyomi.data.track.TrackService
|
import eu.kanade.tachiyomi.data.track.TrackService
|
||||||
import eu.kanade.tachiyomi.source.Source
|
import eu.kanade.tachiyomi.source.Source
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.Locale
|
|
||||||
import java.text.DateFormat
|
import java.text.DateFormat
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Locale
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys
|
import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys
|
||||||
|
|
||||||
fun <T> Preference<T>.getOrDefault(): T = get() ?: defaultValue()!!
|
fun <T> Preference<T>.getOrDefault(): T = get() ?: defaultValue()!!
|
||||||
@ -173,6 +173,8 @@ class PreferencesHelper(val context: Context) {
|
|||||||
|
|
||||||
fun libraryLayout() = rxPrefs.getInteger(Keys.libraryLayout, 1)
|
fun libraryLayout() = rxPrefs.getInteger(Keys.libraryLayout, 1)
|
||||||
|
|
||||||
|
fun libraryUsingPager() = rxPrefs.getBoolean(Keys.libraryUsingPager, false)
|
||||||
|
|
||||||
fun downloadBadge() = rxPrefs.getBoolean(Keys.downloadBadge, false)
|
fun downloadBadge() = rxPrefs.getBoolean(Keys.downloadBadge, false)
|
||||||
|
|
||||||
fun filterDownloaded() = rxPrefs.getInteger(Keys.filterDownloaded, 0)
|
fun filterDownloaded() = rxPrefs.getInteger(Keys.filterDownloaded, 0)
|
||||||
|
@ -6,11 +6,10 @@ import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
|||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||||
import eu.kanade.tachiyomi.ui.category.CategoryAdapter
|
|
||||||
import eu.kanade.tachiyomi.util.lang.chop
|
import eu.kanade.tachiyomi.util.lang.chop
|
||||||
import eu.kanade.tachiyomi.util.lang.removeArticles
|
import eu.kanade.tachiyomi.util.lang.removeArticles
|
||||||
import eu.kanade.tachiyomi.util.system.launchUI
|
import uy.kohesive.injekt.Injekt
|
||||||
import kotlinx.coroutines.delay
|
import uy.kohesive.injekt.api.get
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Calendar
|
import java.util.Calendar
|
||||||
@ -22,19 +21,18 @@ import java.util.Locale
|
|||||||
*
|
*
|
||||||
* @param view the fragment containing this adapter.
|
* @param view the fragment containing this adapter.
|
||||||
*/
|
*/
|
||||||
class LibraryCategoryAdapter(val view: LibraryCategoryView) :
|
class LibraryCategoryAdapter(val libraryListener: LibraryListener) :
|
||||||
FlexibleAdapter<LibraryItem>(null, view, true) {
|
FlexibleAdapter<IFlexible<*>>(null, libraryListener, true) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
setDisplayHeadersAtStartUp(!Injekt.get<PreferencesHelper>().libraryUsingPager()
|
||||||
|
.getOrDefault())
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* The list of manga in this category.
|
* The list of manga in this category.
|
||||||
*/
|
*/
|
||||||
private var mangas: List<LibraryItem> = emptyList()
|
private var mangas: List<LibraryItem> = emptyList()
|
||||||
|
|
||||||
/**
|
|
||||||
* Listener called when an item of the list press start reading.
|
|
||||||
*/
|
|
||||||
val libraryListener: LibraryListener = view
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a list of manga in the adapter.
|
* Sets a list of manga in the adapter.
|
||||||
*
|
*
|
||||||
@ -47,13 +45,26 @@ class LibraryCategoryAdapter(val view: LibraryCategoryView) :
|
|||||||
performFilter()
|
performFilter()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the position in the adapter for the given manga.
|
||||||
|
*
|
||||||
|
* @param manga the manga to find.
|
||||||
|
*/
|
||||||
|
fun indexOf(categoryOrder: Int): Int {
|
||||||
|
return currentItems.indexOfFirst {
|
||||||
|
if (it is LibraryHeaderItem) it.category.order == categoryOrder
|
||||||
|
else false }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the position in the adapter for the given manga.
|
* Returns the position in the adapter for the given manga.
|
||||||
*
|
*
|
||||||
* @param manga the manga to find.
|
* @param manga the manga to find.
|
||||||
*/
|
*/
|
||||||
fun indexOf(manga: Manga): Int {
|
fun indexOf(manga: Manga): Int {
|
||||||
return currentItems.indexOfFirst { it.manga.id == manga.id }
|
return currentItems.indexOfFirst {
|
||||||
|
if (it is LibraryItem) it.manga.id == manga.id
|
||||||
|
else false }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun performFilter() {
|
fun performFilter() {
|
||||||
@ -64,7 +75,7 @@ class LibraryCategoryAdapter(val view: LibraryCategoryView) :
|
|||||||
else {
|
else {
|
||||||
updateDataSet(mangas.filter { it.filter(s) })
|
updateDataSet(mangas.filter { it.filter(s) })
|
||||||
}
|
}
|
||||||
isLongPressDragEnabled = view.canDrag() && s.isNullOrBlank()
|
isLongPressDragEnabled = libraryListener.canDrag() && s.isNullOrBlank()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateBubbleText(position: Int):String {
|
override fun onCreateBubbleText(position: Int):String {
|
||||||
@ -74,6 +85,13 @@ class LibraryCategoryAdapter(val view: LibraryCategoryView) :
|
|||||||
"Bottom"
|
"Bottom"
|
||||||
} else { // Get and show the first character
|
} else { // Get and show the first character
|
||||||
val iFlexible: IFlexible<*>? = getItem(position)
|
val iFlexible: IFlexible<*>? = getItem(position)
|
||||||
|
return if (iFlexible is LibraryHeaderItem) {
|
||||||
|
iFlexible.category.name
|
||||||
|
} else {
|
||||||
|
val db:DatabaseHelper by injectLazy()
|
||||||
|
val category = db.getCategoriesForManga((iFlexible as LibraryItem).manga).executeAsBlocking().firstOrNull()?.name
|
||||||
|
category?.chop(10) ?: "Default"
|
||||||
|
}
|
||||||
val preferences:PreferencesHelper by injectLazy()
|
val preferences:PreferencesHelper by injectLazy()
|
||||||
when (preferences.librarySortingMode().getOrDefault()) {
|
when (preferences.librarySortingMode().getOrDefault()) {
|
||||||
LibrarySort.DRAG_AND_DROP -> {
|
LibrarySort.DRAG_AND_DROP -> {
|
||||||
@ -145,5 +163,6 @@ class LibraryCategoryAdapter(val view: LibraryCategoryView) :
|
|||||||
*/
|
*/
|
||||||
fun startReading(position: Int)
|
fun startReading(position: Int)
|
||||||
fun onItemReleased(position: Int)
|
fun onItemReleased(position: Int)
|
||||||
|
fun canDrag(): Boolean
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,6 +79,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
|
|||||||
fun onCreate(controller: LibraryController) {
|
fun onCreate(controller: LibraryController) {
|
||||||
this.controller = controller
|
this.controller = controller
|
||||||
|
|
||||||
|
adapter = LibraryCategoryAdapter(this)
|
||||||
recycler = if (preferences.libraryLayout().getOrDefault() == 0) {
|
recycler = if (preferences.libraryLayout().getOrDefault() == 0) {
|
||||||
(swipe_refresh.inflate(R.layout.library_list_recycler) as RecyclerView).apply {
|
(swipe_refresh.inflate(R.layout.library_list_recycler) as RecyclerView).apply {
|
||||||
layoutManager = LinearLayoutManager(context)
|
layoutManager = LinearLayoutManager(context)
|
||||||
@ -89,7 +90,6 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
adapter = LibraryCategoryAdapter(this)
|
|
||||||
|
|
||||||
recycler.setHasFixedSize(true)
|
recycler.setHasFixedSize(true)
|
||||||
recycler.adapter = adapter
|
recycler.adapter = adapter
|
||||||
@ -155,7 +155,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
|
|||||||
.subscribe {
|
.subscribe {
|
||||||
if (it == category.id) {
|
if (it == category.id) {
|
||||||
adapter.currentItems.forEach { item ->
|
adapter.currentItems.forEach { item ->
|
||||||
controller.setSelection(item.manga, true)
|
controller.setSelection((item as LibraryItem).manga, true)
|
||||||
}
|
}
|
||||||
controller.invalidateActionMode()
|
controller.invalidateActionMode()
|
||||||
}
|
}
|
||||||
@ -167,11 +167,12 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
|
|||||||
if (it.second in -2..-1) {
|
if (it.second in -2..-1) {
|
||||||
val items = adapter.currentItems.toMutableList()
|
val items = adapter.currentItems.toMutableList()
|
||||||
val mangas = controller.selectedMangas
|
val mangas = controller.selectedMangas
|
||||||
val selectedManga = items.filter { item -> item.manga in mangas }
|
val selectedManga = items.filter { item -> (item as LibraryItem).manga in
|
||||||
|
mangas }
|
||||||
items.removeAll(selectedManga)
|
items.removeAll(selectedManga)
|
||||||
if (it.second == -1) items.addAll(0, selectedManga)
|
if (it.second == -1) items.addAll(0, selectedManga)
|
||||||
else items.addAll(selectedManga)
|
else items.addAll(selectedManga)
|
||||||
adapter.setItems(items)
|
adapter.setItems(items.filterIsInstance<LibraryItem>())
|
||||||
adapter.notifyDataSetChanged()
|
adapter.notifyDataSetChanged()
|
||||||
saveDragSort()
|
saveDragSort()
|
||||||
}
|
}
|
||||||
@ -179,7 +180,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun canDrag(): Boolean {
|
override fun canDrag(): Boolean {
|
||||||
val sortingMode = preferences.librarySortingMode().getOrDefault()
|
val sortingMode = preferences.librarySortingMode().getOrDefault()
|
||||||
val filterOff = preferences.filterCompleted().getOrDefault() +
|
val filterOff = preferences.filterCompleted().getOrDefault() +
|
||||||
preferences.filterTracked().getOrDefault() +
|
preferences.filterTracked().getOrDefault() +
|
||||||
@ -212,6 +213,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
|
|||||||
val mangaForCategory = event.getMangaForCategory(category).orEmpty()
|
val mangaForCategory = event.getMangaForCategory(category).orEmpty()
|
||||||
|
|
||||||
adapter.setItems(mangaForCategory)
|
adapter.setItems(mangaForCategory)
|
||||||
|
adapter.hideAllHeaders()
|
||||||
|
|
||||||
swipe_refresh.isEnabled = !preferences.hideCategories().getOrDefault()
|
swipe_refresh.isEnabled = !preferences.hideCategories().getOrDefault()
|
||||||
|
|
||||||
@ -283,7 +285,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
|
|||||||
*/
|
*/
|
||||||
override fun onItemClick(view: View?, position: Int): Boolean {
|
override fun onItemClick(view: View?, position: Int): Boolean {
|
||||||
// If the action mode is created and the position is valid, toggle the selection.
|
// If the action mode is created and the position is valid, toggle the selection.
|
||||||
val item = adapter.getItem(position) ?: return false
|
val item = adapter.getItem(position) as? LibraryItem ?: return false
|
||||||
return if (adapter.mode == SelectableAdapter.Mode.MULTI) {
|
return if (adapter.mode == SelectableAdapter.Mode.MULTI) {
|
||||||
lastClickPosition = position
|
lastClickPosition = position
|
||||||
toggleSelection(position)
|
toggleSelection(position)
|
||||||
@ -328,13 +330,13 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun startReading(position: Int) {
|
override fun startReading(position: Int) {
|
||||||
val manga = adapter.getItem(position)?.manga ?: return
|
val manga = (adapter.getItem(position) as? LibraryItem)?.manga ?: return
|
||||||
if (adapter.mode == SelectableAdapter.Mode.MULTI) toggleSelection(position)
|
if (adapter.mode == SelectableAdapter.Mode.MULTI) toggleSelection(position)
|
||||||
else controller.startReading(manga)
|
else controller.startReading(manga)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun saveDragSort() {
|
private fun saveDragSort() {
|
||||||
val mangaIds = adapter.currentItems.mapNotNull { it.manga.id }
|
val mangaIds = adapter.currentItems.mapNotNull { (it as? LibraryItem)?.manga?.id }
|
||||||
category.mangaSort = null
|
category.mangaSort = null
|
||||||
category.mangaOrder = mangaIds
|
category.mangaOrder = mangaIds
|
||||||
if (category.id == 0)
|
if (category.id == 0)
|
||||||
@ -373,7 +375,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
|
|||||||
private fun toggleSelection(position: Int) {
|
private fun toggleSelection(position: Int) {
|
||||||
val item = adapter.getItem(position) ?: return
|
val item = adapter.getItem(position) ?: return
|
||||||
|
|
||||||
controller.setSelection(item.manga, !adapter.isSelected(position))
|
controller.setSelection((item as LibraryItem).manga, !adapter.isSelected(position))
|
||||||
controller.invalidateActionMode()
|
controller.invalidateActionMode()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -386,7 +388,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
|
|||||||
private fun setSelection(position: Int) {
|
private fun setSelection(position: Int) {
|
||||||
val item = adapter.getItem(position) ?: return
|
val item = adapter.getItem(position) ?: return
|
||||||
|
|
||||||
controller.setSelection(item.manga, true)
|
controller.setSelection((item as LibraryItem).manga, true)
|
||||||
controller.invalidateActionMode()
|
controller.invalidateActionMode()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.ui.library
|
|||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
@ -14,10 +13,18 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.WindowInsets
|
import android.view.WindowInsets
|
||||||
import android.view.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
|
import android.widget.ArrayAdapter
|
||||||
|
import android.widget.Spinner
|
||||||
|
import android.widget.TextView
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.appcompat.view.ActionMode
|
import androidx.appcompat.view.ActionMode
|
||||||
|
import androidx.appcompat.widget.AppCompatSpinner
|
||||||
import androidx.appcompat.widget.SearchView
|
import androidx.appcompat.widget.SearchView
|
||||||
|
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
import androidx.core.graphics.drawable.DrawableCompat
|
import androidx.core.graphics.drawable.DrawableCompat
|
||||||
|
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
|
||||||
@ -26,9 +33,10 @@ import com.f2prateek.rx.preferences.Preference
|
|||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||||
import com.google.android.material.snackbar.BaseTransientBottomBar
|
import com.google.android.material.snackbar.BaseTransientBottomBar
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
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
|
||||||
@ -40,7 +48,6 @@ import eu.kanade.tachiyomi.data.library.LibraryUpdateService
|
|||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.BaseController
|
import eu.kanade.tachiyomi.ui.base.controller.BaseController
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.TabbedController
|
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
||||||
import eu.kanade.tachiyomi.ui.category.CategoryController
|
import eu.kanade.tachiyomi.ui.category.CategoryController
|
||||||
import eu.kanade.tachiyomi.ui.download.DownloadController
|
import eu.kanade.tachiyomi.ui.download.DownloadController
|
||||||
@ -53,14 +60,20 @@ 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.updateLayoutParams
|
||||||
|
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 rx.Subscription
|
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
@ -68,12 +81,16 @@ class LibraryController(
|
|||||||
bundle: Bundle? = null,
|
bundle: Bundle? = null,
|
||||||
private val preferences: PreferencesHelper = Injekt.get()
|
private val preferences: PreferencesHelper = Injekt.get()
|
||||||
) : BaseController(bundle),
|
) : BaseController(bundle),
|
||||||
TabbedController,
|
//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.
|
||||||
@ -135,14 +152,19 @@ class LibraryController(
|
|||||||
/**
|
/**
|
||||||
* Adapter of the view pager.
|
* Adapter of the view pager.
|
||||||
*/
|
*/
|
||||||
private var adapter: 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)
|
// private var tabsVisibilityRelay: BehaviorRelay<Boolean> = BehaviorRelay.create(false)
|
||||||
|
|
||||||
private var tabsVisibilitySubscription: Subscription? = null
|
// private var tabsVisibilitySubscription: Subscription? = null
|
||||||
|
|
||||||
private var observeLater:Boolean = false
|
private var observeLater:Boolean = false
|
||||||
|
|
||||||
@ -153,29 +175,76 @@ class LibraryController(
|
|||||||
|
|
||||||
private var justStarted = true
|
private var justStarted = true
|
||||||
|
|
||||||
|
private var updateScroll = true
|
||||||
|
|
||||||
|
private var spinnerAdapter: SpinnerAdapter? = null
|
||||||
|
|
||||||
|
var scrollLister = 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
|
||||||
|
if (preferences.librarySortingMode().getOrDefault() == LibrarySort.DRAG_AND_DROP)
|
||||||
|
bottom_sheet.updateTitle()
|
||||||
|
// spinner.onItemSelectedListener = null
|
||||||
|
spinnerAdapter?.setCustomText(category?.name)
|
||||||
|
//spinner.view
|
||||||
|
//spinner.post { spinner.onItemSelectedListener = listener }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recycler view of the list of manga.
|
||||||
|
*/
|
||||||
|
private lateinit var recycler: RecyclerView
|
||||||
|
|
||||||
|
var usePager = preferences.libraryUsingPager().getOrDefault()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
retainViewMode = RetainViewMode.RETAIN_DETACH
|
retainViewMode = RetainViewMode.RETAIN_DETACH
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getTitle(): String? {
|
override fun getTitle(): String? {
|
||||||
return resources?.getString(R.string.label_library)
|
return null//if (title != null) null else resources?.getString(R.string.label_library)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var title: String? = null
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
setTitle()
|
||||||
|
}
|
||||||
|
|
||||||
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View {
|
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View {
|
||||||
return inflater.inflate(R.layout.library_controller, container, false)
|
return inflater.inflate(R.layout.library_controller, container, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View) {
|
override fun onViewCreated(view: View) {
|
||||||
super.onViewCreated(view)
|
super.onViewCreated(view)
|
||||||
|
mangaPerRow = getColumnsPreferenceForCurrentOrientation().getOrDefault()
|
||||||
|
|
||||||
adapter = LibraryAdapter(this)
|
if (usePager) {
|
||||||
library_pager.adapter = adapter
|
pager_layout.visible()
|
||||||
|
fast_scroller.gone()
|
||||||
|
pagerAdapter = LibraryAdapter(this)
|
||||||
|
library_pager.adapter = pagerAdapter
|
||||||
library_pager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
|
library_pager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
|
||||||
override fun onPageSelected(position: Int) {
|
override fun onPageSelected(position: Int) {
|
||||||
preferences.lastUsedCategory().set(position)
|
preferences.lastUsedCategory().set(position)
|
||||||
activeCategory = position
|
activeCategory = position
|
||||||
bottom_sheet.lastCategory = adapter?.categories?.getOrNull(position)
|
bottom_sheet.lastCategory = pagerAdapter?.categories?.getOrNull(position)
|
||||||
if (preferences.librarySortingMode().getOrDefault() == LibrarySort.DRAG_AND_DROP) bottom_sheet.updateTitle()
|
if (preferences.librarySortingMode().getOrDefault() == LibrarySort.DRAG_AND_DROP) bottom_sheet.updateTitle()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,14 +255,51 @@ class LibraryController(
|
|||||||
|
|
||||||
override fun onPageScrollStateChanged(state: Int) {}
|
override fun onPageScrollStateChanged(state: Int) {}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
adapter = LibraryCategoryAdapter(this)
|
||||||
|
recycler = if (preferences.libraryLayout().getOrDefault() == 0) {
|
||||||
|
(swipe_refresh.inflate(R.layout.library_list_recycler) as RecyclerView).apply {
|
||||||
|
layoutManager = LinearLayoutManager(context)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
(swipe_refresh.inflate(R.layout.library_grid_recycler) as AutofitRecyclerView).apply {
|
||||||
|
spanCount = mangaPerRow
|
||||||
|
manager.spanSizeLookup = (object : GridLayoutManager.SpanSizeLookup() {
|
||||||
|
override fun getSpanSize(position: Int): Int {
|
||||||
|
val item = this@LibraryController.adapter.getItem(position)
|
||||||
|
return if (item is LibraryHeaderItem)
|
||||||
|
manager.spanCount else 1
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
recycler.setHasFixedSize(true)
|
||||||
|
recycler.adapter = adapter
|
||||||
|
//adapter.setStickyHeaders(true)
|
||||||
|
recycler_layout.addView(recycler)
|
||||||
|
adapter.fastScroller = fast_scroller
|
||||||
|
recycler.addOnScrollListener(scrollLister)
|
||||||
|
|
||||||
|
spinner = swipe_refresh.inflate(R.layout.library_spinner) as AppCompatSpinner
|
||||||
|
(activity as MainActivity).supportActionBar?.setDisplayShowCustomEnabled(true)
|
||||||
|
(activity as MainActivity).supportActionBar?.customView = spinner
|
||||||
|
spinnerAdapter = SpinnerAdapter(view.context, R.layout.library_spinner_textview,
|
||||||
|
arrayOf(resources!!.getString(R.string.label_library)))
|
||||||
|
spinner.adapter = spinnerAdapter
|
||||||
|
|
||||||
|
spinnerAdapter?.setCustomText(resources?.getString(R.string.label_library))
|
||||||
|
}
|
||||||
|
|
||||||
mangaPerRow = getColumnsPreferenceForCurrentOrientation().getOrDefault()
|
|
||||||
|
|
||||||
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 swipe_refresh)
|
||||||
|
|
||||||
bottom_sheet.onGroupClicked = {
|
bottom_sheet.onGroupClicked = {
|
||||||
when (it) {
|
when (it) {
|
||||||
@ -211,6 +317,28 @@ class LibraryController(
|
|||||||
router.pushController(DownloadController().withFadeTransaction())
|
router.pushController(DownloadController().withFadeTransaction())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// spinner.onItemSelectedListener = listener
|
||||||
|
/*spinner.onItemSelectedListener = IgnoreFirstSpinnerListener { position ->
|
||||||
|
if (!updateScroll) return@IgnoreFirstSpinnerListener
|
||||||
|
val headerPosition = adapter.indexOf(position) + 1
|
||||||
|
if (headerPosition > -1) (recycler.layoutManager as LinearLayoutManager)
|
||||||
|
.scrollToPositionWithOffset(position, 0)
|
||||||
|
}*/
|
||||||
|
|
||||||
|
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) + 5.dpToPx
|
||||||
|
recycler.updatePaddingRelative(bottom = height)
|
||||||
|
fast_scroller.updateLayoutParams<CoordinatorLayout.LayoutParams> {
|
||||||
|
bottomMargin = height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (presenter.isDownloading()) {
|
if (presenter.isDownloading()) {
|
||||||
fab.scaleY = 1f
|
fab.scaleY = 1f
|
||||||
fab.scaleX = 1f
|
fab.scaleX = 1f
|
||||||
@ -219,9 +347,10 @@ class LibraryController(
|
|||||||
}
|
}
|
||||||
presenter.onRestore()
|
presenter.onRestore()
|
||||||
val library = presenter.getAllManga()
|
val library = presenter.getAllManga()
|
||||||
if (library != null) onNextLibraryUpdate(presenter.categories, library)
|
if (library != null) presenter.updateViewBlocking() //onNextLibraryUpdate(presenter.categories, library)
|
||||||
else {
|
else {
|
||||||
library_pager.alpha = 0f
|
library_pager.alpha = 0f
|
||||||
|
swipe_refresh.alpha = 0f
|
||||||
presenter.getLibraryBlocking()
|
presenter.getLibraryBlocking()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -229,12 +358,17 @@ class LibraryController(
|
|||||||
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) {
|
||||||
activity?.tabs?.setupWithViewPager(library_pager)
|
if (usePager)
|
||||||
|
(activity as MainActivity).supportActionBar?.setDisplayShowCustomEnabled(true)
|
||||||
|
//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).supportActionBar?.setDisplayShowCustomEnabled(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityResumed(activity: Activity) {
|
override fun onActivityResumed(activity: Activity) {
|
||||||
@ -251,18 +385,19 @@ class LibraryController(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
|
(activity as MainActivity).supportActionBar?.setDisplayShowCustomEnabled(false)
|
||||||
presenter.onDestroy()
|
presenter.onDestroy()
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView(view: View) {
|
override fun onDestroyView(view: View) {
|
||||||
adapter?.onDestroy()
|
//adapter.onDestroy()
|
||||||
DownloadService.removeListener(this)
|
DownloadService.removeListener(this)
|
||||||
LibraryUpdateService.removeListener()
|
LibraryUpdateService.removeListener()
|
||||||
adapter = null
|
//adapter = null
|
||||||
actionMode = null
|
actionMode = null
|
||||||
tabsVisibilitySubscription?.unsubscribe()
|
//tabsVisibilitySubscription?.unsubscribe()
|
||||||
tabsVisibilitySubscription = null
|
//tabsVisibilitySubscription = null
|
||||||
super.onDestroyView(view)
|
super.onDestroyView(view)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,7 +423,7 @@ class LibraryController(
|
|||||||
super.onDetach(view)
|
super.onDetach(view)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun configureTabs(tabs: TabLayout) {
|
/*override fun configureTabs(tabs: TabLayout) {
|
||||||
with(tabs) {
|
with(tabs) {
|
||||||
tabGravity = TabLayout.GRAVITY_CENTER
|
tabGravity = TabLayout.GRAVITY_CENTER
|
||||||
tabMode = TabLayout.MODE_SCROLLABLE
|
tabMode = TabLayout.MODE_SCROLLABLE
|
||||||
@ -307,12 +442,53 @@ class LibraryController(
|
|||||||
override fun cleanupTabs(tabs: TabLayout) {
|
override fun cleanupTabs(tabs: TabLayout) {
|
||||||
tabsVisibilitySubscription?.unsubscribe()
|
tabsVisibilitySubscription?.unsubscribe()
|
||||||
tabsVisibilitySubscription = null
|
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)
|
||||||
|
|
||||||
|
val position = if (freshStart) adapter.indexOf(activeCategory) + 1 else null
|
||||||
|
|
||||||
|
spinner.onItemSelectedListener = null
|
||||||
|
spinnerAdapter = SpinnerAdapter(view!!.context, R.layout.library_spinner_textview,
|
||||||
|
presenter.categories.map { it.name }.toTypedArray())
|
||||||
|
spinner.adapter = spinnerAdapter
|
||||||
|
|
||||||
|
|
||||||
|
spinnerAdapter?.setCustomText(presenter.categories.find { it.order == activeCategory
|
||||||
|
}?.name ?: resources?.getString(R.string.label_library))
|
||||||
|
if (!freshStart) {
|
||||||
|
spinnerAdapter?.setCustomText(presenter.categories.find { it.order == activeCategory
|
||||||
|
}?.name ?: resources?.getString(R.string.label_library))
|
||||||
|
justStarted = false
|
||||||
|
if (swipe_refresh.alpha == 0f)
|
||||||
|
swipe_refresh.animate().alpha(1f).setDuration(500).start()
|
||||||
|
|
||||||
|
|
||||||
|
}else {
|
||||||
|
if (position != null)
|
||||||
|
(recycler.layoutManager as LinearLayoutManager)
|
||||||
|
.scrollToPositionWithOffset(position, 0)
|
||||||
|
}
|
||||||
|
spinner.onItemSelectedListener = IgnoreFirstSpinnerListener { pos ->
|
||||||
|
val headerPosition = adapter.indexOf(pos - 1) + 1
|
||||||
|
if (headerPosition > -1) {
|
||||||
|
(recycler.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(
|
||||||
|
headerPosition, 0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
val view = view ?: return
|
val view = view ?: return
|
||||||
val adapter = adapter ?: return
|
val adapter = pagerAdapter ?: return
|
||||||
|
|
||||||
// Show empty view if needed
|
// Show empty view if needed
|
||||||
if (mangaMap.isNotEmpty()) {
|
if (mangaMap.isNotEmpty()) {
|
||||||
@ -342,13 +518,13 @@ class LibraryController(
|
|||||||
bottom_sheet.lastCategory = adapter.categories.getOrNull(activeCat)
|
bottom_sheet.lastCategory = adapter.categories.getOrNull(activeCat)
|
||||||
bottom_sheet.updateTitle()
|
bottom_sheet.updateTitle()
|
||||||
|
|
||||||
tabsVisibilityRelay.call(categories.size > 1)
|
//tabsVisibilityRelay.call(categories.size > 1)
|
||||||
|
|
||||||
if (freshStart || !justStarted) {
|
if (freshStart || !justStarted) {
|
||||||
// Delay the scroll position to allow the view to be properly measured.
|
// Delay the scroll position to allow the view to be properly measured.
|
||||||
view.post {
|
view.post {
|
||||||
if (isAttached) {
|
if (isAttached) {
|
||||||
activity?.tabs?.setScrollPosition(library_pager.currentItem, 0f, true)
|
//activity?.tabs?.setScrollPosition(library_pager.currentItem, 0f, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -398,22 +574,50 @@ class LibraryController(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun onCatSortChanged(id: Int? = null) {
|
fun onCatSortChanged(id: Int? = null) {
|
||||||
val catId = id ?: adapter?.categories?.getOrNull(library_pager.currentItem)?.id ?: return
|
val catId = id ?: presenter.categories.find { it.order == activeCategory }?.id ?: return
|
||||||
presenter.requestCatSortUpdate(catId)
|
presenter.requestCatSortUpdate(catId)
|
||||||
|
//val catId = id ?: adapter?.categories?.getOrNull(library_pager.currentItem)?.id ?: return
|
||||||
|
// presenter.requestCatSortUpdate(catId)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reattaches the adapter to the view pager to recreate fragments
|
* Reattaches the adapter to the view pager to recreate fragments
|
||||||
*/
|
*/
|
||||||
private fun reattachAdapter() {
|
private fun reattachAdapter() {
|
||||||
val adapter = adapter ?: return
|
val position = (recycler.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition()
|
||||||
|
if (recycler is AutofitRecyclerView && preferences.libraryLayout().getOrDefault() == 0 ||
|
||||||
|
recycler !is AutofitRecyclerView && preferences.libraryLayout().getOrDefault() > 0) {
|
||||||
|
recycler_layout.removeView(recycler)
|
||||||
|
recycler = if (preferences.libraryLayout().getOrDefault() == 0) {
|
||||||
|
(swipe_refresh.inflate(R.layout.library_list_recycler) as RecyclerView).apply {
|
||||||
|
layoutManager = LinearLayoutManager(context)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
(swipe_refresh.inflate(R.layout.library_grid_recycler) as AutofitRecyclerView).apply {
|
||||||
|
spanCount = mangaPerRow
|
||||||
|
manager.spanSizeLookup = (object : GridLayoutManager.SpanSizeLookup() {
|
||||||
|
override fun getSpanSize(position: Int): Int {
|
||||||
|
val item = this@LibraryController.adapter.getItem(position)
|
||||||
|
return if (item is LibraryHeaderItem)
|
||||||
|
manager.spanCount else 1
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
recycler.setHasFixedSize(true)
|
||||||
|
recycler.addOnScrollListener(scrollLister)
|
||||||
|
recycler_layout.addView(recycler)
|
||||||
|
}
|
||||||
|
recycler.adapter = adapter
|
||||||
|
(recycler.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(position, 0)
|
||||||
|
//val adapter = adapter ?: 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*/
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -533,8 +737,8 @@ class LibraryController(
|
|||||||
mode.title = resources?.getString(R.string.label_selected, count)
|
mode.title = resources?.getString(R.string.label_selected, count)
|
||||||
if (preferences.librarySortingMode().getOrDefault() == LibrarySort.DRAG_AND_DROP) {
|
if (preferences.librarySortingMode().getOrDefault() == LibrarySort.DRAG_AND_DROP) {
|
||||||
val catId = (selectedMangas.first() as? LibraryManga)?.category
|
val catId = (selectedMangas.first() as? LibraryManga)?.category
|
||||||
val sameCat = (adapter?.categories?.getOrNull(library_pager.currentItem)?.id
|
val sameCat = /*(adapter?.categories?.getOrNull(library_pager.currentItem)?.id
|
||||||
== catId) && selectedMangas.all { (it as? LibraryManga)?.category == catId }
|
== catId) &&*/ selectedMangas.all { (it as? LibraryManga)?.category == catId }
|
||||||
menu.findItem(R.id.action_move_manga).isVisible = sameCat
|
menu.findItem(R.id.action_move_manga).isVisible = sameCat
|
||||||
}
|
}
|
||||||
else menu.findItem(R.id.action_move_manga).isVisible = false
|
else menu.findItem(R.id.action_move_manga).isVisible = false
|
||||||
@ -554,11 +758,11 @@ class LibraryController(
|
|||||||
.negativeButton(android.R.string.no)
|
.negativeButton(android.R.string.no)
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
R.id.action_select_all -> {
|
/*R.id.action_select_all -> {
|
||||||
adapter?.categories?.getOrNull(library_pager.currentItem)?.id?.let {
|
adapter?.categories?.getOrNull(library_pager.currentItem)?.id?.let {
|
||||||
selectAllRelay.call(it)
|
selectAllRelay.call(it)
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
R.id.action_migrate -> {
|
R.id.action_migrate -> {
|
||||||
router.pushController(
|
router.pushController(
|
||||||
if (preferences.skipPreMigration().getOrDefault()) {
|
if (preferences.skipPreMigration().getOrDefault()) {
|
||||||
@ -573,12 +777,12 @@ class LibraryController(
|
|||||||
.withFadeTransaction())
|
.withFadeTransaction())
|
||||||
destroyActionModeIfNeeded()
|
destroyActionModeIfNeeded()
|
||||||
}
|
}
|
||||||
R.id.action_to_top, R.id.action_to_bottom -> {
|
/*R.id.action_to_top, R.id.action_to_bottom -> {
|
||||||
adapter?.categories?.getOrNull(library_pager.currentItem)?.id?.let {
|
adapter?.categories?.getOrNull(library_pager.currentItem)?.id?.let {
|
||||||
reorganizeRelay.call(it to if (item.itemId == R.id.action_to_top) -1 else -2)
|
reorganizeRelay.call(it to if (item.itemId == R.id.action_to_top) -1 else -2)
|
||||||
}
|
}
|
||||||
destroyActionModeIfNeeded()
|
destroyActionModeIfNeeded()
|
||||||
}
|
}*/
|
||||||
else -> return false
|
else -> return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@ -669,6 +873,19 @@ class LibraryController(
|
|||||||
destroyActionModeIfNeeded()
|
destroyActionModeIfNeeded()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun startReading(position: Int) {
|
||||||
|
val activity = activity ?: return
|
||||||
|
val manga = (adapter.getItem(position) as? LibraryItem)?.manga ?: return
|
||||||
|
val chapter = presenter.getFirstUnread(manga) ?: return
|
||||||
|
val intent = ReaderActivity.newIntent(activity, manga, chapter)
|
||||||
|
destroyActionModeIfNeeded()
|
||||||
|
startActivity(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onItemReleased(position: Int) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
fun startReading(manga: Manga) {
|
fun startReading(manga: Manga) {
|
||||||
val activity = activity ?: return
|
val activity = activity ?: return
|
||||||
val chapter = presenter.getFirstUnread(manga) ?: return
|
val chapter = presenter.getFirstUnread(manga) ?: return
|
||||||
@ -676,6 +893,94 @@ class LibraryController(
|
|||||||
destroyActionModeIfNeeded()
|
destroyActionModeIfNeeded()
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun canDrag(): Boolean {
|
||||||
|
val sortingMode = preferences.librarySortingMode().getOrDefault()
|
||||||
|
val filterOff = preferences.filterCompleted().getOrDefault() +
|
||||||
|
preferences.filterTracked().getOrDefault() +
|
||||||
|
preferences.filterUnread().getOrDefault() +
|
||||||
|
preferences.filterCompleted().getOrDefault() == 0 &&
|
||||||
|
!preferences.hideCategories().getOrDefault()
|
||||||
|
return sortingMode == LibrarySort.DRAG_AND_DROP && 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, 0f)
|
||||||
|
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) ?: return
|
||||||
|
|
||||||
|
setSelection((item as LibraryItem).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) ?: return
|
||||||
|
|
||||||
|
setSelection((item as LibraryItem).manga, true)
|
||||||
|
invalidateActionMode()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onItemMove(fromPosition: Int, toPosition: Int) { }
|
||||||
|
|
||||||
|
override fun shouldMoveItem(fromPosition: Int, toPosition: Int): Boolean {
|
||||||
|
if (adapter.selectedItemCount > 1)
|
||||||
|
return false
|
||||||
|
if (adapter.isSelected(fromPosition))
|
||||||
|
toggleSelection(fromPosition)
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object HeightTopWindowInsetsListener : View.OnApplyWindowInsetsListener {
|
object HeightTopWindowInsetsListener : View.OnApplyWindowInsetsListener {
|
||||||
@ -689,3 +994,29 @@ object HeightTopWindowInsetsListener : View.OnApplyWindowInsetsListener {
|
|||||||
return insets
|
return insets
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SpinnerAdapter(context: Context, layoutId: Int, val array: Array<String>) :
|
||||||
|
ArrayAdapter<String>
|
||||||
|
(context, layoutId, array) {
|
||||||
|
private var mCustomText = ""
|
||||||
|
|
||||||
|
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||||
|
val view = super.getView(position, convertView, parent)
|
||||||
|
val tv: TextView = view as TextView
|
||||||
|
tv.text = mCustomText
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setCustomText(customText: String?) {
|
||||||
|
// Call to set the text that must be shown in the spinner for the custom option.
|
||||||
|
val text = customText ?: return
|
||||||
|
mCustomText = text
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||||
|
val view = parent.inflate(R.layout.library_spinner_entry_text) as TextView
|
||||||
|
view.text = array[position]
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
}
|
@ -7,13 +7,11 @@ import android.widget.FrameLayout
|
|||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||||
import com.bumptech.glide.signature.ObjectKey
|
import com.bumptech.glide.signature.ObjectKey
|
||||||
import eu.kanade.tachiyomi.R
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.MangaImpl
|
import eu.kanade.tachiyomi.data.database.models.MangaImpl
|
||||||
import eu.kanade.tachiyomi.data.glide.GlideApp
|
import eu.kanade.tachiyomi.data.glide.GlideApp
|
||||||
import eu.kanade.tachiyomi.source.LocalSource
|
import eu.kanade.tachiyomi.source.LocalSource
|
||||||
import eu.kanade.tachiyomi.util.system.dpToPx
|
import eu.kanade.tachiyomi.util.system.dpToPx
|
||||||
import eu.kanade.tachiyomi.util.view.gone
|
import eu.kanade.tachiyomi.util.view.gone
|
||||||
import eu.kanade.tachiyomi.util.view.updatePaddingRelative
|
|
||||||
import eu.kanade.tachiyomi.util.view.visible
|
import eu.kanade.tachiyomi.util.view.visible
|
||||||
import kotlinx.android.synthetic.main.catalogue_grid_item.*
|
import kotlinx.android.synthetic.main.catalogue_grid_item.*
|
||||||
import kotlinx.android.synthetic.main.unread_download_badge.*
|
import kotlinx.android.synthetic.main.unread_download_badge.*
|
||||||
|
@ -0,0 +1,64 @@
|
|||||||
|
package eu.kanade.tachiyomi.ui.library
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||||
|
import eu.davidea.flexibleadapter.items.AbstractHeaderItem
|
||||||
|
import eu.davidea.flexibleadapter.items.IFlexible
|
||||||
|
import eu.davidea.viewholders.FlexibleViewHolder
|
||||||
|
import eu.kanade.tachiyomi.R
|
||||||
|
import eu.kanade.tachiyomi.data.database.models.Category
|
||||||
|
|
||||||
|
class LibraryHeaderItem(val category: Category) : AbstractHeaderItem<LibraryHeaderItem.Holder>() {
|
||||||
|
|
||||||
|
override fun getLayoutRes(): Int {
|
||||||
|
return R.layout.library_category_header_item
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createViewHolder(
|
||||||
|
view: View,
|
||||||
|
adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>
|
||||||
|
): Holder {
|
||||||
|
return Holder(view, adapter)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun bindViewHolder(
|
||||||
|
adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>,
|
||||||
|
holder: Holder,
|
||||||
|
position: Int,
|
||||||
|
payloads: MutableList<Any?>?
|
||||||
|
) {
|
||||||
|
holder.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (other is LibraryHeaderItem) {
|
||||||
|
return category.id == other.category.id
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isDraggable(): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isSelectable(): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return category.id!!
|
||||||
|
}
|
||||||
|
|
||||||
|
class Holder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>) :
|
||||||
|
FlexibleViewHolder(view, adapter, true) {
|
||||||
|
|
||||||
|
private val sectionText: TextView = view.findViewById(R.id.category_title)
|
||||||
|
|
||||||
|
fun bind(item: LibraryHeaderItem) {
|
||||||
|
sectionText.text = item.category.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -37,8 +37,4 @@ abstract class LibraryHolder(
|
|||||||
super.onItemReleased(position)
|
super.onItemReleased(position)
|
||||||
(adapter as? LibraryCategoryAdapter)?.libraryListener?.onItemReleased(position)
|
(adapter as? LibraryCategoryAdapter)?.libraryListener?.onItemReleased(position)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun convertColor(color: Int):String {
|
|
||||||
return Integer.toHexString(color and 0x00ffffff)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ import androidx.core.content.ContextCompat
|
|||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.f2prateek.rx.preferences.Preference
|
import com.f2prateek.rx.preferences.Preference
|
||||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
import eu.davidea.flexibleadapter.items.AbstractSectionableItem
|
||||||
import eu.davidea.flexibleadapter.items.IFilterable
|
import eu.davidea.flexibleadapter.items.IFilterable
|
||||||
import eu.davidea.flexibleadapter.items.IFlexible
|
import eu.davidea.flexibleadapter.items.IFlexible
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
@ -21,8 +21,10 @@ import eu.kanade.tachiyomi.widget.AutofitRecyclerView
|
|||||||
import kotlinx.android.synthetic.main.catalogue_grid_item.view.*
|
import kotlinx.android.synthetic.main.catalogue_grid_item.view.*
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
|
||||||
class LibraryItem(val manga: LibraryManga, private val libraryLayout: Preference<Int>) :
|
class LibraryItem(val manga: LibraryManga,
|
||||||
AbstractFlexibleItem<LibraryHolder>(), IFilterable<String> {
|
private val libraryLayout: Preference<Int>,
|
||||||
|
header: LibraryHeaderItem) :
|
||||||
|
AbstractSectionableItem<LibraryHolder, LibraryHeaderItem>(header), IFilterable<String> {
|
||||||
|
|
||||||
var downloadCount = -1
|
var downloadCount = -1
|
||||||
var unreadType = 1
|
var unreadType = 1
|
||||||
@ -51,7 +53,8 @@ class LibraryItem(val manga: LibraryManga, private val libraryLayout: Preference
|
|||||||
cover_thumbnail.maxHeight = Integer.MAX_VALUE
|
cover_thumbnail.maxHeight = Integer.MAX_VALUE
|
||||||
constraint_layout.minHeight = 0
|
constraint_layout.minHeight = 0
|
||||||
cover_thumbnail.adjustViewBounds = false
|
cover_thumbnail.adjustViewBounds = false
|
||||||
cover_thumbnail.layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, coverHeight)
|
cover_thumbnail.layoutParams =
|
||||||
|
FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, coverHeight)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
constraint_layout.minHeight = coverHeight
|
constraint_layout.minHeight = coverHeight
|
||||||
|
@ -17,7 +17,6 @@ import eu.kanade.tachiyomi.source.SourceManager
|
|||||||
import eu.kanade.tachiyomi.source.model.SChapter
|
import eu.kanade.tachiyomi.source.model.SChapter
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
import eu.kanade.tachiyomi.ui.manga.chapter.ChapterItem
|
|
||||||
import eu.kanade.tachiyomi.ui.migration.MigrationFlags
|
import eu.kanade.tachiyomi.ui.migration.MigrationFlags
|
||||||
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
|
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
|
||||||
import eu.kanade.tachiyomi.util.lang.removeArticles
|
import eu.kanade.tachiyomi.util.lang.removeArticles
|
||||||
@ -29,20 +28,16 @@ import eu.kanade.tachiyomi.widget.ExtendedNavigationView.Item.TriStateGroup.Comp
|
|||||||
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.delay
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import rx.Subscription
|
|
||||||
import rx.android.schedulers.AndroidSchedulers
|
import rx.android.schedulers.AndroidSchedulers
|
||||||
import rx.schedulers.Schedulers
|
import rx.schedulers.Schedulers
|
||||||
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.ArrayList
|
import java.util.ArrayList
|
||||||
import java.util.Calendar
|
|
||||||
import java.util.Collections
|
import java.util.Collections
|
||||||
import java.util.Comparator
|
import java.util.Comparator
|
||||||
import java.util.Date
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class containing library information.
|
* Class containing library information.
|
||||||
@ -109,7 +104,7 @@ class LibraryPresenter(
|
|||||||
mangaMap
|
mangaMap
|
||||||
}
|
}
|
||||||
currentMangaMap = mangaMap
|
currentMangaMap = mangaMap
|
||||||
view.onNextLibraryUpdate(categories, mangaMap)
|
updateView(categories, mangaMap)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,7 +119,9 @@ class LibraryPresenter(
|
|||||||
mangaMap
|
mangaMap
|
||||||
}()
|
}()
|
||||||
currentMangaMap = mangaMap
|
currentMangaMap = mangaMap
|
||||||
view.onNextLibraryUpdate(categories, mangaMap, true)
|
launchUI {
|
||||||
|
updateView(categories, mangaMap, true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAllManga(): LibraryMap? {
|
fun getAllManga(): LibraryMap? {
|
||||||
@ -314,7 +311,6 @@ class LibraryPresenter(
|
|||||||
}
|
}
|
||||||
val catListing by lazy {
|
val catListing by lazy {
|
||||||
val default = createDefaultCategory()
|
val default = createDefaultCategory()
|
||||||
default.order = -1
|
|
||||||
listOf(default) + db.getCategories().executeAsBlocking()
|
listOf(default) + db.getCategories().executeAsBlocking()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -423,10 +419,37 @@ class LibraryPresenter(
|
|||||||
var libraryManga = db.getLibraryMangas().executeAsBlocking()
|
var libraryManga = db.getLibraryMangas().executeAsBlocking()
|
||||||
if (!showCategories)
|
if (!showCategories)
|
||||||
libraryManga = libraryManga.distinctBy { it.id }
|
libraryManga = libraryManga.distinctBy { it.id }
|
||||||
val libraryMap = libraryManga.map { manga ->
|
/*val libraryMap = libraryManga.map { manga ->
|
||||||
LibraryItem(manga, libraryLayout).apply { unreadType = unreadBadgeType }
|
LibraryItem(manga, libraryLayout).apply { unreadType = unreadBadgeType }
|
||||||
}.groupBy {
|
}.groupBy {
|
||||||
if (showCategories) it.manga.category else 0
|
if (showCategories) it.manga.category else 0
|
||||||
|
}*/
|
||||||
|
val catItemMain = LibraryHeaderItem(categories.firstOrNull() ?: createDefaultCategory())
|
||||||
|
val libraryMap =
|
||||||
|
if (preferences.libraryUsingPager().getOrDefault()) {
|
||||||
|
libraryManga.map { manga ->
|
||||||
|
LibraryItem(manga, libraryLayout, catItemMain).apply { unreadType = unreadBadgeType }
|
||||||
|
}.groupBy {
|
||||||
|
if (showCategories) it.manga.category else 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
libraryManga.groupBy { manga ->
|
||||||
|
if (showCategories) manga.category else 0
|
||||||
|
//LibraryItem(manga, libraryLayout).apply { unreadType = unreadBadgeType }
|
||||||
|
}.map { entry ->
|
||||||
|
val categoryItem = LibraryHeaderItem(categories.find { entry.key == it.id }
|
||||||
|
?: createDefaultCategory())
|
||||||
|
entry.value.map {
|
||||||
|
LibraryItem(
|
||||||
|
it, libraryLayout, categoryItem
|
||||||
|
).apply { unreadType = unreadBadgeType }
|
||||||
|
}
|
||||||
|
}.map {
|
||||||
|
val cat = if (showCategories) it.firstOrNull()?.manga?.category ?: 0 else 0
|
||||||
|
cat to it
|
||||||
|
//LibraryItem(manga, libraryLayout).apply { unreadType = unreadBadgeType }
|
||||||
|
}.toMap()
|
||||||
}
|
}
|
||||||
if (libraryMap.containsKey(0))
|
if (libraryMap.containsKey(0))
|
||||||
categories.add(0, createDefaultCategory())
|
categories.add(0, createDefaultCategory())
|
||||||
@ -441,6 +464,7 @@ class LibraryPresenter(
|
|||||||
|
|
||||||
private fun createDefaultCategory(): Category {
|
private fun createDefaultCategory(): Category {
|
||||||
val default = Category.createDefault(context)
|
val default = Category.createDefault(context)
|
||||||
|
default.order = -1
|
||||||
val defOrder = preferences.defaultMangaOrder().getOrDefault()
|
val defOrder = preferences.defaultMangaOrder().getOrDefault()
|
||||||
if (defOrder.firstOrNull()?.isLetter() == true) default.mangaSort = defOrder.first()
|
if (defOrder.firstOrNull()?.isLetter() == true) default.mangaSort = defOrder.first()
|
||||||
else default.mangaOrder = defOrder.split("/").mapNotNull { it.toLongOrNull() }
|
else default.mangaOrder = defOrder.split("/").mapNotNull { it.toLongOrNull() }
|
||||||
@ -456,10 +480,61 @@ class LibraryPresenter(
|
|||||||
mangaMap = withContext(Dispatchers.IO) { applyFilters(mangaMap) }
|
mangaMap = withContext(Dispatchers.IO) { applyFilters(mangaMap) }
|
||||||
mangaMap = withContext(Dispatchers.IO) { applySort(mangaMap) }
|
mangaMap = withContext(Dispatchers.IO) { applySort(mangaMap) }
|
||||||
currentMangaMap = mangaMap
|
currentMangaMap = mangaMap
|
||||||
view.onNextLibraryUpdate(categories, mangaMap)
|
updateView(categories, mangaMap)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun updateView(categories: List<Category>, mangaMap: LibraryMap, freshStart:Boolean
|
||||||
|
= false) {
|
||||||
|
/* val list = withContext(Dispatchers.IO) {
|
||||||
|
val showCategories = !preferences.hideCategories().getOrDefault()
|
||||||
|
val current = mangaMap.values.first()
|
||||||
|
current.groupBy {
|
||||||
|
if (showCategories) it.manga.category else 0
|
||||||
|
}.flatMap { it.value }
|
||||||
|
}*/
|
||||||
|
if (preferences.libraryUsingPager().getOrDefault()) {
|
||||||
|
view.onNextLibraryUpdate(categories, mangaMap, true)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val mangaList = withContext(Dispatchers.IO) {
|
||||||
|
val list = mutableListOf<LibraryItem>()
|
||||||
|
val many = categories.size > 1
|
||||||
|
for (element in mangaMap.toSortedMap(compareBy { entry ->
|
||||||
|
categories.find { it.id == entry }?.order ?: -1
|
||||||
|
})) {
|
||||||
|
list.addAll(element.value)
|
||||||
|
}
|
||||||
|
list
|
||||||
|
}
|
||||||
|
view.onNextLibraryUpdate(mangaList, freshStart)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateViewBlocking() {
|
||||||
|
/* val list = withContext(Dispatchers.IO) {
|
||||||
|
val showCategories = !preferences.hideCategories().getOrDefault()
|
||||||
|
val current = mangaMap.values.first()
|
||||||
|
current.groupBy {
|
||||||
|
if (showCategories) it.manga.category else 0
|
||||||
|
}.flatMap { it.value }
|
||||||
|
}*/
|
||||||
|
val mangaMap = currentMangaMap ?: return
|
||||||
|
if (preferences.libraryUsingPager().getOrDefault()) {
|
||||||
|
view.onNextLibraryUpdate(categories, mangaMap, true)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val list = mutableListOf<LibraryItem>()
|
||||||
|
for (element in mangaMap?.toSortedMap(compareBy { entry ->
|
||||||
|
categories.find { it.id == entry }?.order ?: -1
|
||||||
|
})) {
|
||||||
|
list.addAll(element.value)
|
||||||
|
}
|
||||||
|
view.onNextLibraryUpdate(list, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requests the library to have download badges added/removed.
|
* Requests the library to have download badges added/removed.
|
||||||
*/
|
*/
|
||||||
@ -471,7 +546,7 @@ class LibraryPresenter(
|
|||||||
val current = currentMangaMap ?: return@launchUI
|
val current = currentMangaMap ?: return@launchUI
|
||||||
withContext(Dispatchers.IO) { setDownloadCount(current) }
|
withContext(Dispatchers.IO) { setDownloadCount(current) }
|
||||||
currentMangaMap = current
|
currentMangaMap = current
|
||||||
view.onNextLibraryUpdate(categories, current)
|
updateView(categories, current)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -487,7 +562,7 @@ class LibraryPresenter(
|
|||||||
val current = currentMangaMap ?: return@launchUI
|
val current = currentMangaMap ?: return@launchUI
|
||||||
withContext(Dispatchers.IO) { setUnreadBadge(current) }
|
withContext(Dispatchers.IO) { setUnreadBadge(current) }
|
||||||
currentMangaMap = current
|
currentMangaMap = current
|
||||||
view.onNextLibraryUpdate(categories, current)
|
updateView(categories, current)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -499,7 +574,7 @@ class LibraryPresenter(
|
|||||||
var mangaMap = currentMangaMap ?: return@launchUI
|
var mangaMap = currentMangaMap ?: return@launchUI
|
||||||
mangaMap = withContext(Dispatchers.IO) { applySort(mangaMap) }
|
mangaMap = withContext(Dispatchers.IO) { applySort(mangaMap) }
|
||||||
currentMangaMap = mangaMap
|
currentMangaMap = mangaMap
|
||||||
view.onNextLibraryUpdate(categories, mangaMap)
|
updateView(categories, mangaMap)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -508,7 +583,7 @@ class LibraryPresenter(
|
|||||||
var mangaMap = currentMangaMap ?: return@launchUI
|
var mangaMap = currentMangaMap ?: return@launchUI
|
||||||
mangaMap = withContext(Dispatchers.IO) { applyCatSort(mangaMap, catId) }
|
mangaMap = withContext(Dispatchers.IO) { applyCatSort(mangaMap, catId) }
|
||||||
currentMangaMap = mangaMap
|
currentMangaMap = mangaMap
|
||||||
view.onNextLibraryUpdate(categories, mangaMap)
|
updateView(categories, mangaMap)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -698,5 +773,6 @@ class LibraryPresenter(
|
|||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
var currentLibrary:Library? = null
|
var currentLibrary:Library? = null
|
||||||
|
var currentList:List<LibraryItem>? = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package eu.kanade.tachiyomi.ui.main
|
package eu.kanade.tachiyomi.ui.main
|
||||||
|
|
||||||
import android.app.SearchManager
|
import android.app.SearchManager
|
||||||
import android.content.ComponentCallbacks2
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
@ -48,7 +47,6 @@ 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.LibraryPresenter
|
|
||||||
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
|
||||||
@ -58,7 +56,6 @@ import eu.kanade.tachiyomi.util.system.launchUI
|
|||||||
import eu.kanade.tachiyomi.util.view.gone
|
import eu.kanade.tachiyomi.util.view.gone
|
||||||
import eu.kanade.tachiyomi.util.view.updateLayoutParams
|
import eu.kanade.tachiyomi.util.view.updateLayoutParams
|
||||||
import eu.kanade.tachiyomi.util.view.updatePadding
|
import eu.kanade.tachiyomi.util.view.updatePadding
|
||||||
import eu.kanade.tachiyomi.util.view.updatePaddingRelative
|
|
||||||
import eu.kanade.tachiyomi.util.view.visible
|
import eu.kanade.tachiyomi.util.view.visible
|
||||||
import kotlinx.android.synthetic.main.main_activity.*
|
import kotlinx.android.synthetic.main.main_activity.*
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@ -186,6 +183,8 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
supportActionBar?.setDisplayShowCustomEnabled(true)
|
||||||
|
|
||||||
content.setOnApplyWindowInsetsListener { v, insets ->
|
content.setOnApplyWindowInsetsListener { v, insets ->
|
||||||
// if device doesn't support light nav bar
|
// if device doesn't support light nav bar
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||||
@ -279,6 +278,10 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
|
|||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (router.backstackSize <= 1) {
|
||||||
|
tabAnimator.hide()
|
||||||
|
}
|
||||||
|
|
||||||
syncActivityViewWithController(router.backstack.lastOrNull()?.controller())
|
syncActivityViewWithController(router.backstack.lastOrNull()?.controller())
|
||||||
|
|
||||||
if (savedInstanceState == null) {
|
if (savedInstanceState == null) {
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package eu.kanade.tachiyomi.ui.main
|
package eu.kanade.tachiyomi.ui.main
|
||||||
|
|
||||||
import android.animation.ObjectAnimator
|
import android.animation.ObjectAnimator
|
||||||
import com.google.android.material.tabs.TabLayout
|
|
||||||
import android.view.ViewTreeObserver
|
import android.view.ViewTreeObserver
|
||||||
import android.view.animation.DecelerateInterpolator
|
import android.view.animation.DecelerateInterpolator
|
||||||
|
import com.google.android.material.tabs.TabLayout
|
||||||
|
|
||||||
class TabsAnimator(val tabs: TabLayout) {
|
class TabsAnimator(val tabs: TabLayout) {
|
||||||
|
|
||||||
@ -97,6 +97,11 @@ class TabsAnimator(val tabs: TabLayout) {
|
|||||||
isLastStateShown = false
|
isLastStateShown = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun hide() {
|
||||||
|
setHeight(0)
|
||||||
|
isLastStateShown = false
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the tab layout has a known height.
|
* Returns whether the tab layout has a known height.
|
||||||
*/
|
*/
|
||||||
|
@ -52,6 +52,13 @@ class SettingsLibraryController : SettingsController() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switchPreference {
|
||||||
|
key = Keys.libraryUsingPager
|
||||||
|
titleRes = R.string.pref_remove_articles
|
||||||
|
summaryRes = R.string.pref_remove_articles_summary
|
||||||
|
defaultValue = false
|
||||||
|
}
|
||||||
|
|
||||||
switchPreference {
|
switchPreference {
|
||||||
key = Keys.removeArticles
|
key = Keys.removeArticles
|
||||||
titleRes = R.string.pref_remove_articles
|
titleRes = R.string.pref_remove_articles
|
||||||
|
@ -3,13 +3,12 @@ package eu.kanade.tachiyomi.widget
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
|
||||||
class AutofitRecyclerView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
|
class AutofitRecyclerView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
|
||||||
androidx.recyclerview.widget.RecyclerView(context, attrs) {
|
androidx.recyclerview.widget.RecyclerView(context, attrs) {
|
||||||
|
|
||||||
private val manager = GridLayoutManager(context, 1)
|
val manager = GridLayoutManager(context, 1)
|
||||||
|
|
||||||
private var columnWidth = -1
|
private var columnWidth = -1
|
||||||
|
|
||||||
|
@ -57,8 +57,8 @@
|
|||||||
android:layout_height="30dp"
|
android:layout_height="30dp"
|
||||||
android:layout_gravity="end"
|
android:layout_gravity="end"
|
||||||
android:layout_marginTop="6dp"
|
android:layout_marginTop="6dp"
|
||||||
android:layout_marginBottom="6dp"
|
|
||||||
android:layout_marginEnd="6dp"
|
android:layout_marginEnd="6dp"
|
||||||
|
android:layout_marginBottom="6dp"
|
||||||
android:background="@drawable/round_play_background"
|
android:background="@drawable/round_play_background"
|
||||||
android:contentDescription="@string/start_reading"
|
android:contentDescription="@string/start_reading"
|
||||||
android:padding="6dp"
|
android:padding="6dp"
|
||||||
@ -109,11 +109,12 @@
|
|||||||
android:layout_height="10dp"
|
android:layout_height="10dp"
|
||||||
app:layout_constraintTop_toTopOf="@+id/card" />
|
app:layout_constraintTop_toTopOf="@+id/card" />
|
||||||
|
|
||||||
<include layout="@layout/unread_download_badge"
|
<include
|
||||||
|
layout="@layout/unread_download_badge"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintBottom_toBottomOf="@id/badge_guide"
|
||||||
app:layout_constraintBottom_toBottomOf="@id/badge_guide"/>
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
|
||||||
<com.google.android.material.textview.MaterialTextView
|
<com.google.android.material.textview.MaterialTextView
|
||||||
android:id="@+id/title"
|
android:id="@+id/title"
|
||||||
@ -138,9 +139,9 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginTop="-1dp"
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
android:layout_marginBottom="8dp"
|
android:layout_marginBottom="8dp"
|
||||||
android:layout_marginTop="-1dp"
|
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
27
app/src/main/res/layout/library_category_header_item.xml
Normal file
27
app/src/main/res/layout/library_category_header_item.xml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingTop="15dp"
|
||||||
|
android:paddingBottom="5dp"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/category_title"
|
||||||
|
style="@style/TextAppearance.MaterialComponents.Headline5"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center|start"
|
||||||
|
android:inputType="none"
|
||||||
|
android:layout_marginStart="10dp"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textColor="?attr/actionBarTintColor"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:text="Title" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -2,11 +2,24 @@
|
|||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/library_layout"
|
android:id="@+id/library_layout"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
|
android:id="@+id/swipe_refresh"
|
||||||
|
android:layout_width="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.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||||
|
|
||||||
<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">
|
||||||
|
|
||||||
@ -16,6 +29,14 @@
|
|||||||
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:fastScrollerBubbleEnabled="true"
|
||||||
|
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"
|
||||||
|
5
app/src/main/res/layout/library_spinner.xml
Normal file
5
app/src/main/res/layout/library_spinner.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.appcompat.widget.AppCompatSpinner xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:entries="@array/color_filter_modes" />
|
13
app/src/main/res/layout/library_spinner_entry_text.xml
Normal file
13
app/src/main/res/layout/library_spinner_entry_text.xml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<TextView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
style="@style/TextAppearance.AppCompat.Widget.DropDownItem"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="start"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:padding="10dip"
|
||||||
|
tools:text="Title"
|
||||||
|
tools:background="?attr/colorPrimary"/>
|
11
app/src/main/res/layout/library_spinner_textview.xml
Normal file
11
app/src/main/res/layout/library_spinner_textview.xml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<TextView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="start"
|
||||||
|
android:textSize="20sp"
|
||||||
|
tools:text="Title"
|
||||||
|
tools:background="?attr/colorPrimary"/>
|
Loading…
x
Reference in New Issue
Block a user