mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-12-23 07:11:52 +01:00
Comfortable Grid (#3238)
* Comfortable Grid * Add requested changes * Add more requested changes
This commit is contained in:
parent
f05b99ec1f
commit
52e82b3548
@ -73,7 +73,7 @@ object PreferenceKeys {
|
|||||||
|
|
||||||
const val lastUsedCategory = "last_used_category"
|
const val lastUsedCategory = "last_used_category"
|
||||||
|
|
||||||
const val catalogueAsList = "pref_display_catalogue_as_list"
|
const val catalogueDisplayMode = "pref_display_catalogue_display_mode"
|
||||||
|
|
||||||
const val enabledLanguages = "source_languages"
|
const val enabledLanguages = "source_languages"
|
||||||
|
|
||||||
@ -131,7 +131,7 @@ object PreferenceKeys {
|
|||||||
|
|
||||||
const val downloadNewCategories = "download_new_categories"
|
const val downloadNewCategories = "download_new_categories"
|
||||||
|
|
||||||
const val libraryAsList = "pref_display_library_as_list"
|
const val libraryDisplayMode = "pref_display_library_display_mode"
|
||||||
|
|
||||||
const val lang = "app_language"
|
const val lang = "app_language"
|
||||||
|
|
||||||
|
@ -15,4 +15,8 @@ object PreferenceValues {
|
|||||||
const val THEME_DARK_DEFAULT = "default"
|
const val THEME_DARK_DEFAULT = "default"
|
||||||
const val THEME_DARK_BLUE = "blue"
|
const val THEME_DARK_BLUE = "blue"
|
||||||
const val THEME_DARK_AMOLED = "amoled"
|
const val THEME_DARK_AMOLED = "amoled"
|
||||||
|
|
||||||
|
const val DISPLAY_COMPACT_GRID = 0
|
||||||
|
const val DISPLAY_LIST = 1
|
||||||
|
const val DISPLAY_COMFORTABLE_GRID = 2
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import androidx.preference.PreferenceManager
|
|||||||
import com.tfcporciuncula.flow.FlowSharedPreferences
|
import com.tfcporciuncula.flow.FlowSharedPreferences
|
||||||
import com.tfcporciuncula.flow.Preference
|
import com.tfcporciuncula.flow.Preference
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
|
import eu.kanade.tachiyomi.data.preference.PreferenceValues.DISPLAY_COMPACT_GRID
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys
|
import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferenceValues as Values
|
import eu.kanade.tachiyomi.data.preference.PreferenceValues as Values
|
||||||
import eu.kanade.tachiyomi.data.track.TrackService
|
import eu.kanade.tachiyomi.data.track.TrackService
|
||||||
@ -138,7 +139,7 @@ class PreferencesHelper(val context: Context) {
|
|||||||
|
|
||||||
fun lastVersionCode() = flowPrefs.getInt("last_version_code", 0)
|
fun lastVersionCode() = flowPrefs.getInt("last_version_code", 0)
|
||||||
|
|
||||||
fun catalogueAsList() = flowPrefs.getBoolean(Keys.catalogueAsList, false)
|
fun catalogueDisplayMode() = flowPrefs.getInt(Keys.catalogueDisplayMode, DISPLAY_COMPACT_GRID)
|
||||||
|
|
||||||
fun enabledLanguages() = flowPrefs.getStringSet(Keys.enabledLanguages, setOf("en", Locale.getDefault().language))
|
fun enabledLanguages() = flowPrefs.getStringSet(Keys.enabledLanguages, setOf("en", Locale.getDefault().language))
|
||||||
|
|
||||||
@ -184,7 +185,7 @@ class PreferencesHelper(val context: Context) {
|
|||||||
|
|
||||||
fun libraryUpdatePrioritization() = flowPrefs.getInt(Keys.libraryUpdatePrioritization, 0)
|
fun libraryUpdatePrioritization() = flowPrefs.getInt(Keys.libraryUpdatePrioritization, 0)
|
||||||
|
|
||||||
fun libraryAsList() = flowPrefs.getBoolean(Keys.libraryAsList, false)
|
fun libraryDisplayMode() = flowPrefs.getInt(Keys.libraryDisplayMode, DISPLAY_COMPACT_GRID)
|
||||||
|
|
||||||
fun downloadBadge() = flowPrefs.getBoolean(Keys.downloadBadge, false)
|
fun downloadBadge() = flowPrefs.getBoolean(Keys.downloadBadge, false)
|
||||||
|
|
||||||
|
@ -22,6 +22,9 @@ import eu.davidea.flexibleadapter.items.IFlexible
|
|||||||
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.Manga
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
|
import eu.kanade.tachiyomi.data.preference.PreferenceValues.DISPLAY_COMFORTABLE_GRID
|
||||||
|
import eu.kanade.tachiyomi.data.preference.PreferenceValues.DISPLAY_COMPACT_GRID
|
||||||
|
import eu.kanade.tachiyomi.data.preference.PreferenceValues.DISPLAY_LIST
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.data.preference.asImmediateFlow
|
import eu.kanade.tachiyomi.data.preference.asImmediateFlow
|
||||||
import eu.kanade.tachiyomi.databinding.SourceControllerBinding
|
import eu.kanade.tachiyomi.databinding.SourceControllerBinding
|
||||||
@ -187,7 +190,7 @@ open class BrowseSourceController(bundle: Bundle) :
|
|||||||
binding.catalogueView.removeView(oldRecycler)
|
binding.catalogueView.removeView(oldRecycler)
|
||||||
}
|
}
|
||||||
|
|
||||||
val recycler = if (presenter.isListMode) {
|
val recycler = if (preferences.catalogueDisplayMode().get() == DISPLAY_LIST) {
|
||||||
RecyclerView(view.context).apply {
|
RecyclerView(view.context).apply {
|
||||||
id = R.id.recycler
|
id = R.id.recycler
|
||||||
layoutManager = LinearLayoutManager(context)
|
layoutManager = LinearLayoutManager(context)
|
||||||
@ -205,7 +208,7 @@ open class BrowseSourceController(bundle: Bundle) :
|
|||||||
(layoutManager as GridLayoutManager).spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
|
(layoutManager as GridLayoutManager).spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
|
||||||
override fun getSpanSize(position: Int): Int {
|
override fun getSpanSize(position: Int): Int {
|
||||||
return when (adapter?.getItemViewType(position)) {
|
return when (adapter?.getItemViewType(position)) {
|
||||||
R.layout.source_grid_item, null -> 1
|
R.layout.source_grid_item, R.layout.source_comfortable_grid_item, null -> 1
|
||||||
else -> spanCount
|
else -> spanCount
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -266,15 +269,13 @@ open class BrowseSourceController(bundle: Bundle) :
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Show next display mode
|
val displayItem = when (preferences.catalogueDisplayMode().get()) {
|
||||||
menu.findItem(R.id.action_display_mode).apply {
|
DISPLAY_COMPACT_GRID -> R.id.action_compact_grid
|
||||||
val icon = if (presenter.isListMode) {
|
DISPLAY_LIST -> R.id.action_list
|
||||||
R.drawable.ic_view_module_24dp
|
DISPLAY_COMFORTABLE_GRID -> R.id.action_comfortable_grid
|
||||||
} else {
|
else -> throw NotImplementedError("Unimplemented display")
|
||||||
R.drawable.ic_view_list_24dp
|
|
||||||
}
|
|
||||||
setIcon(icon)
|
|
||||||
}
|
}
|
||||||
|
menu.findItem(displayItem).isChecked = true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPrepareOptionsMenu(menu: Menu) {
|
override fun onPrepareOptionsMenu(menu: Menu) {
|
||||||
@ -290,7 +291,9 @@ open class BrowseSourceController(bundle: Bundle) :
|
|||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
when (item.itemId) {
|
when (item.itemId) {
|
||||||
R.id.action_search -> expandActionViewFromInteraction = true
|
R.id.action_search -> expandActionViewFromInteraction = true
|
||||||
R.id.action_display_mode -> swapDisplayMode()
|
R.id.action_compact_grid -> setDisplayMode(DISPLAY_COMPACT_GRID)
|
||||||
|
R.id.action_list -> setDisplayMode(DISPLAY_LIST)
|
||||||
|
R.id.action_comfortable_grid -> setDisplayMode(DISPLAY_COMFORTABLE_GRID)
|
||||||
R.id.action_open_in_web_view -> openInWebView()
|
R.id.action_open_in_web_view -> openInWebView()
|
||||||
R.id.action_local_source_help -> openLocalSourceHelpGuide()
|
R.id.action_local_source_help -> openLocalSourceHelpGuide()
|
||||||
}
|
}
|
||||||
@ -433,17 +436,19 @@ open class BrowseSourceController(bundle: Bundle) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Swaps the current display mode.
|
* Sets the current display mode.
|
||||||
|
*
|
||||||
|
* @param mode the mode to change to
|
||||||
*/
|
*/
|
||||||
private fun swapDisplayMode() {
|
private fun setDisplayMode(mode: Int) {
|
||||||
val view = view ?: return
|
val view = view ?: return
|
||||||
val adapter = adapter ?: return
|
val adapter = adapter ?: return
|
||||||
|
|
||||||
presenter.swapDisplayMode()
|
preferences.catalogueDisplayMode().set(mode)
|
||||||
val isListMode = presenter.isListMode
|
presenter.refreshDisplayMode()
|
||||||
activity?.invalidateOptionsMenu()
|
activity?.invalidateOptionsMenu()
|
||||||
setupRecycler(view)
|
setupRecycler(view)
|
||||||
if (!isListMode || !view.context.connectivityManager.isActiveNetworkMetered) {
|
if (mode == DISPLAY_LIST || !view.context.connectivityManager.isActiveNetworkMetered) {
|
||||||
// Initialize mangas if going to grid view or if over wifi when going to list view
|
// Initialize mangas if going to grid view or if over wifi when going to list view
|
||||||
val mangas = (0 until adapter.itemCount).mapNotNull {
|
val mangas = (0 until adapter.itemCount).mapNotNull {
|
||||||
(adapter.getItem(it) as? SourceItem)?.manga
|
(adapter.getItem(it) as? SourceItem)?.manga
|
||||||
|
@ -87,12 +87,6 @@ open class BrowseSourcePresenter(
|
|||||||
*/
|
*/
|
||||||
private val mangaDetailSubject = PublishSubject.create<List<Manga>>()
|
private val mangaDetailSubject = PublishSubject.create<List<Manga>>()
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether the view is in list mode or not.
|
|
||||||
*/
|
|
||||||
var isListMode: Boolean = false
|
|
||||||
private set
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscription for the pager.
|
* Subscription for the pager.
|
||||||
*/
|
*/
|
||||||
@ -119,7 +113,6 @@ open class BrowseSourcePresenter(
|
|||||||
query = savedState.getString(::query.name, "")
|
query = savedState.getString(::query.name, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
isListMode = prefs.catalogueAsList().get()
|
|
||||||
restartPager()
|
restartPager()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,7 +138,7 @@ open class BrowseSourcePresenter(
|
|||||||
|
|
||||||
val sourceId = source.id
|
val sourceId = source.id
|
||||||
|
|
||||||
val catalogueAsList = prefs.catalogueAsList()
|
val catalogueDisplayMode = prefs.catalogueDisplayMode()
|
||||||
|
|
||||||
// Prepare the pager.
|
// Prepare the pager.
|
||||||
pagerSubscription?.let { remove(it) }
|
pagerSubscription?.let { remove(it) }
|
||||||
@ -153,7 +146,7 @@ open class BrowseSourcePresenter(
|
|||||||
.observeOn(Schedulers.io())
|
.observeOn(Schedulers.io())
|
||||||
.map { pair -> pair.first to pair.second.map { networkToLocalManga(it, sourceId) } }
|
.map { pair -> pair.first to pair.second.map { networkToLocalManga(it, sourceId) } }
|
||||||
.doOnNext { initializeMangas(it.second) }
|
.doOnNext { initializeMangas(it.second) }
|
||||||
.map { pair -> pair.first to pair.second.map { SourceItem(it, catalogueAsList) } }
|
.map { pair -> pair.first to pair.second.map { SourceItem(it, catalogueDisplayMode) } }
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribeReplay(
|
.subscribeReplay(
|
||||||
{ view, (page, mangas) ->
|
{ view, (page, mangas) ->
|
||||||
@ -273,12 +266,9 @@ open class BrowseSourcePresenter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Changes the active display mode.
|
* Refreshes the active display mode.
|
||||||
*/
|
*/
|
||||||
fun swapDisplayMode() {
|
fun refreshDisplayMode() {
|
||||||
val mode = !isListMode
|
|
||||||
prefs.catalogueAsList().set(mode)
|
|
||||||
isListMode = mode
|
|
||||||
subscribeToMangaInitializer()
|
subscribeToMangaInitializer()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
package eu.kanade.tachiyomi.ui.browse.source.browse
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||||
|
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||||
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
|
import eu.kanade.tachiyomi.data.glide.GlideApp
|
||||||
|
import eu.kanade.tachiyomi.data.glide.toMangaThumbnail
|
||||||
|
import eu.kanade.tachiyomi.widget.StateImageViewTarget
|
||||||
|
import kotlinx.android.synthetic.main.source_comfortable_grid_item.card
|
||||||
|
import kotlinx.android.synthetic.main.source_comfortable_grid_item.progress
|
||||||
|
import kotlinx.android.synthetic.main.source_comfortable_grid_item.thumbnail
|
||||||
|
import kotlinx.android.synthetic.main.source_comfortable_grid_item.title
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class used to hold the displayed data of a manga in the catalogue, like the cover or the title.
|
||||||
|
* All the elements from the layout file "item_source_grid" are available in this class.
|
||||||
|
*
|
||||||
|
* @param view the inflated view for this holder.
|
||||||
|
* @param adapter the adapter handling this holder.
|
||||||
|
* @constructor creates a new catalogue holder.
|
||||||
|
*/
|
||||||
|
class SourceComfortableGridHolder(private val view: View, private val adapter: FlexibleAdapter<*>) :
|
||||||
|
SourceGridHolder(view, adapter) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method called from [CatalogueAdapter.onBindViewHolder]. It updates the data for this
|
||||||
|
* holder with the given manga.
|
||||||
|
*
|
||||||
|
* @param manga the manga to bind.
|
||||||
|
*/
|
||||||
|
override fun onSetValues(manga: Manga) {
|
||||||
|
// Set manga title
|
||||||
|
title.text = manga.title
|
||||||
|
|
||||||
|
// Set alpha of thumbnail.
|
||||||
|
thumbnail.alpha = if (manga.favorite) 0.3f else 1.0f
|
||||||
|
|
||||||
|
setImage(manga)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setImage(manga: Manga) {
|
||||||
|
// Setting this via XML doesn't work
|
||||||
|
card.clipToOutline = true
|
||||||
|
|
||||||
|
GlideApp.with(view.context).clear(thumbnail)
|
||||||
|
if (!manga.thumbnail_url.isNullOrEmpty()) {
|
||||||
|
GlideApp.with(view.context)
|
||||||
|
.load(manga.toMangaThumbnail())
|
||||||
|
.diskCacheStrategy(DiskCacheStrategy.DATA)
|
||||||
|
.centerCrop()
|
||||||
|
.placeholder(android.R.color.transparent)
|
||||||
|
.into(StateImageViewTarget(thumbnail, progress))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -20,7 +20,7 @@ import kotlinx.android.synthetic.main.source_grid_item.title
|
|||||||
* @param adapter the adapter handling this holder.
|
* @param adapter the adapter handling this holder.
|
||||||
* @constructor creates a new catalogue holder.
|
* @constructor creates a new catalogue holder.
|
||||||
*/
|
*/
|
||||||
class SourceGridHolder(private val view: View, private val adapter: FlexibleAdapter<*>) :
|
open class SourceGridHolder(private val view: View, private val adapter: FlexibleAdapter<*>) :
|
||||||
SourceHolder(view, adapter) {
|
SourceHolder(view, adapter) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -4,6 +4,7 @@ import android.view.Gravity
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.tfcporciuncula.flow.Preference
|
import com.tfcporciuncula.flow.Preference
|
||||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||||
@ -11,18 +12,20 @@ import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
|||||||
import eu.davidea.flexibleadapter.items.IFlexible
|
import eu.davidea.flexibleadapter.items.IFlexible
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
|
import eu.kanade.tachiyomi.data.preference.PreferenceValues.DISPLAY_COMPACT_GRID
|
||||||
|
import eu.kanade.tachiyomi.data.preference.PreferenceValues.DISPLAY_LIST
|
||||||
import eu.kanade.tachiyomi.widget.AutofitRecyclerView
|
import eu.kanade.tachiyomi.widget.AutofitRecyclerView
|
||||||
import kotlinx.android.synthetic.main.source_grid_item.view.card
|
import kotlinx.android.synthetic.main.source_grid_item.view.card
|
||||||
import kotlinx.android.synthetic.main.source_grid_item.view.gradient
|
import kotlinx.android.synthetic.main.source_grid_item.view.gradient
|
||||||
|
|
||||||
class SourceItem(val manga: Manga, private val catalogueAsList: Preference<Boolean>) :
|
class SourceItem(val manga: Manga, private val catalogueDisplayMode: Preference<Int>) :
|
||||||
AbstractFlexibleItem<SourceHolder>() {
|
AbstractFlexibleItem<SourceHolder>() {
|
||||||
|
|
||||||
override fun getLayoutRes(): Int {
|
override fun getLayoutRes(): Int {
|
||||||
return if (catalogueAsList.get()) {
|
return when (catalogueDisplayMode.get()) {
|
||||||
R.layout.source_list_item
|
DISPLAY_COMPACT_GRID -> R.layout.source_grid_item
|
||||||
} else {
|
DISPLAY_LIST -> R.layout.source_list_item
|
||||||
R.layout.source_grid_item
|
else -> R.layout.source_comfortable_grid_item
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,15 +35,28 @@ class SourceItem(val manga: Manga, private val catalogueAsList: Preference<Boole
|
|||||||
): SourceHolder {
|
): SourceHolder {
|
||||||
val parent = adapter.recyclerView
|
val parent = adapter.recyclerView
|
||||||
return if (parent is AutofitRecyclerView) {
|
return if (parent is AutofitRecyclerView) {
|
||||||
|
val coverHeight = parent.itemWidth / 3 * 4
|
||||||
|
if (catalogueDisplayMode.get() == DISPLAY_COMPACT_GRID) {
|
||||||
view.apply {
|
view.apply {
|
||||||
card.layoutParams = FrameLayout.LayoutParams(
|
card.layoutParams = FrameLayout.LayoutParams(
|
||||||
MATCH_PARENT, parent.itemWidth / 3 * 4
|
MATCH_PARENT, coverHeight
|
||||||
)
|
)
|
||||||
gradient.layoutParams = FrameLayout.LayoutParams(
|
gradient.layoutParams = FrameLayout.LayoutParams(
|
||||||
MATCH_PARENT, parent.itemWidth / 3 * 4 / 2, Gravity.BOTTOM
|
MATCH_PARENT, coverHeight / 2, Gravity.BOTTOM
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
SourceGridHolder(view, adapter)
|
SourceGridHolder(view, adapter)
|
||||||
|
} else {
|
||||||
|
view.apply {
|
||||||
|
card.layoutParams = ConstraintLayout.LayoutParams(
|
||||||
|
MATCH_PARENT, coverHeight
|
||||||
|
)
|
||||||
|
gradient.layoutParams = FrameLayout.LayoutParams(
|
||||||
|
MATCH_PARENT, coverHeight / 2, Gravity.BOTTOM
|
||||||
|
)
|
||||||
|
}
|
||||||
|
SourceComfortableGridHolder(view, adapter)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
SourceListHolder(view, adapter)
|
SourceListHolder(view, adapter)
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ 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.Manga
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateService
|
import eu.kanade.tachiyomi.data.library.LibraryUpdateService
|
||||||
|
import eu.kanade.tachiyomi.data.preference.PreferenceValues.DISPLAY_LIST
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.util.lang.plusAssign
|
import eu.kanade.tachiyomi.util.lang.plusAssign
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
@ -72,7 +73,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
|
|||||||
fun onCreate(controller: LibraryController) {
|
fun onCreate(controller: LibraryController) {
|
||||||
this.controller = controller
|
this.controller = controller
|
||||||
|
|
||||||
recycler = if (preferences.libraryAsList().get()) {
|
recycler = if (preferences.libraryDisplayMode().get() == DISPLAY_LIST) {
|
||||||
(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)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,68 @@
|
|||||||
|
package eu.kanade.tachiyomi.ui.library
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||||
|
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||||
|
import eu.davidea.flexibleadapter.items.IFlexible
|
||||||
|
import eu.kanade.tachiyomi.data.glide.GlideApp
|
||||||
|
import eu.kanade.tachiyomi.data.glide.toMangaThumbnail
|
||||||
|
import eu.kanade.tachiyomi.util.isLocal
|
||||||
|
import eu.kanade.tachiyomi.util.view.visibleIf
|
||||||
|
import kotlinx.android.synthetic.main.source_comfortable_grid_item.card
|
||||||
|
import kotlinx.android.synthetic.main.source_comfortable_grid_item.download_text
|
||||||
|
import kotlinx.android.synthetic.main.source_comfortable_grid_item.local_text
|
||||||
|
import kotlinx.android.synthetic.main.source_comfortable_grid_item.thumbnail
|
||||||
|
import kotlinx.android.synthetic.main.source_comfortable_grid_item.title
|
||||||
|
import kotlinx.android.synthetic.main.source_comfortable_grid_item.unread_text
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class used to hold the displayed data of a manga in the library, like the cover or the title.
|
||||||
|
* All the elements from the layout file "item_source_grid" are available in this class.
|
||||||
|
*
|
||||||
|
* @param view the inflated view for this holder.
|
||||||
|
* @param adapter the adapter handling this holder.
|
||||||
|
* @param listener a listener to react to single tap and long tap events.
|
||||||
|
* @constructor creates a new library holder.
|
||||||
|
*/
|
||||||
|
class LibraryComfortableGridHolder(
|
||||||
|
private val view: View,
|
||||||
|
adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>
|
||||||
|
) : LibraryGridHolder(view, adapter) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method called from [LibraryCategoryAdapter.onBindViewHolder]. It updates the data for this
|
||||||
|
* holder with the given manga.
|
||||||
|
*
|
||||||
|
* @param item the manga item to bind.
|
||||||
|
*/
|
||||||
|
override fun onSetValues(item: LibraryItem) {
|
||||||
|
// Update the title of the manga.
|
||||||
|
title.text = item.manga.title
|
||||||
|
|
||||||
|
// Update the unread count and its visibility.
|
||||||
|
with(unread_text) {
|
||||||
|
visibleIf { item.unreadCount > 0 }
|
||||||
|
text = item.unreadCount.toString()
|
||||||
|
}
|
||||||
|
// Update the download count and its visibility.
|
||||||
|
with(download_text) {
|
||||||
|
visibleIf { item.downloadCount > 0 }
|
||||||
|
text = item.downloadCount.toString()
|
||||||
|
}
|
||||||
|
// set local visibility if its local manga
|
||||||
|
local_text.visibleIf { item.manga.isLocal() }
|
||||||
|
|
||||||
|
// Setting this via XML doesn't work
|
||||||
|
card.clipToOutline = true
|
||||||
|
|
||||||
|
// Update the cover.
|
||||||
|
GlideApp.with(view.context).clear(thumbnail)
|
||||||
|
GlideApp.with(view.context)
|
||||||
|
.load(item.manga.toMangaThumbnail())
|
||||||
|
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
|
||||||
|
.centerCrop()
|
||||||
|
.dontAnimate()
|
||||||
|
.into(thumbnail)
|
||||||
|
}
|
||||||
|
}
|
@ -23,7 +23,7 @@ import kotlinx.android.synthetic.main.source_grid_item.unread_text
|
|||||||
* @param listener a listener to react to single tap and long tap events.
|
* @param listener a listener to react to single tap and long tap events.
|
||||||
* @constructor creates a new library holder.
|
* @constructor creates a new library holder.
|
||||||
*/
|
*/
|
||||||
class LibraryGridHolder(
|
open class LibraryGridHolder(
|
||||||
private val view: View,
|
private val view: View,
|
||||||
private val adapter: FlexibleAdapter<*>
|
private val adapter: FlexibleAdapter<*>
|
||||||
) : LibraryHolder(view, adapter) {
|
) : LibraryHolder(view, adapter) {
|
||||||
|
@ -4,6 +4,7 @@ import android.view.Gravity
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.tfcporciuncula.flow.Preference
|
import com.tfcporciuncula.flow.Preference
|
||||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||||
@ -12,6 +13,8 @@ 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
|
||||||
import eu.kanade.tachiyomi.data.database.models.LibraryManga
|
import eu.kanade.tachiyomi.data.database.models.LibraryManga
|
||||||
|
import eu.kanade.tachiyomi.data.preference.PreferenceValues.DISPLAY_COMPACT_GRID
|
||||||
|
import eu.kanade.tachiyomi.data.preference.PreferenceValues.DISPLAY_LIST
|
||||||
import eu.kanade.tachiyomi.source.SourceManager
|
import eu.kanade.tachiyomi.source.SourceManager
|
||||||
import eu.kanade.tachiyomi.widget.AutofitRecyclerView
|
import eu.kanade.tachiyomi.widget.AutofitRecyclerView
|
||||||
import kotlinx.android.synthetic.main.source_grid_item.view.card
|
import kotlinx.android.synthetic.main.source_grid_item.view.card
|
||||||
@ -19,7 +22,7 @@ import kotlinx.android.synthetic.main.source_grid_item.view.gradient
|
|||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
class LibraryItem(val manga: LibraryManga, private val libraryAsList: Preference<Boolean>) :
|
class LibraryItem(val manga: LibraryManga, private val libraryDisplayMode: Preference<Int>) :
|
||||||
AbstractFlexibleItem<LibraryHolder>(), IFilterable<String> {
|
AbstractFlexibleItem<LibraryHolder>(), IFilterable<String> {
|
||||||
|
|
||||||
private val sourceManager: SourceManager = Injekt.get()
|
private val sourceManager: SourceManager = Injekt.get()
|
||||||
@ -28,24 +31,36 @@ class LibraryItem(val manga: LibraryManga, private val libraryAsList: Preference
|
|||||||
var unreadCount = -1
|
var unreadCount = -1
|
||||||
|
|
||||||
override fun getLayoutRes(): Int {
|
override fun getLayoutRes(): Int {
|
||||||
return if (libraryAsList.get()) {
|
return when (libraryDisplayMode.get()) {
|
||||||
R.layout.source_list_item
|
DISPLAY_COMPACT_GRID -> R.layout.source_grid_item
|
||||||
} else {
|
DISPLAY_LIST -> R.layout.source_list_item
|
||||||
R.layout.source_grid_item
|
else -> R.layout.source_comfortable_grid_item
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): LibraryHolder {
|
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): LibraryHolder {
|
||||||
val parent = adapter.recyclerView
|
val parent = adapter.recyclerView
|
||||||
return if (parent is AutofitRecyclerView) {
|
return if (parent is AutofitRecyclerView) {
|
||||||
view.apply {
|
|
||||||
val coverHeight = parent.itemWidth / 3 * 4
|
val coverHeight = parent.itemWidth / 3 * 4
|
||||||
|
if (libraryDisplayMode.get() == DISPLAY_COMPACT_GRID) {
|
||||||
|
view.apply {
|
||||||
card.layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, coverHeight)
|
card.layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, coverHeight)
|
||||||
gradient.layoutParams = FrameLayout.LayoutParams(
|
gradient.layoutParams = FrameLayout.LayoutParams(
|
||||||
MATCH_PARENT, coverHeight / 2, Gravity.BOTTOM
|
MATCH_PARENT, coverHeight / 2, Gravity.BOTTOM
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
LibraryGridHolder(view, adapter)
|
LibraryGridHolder(view, adapter)
|
||||||
|
} else {
|
||||||
|
view.apply {
|
||||||
|
card.layoutParams = ConstraintLayout.LayoutParams(
|
||||||
|
MATCH_PARENT, coverHeight
|
||||||
|
)
|
||||||
|
gradient.layoutParams = FrameLayout.LayoutParams(
|
||||||
|
MATCH_PARENT, coverHeight / 2, Gravity.BOTTOM
|
||||||
|
)
|
||||||
|
}
|
||||||
|
LibraryComfortableGridHolder(view, adapter)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
LibraryListHolder(view, adapter)
|
LibraryListHolder(view, adapter)
|
||||||
}
|
}
|
||||||
|
@ -266,10 +266,10 @@ class LibraryPresenter(
|
|||||||
* value.
|
* value.
|
||||||
*/
|
*/
|
||||||
private fun getLibraryMangasObservable(): Observable<LibraryMap> {
|
private fun getLibraryMangasObservable(): Observable<LibraryMap> {
|
||||||
val libraryAsList = preferences.libraryAsList()
|
val libraryDisplayMode = preferences.libraryDisplayMode()
|
||||||
return db.getLibraryMangas().asRxObservable()
|
return db.getLibraryMangas().asRxObservable()
|
||||||
.map { list ->
|
.map { list ->
|
||||||
list.map { LibraryItem(it, libraryAsList) }.groupBy { it.manga.category }
|
list.map { LibraryItem(it, libraryDisplayMode) }.groupBy { it.manga.category }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,9 @@ import android.content.Context
|
|||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
|
import eu.kanade.tachiyomi.data.preference.PreferenceValues.DISPLAY_COMFORTABLE_GRID
|
||||||
|
import eu.kanade.tachiyomi.data.preference.PreferenceValues.DISPLAY_COMPACT_GRID
|
||||||
|
import eu.kanade.tachiyomi.data.preference.PreferenceValues.DISPLAY_LIST
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.widget.ExtendedNavigationView
|
import eu.kanade.tachiyomi.widget.ExtendedNavigationView
|
||||||
import eu.kanade.tachiyomi.widget.TabbedBottomSheetDialog
|
import eu.kanade.tachiyomi.widget.TabbedBottomSheetDialog
|
||||||
@ -183,16 +186,18 @@ class LibrarySettingsSheet(
|
|||||||
inner class DisplayGroup : Group {
|
inner class DisplayGroup : Group {
|
||||||
|
|
||||||
private val grid = Item.Radio(R.string.action_display_grid, this)
|
private val grid = Item.Radio(R.string.action_display_grid, this)
|
||||||
|
private val comfortableGrid = Item.Radio(R.string.action_display_comfortable_grid, this)
|
||||||
private val list = Item.Radio(R.string.action_display_list, this)
|
private val list = Item.Radio(R.string.action_display_list, this)
|
||||||
|
|
||||||
override val header = null
|
override val header = null
|
||||||
override val items = listOf(grid, list)
|
override val items = listOf(grid, comfortableGrid, list)
|
||||||
override val footer = null
|
override val footer = null
|
||||||
|
|
||||||
override fun initModels() {
|
override fun initModels() {
|
||||||
val asList = preferences.libraryAsList().get()
|
val mode = preferences.libraryDisplayMode().get()
|
||||||
grid.checked = !asList
|
grid.checked = mode == DISPLAY_COMPACT_GRID
|
||||||
list.checked = asList
|
list.checked = mode == DISPLAY_LIST
|
||||||
|
comfortableGrid.checked = mode == DISPLAY_COMFORTABLE_GRID
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onItemClicked(item: Item) {
|
override fun onItemClicked(item: Item) {
|
||||||
@ -202,7 +207,13 @@ class LibrarySettingsSheet(
|
|||||||
item.group.items.forEach { (it as Item.Radio).checked = false }
|
item.group.items.forEach { (it as Item.Radio).checked = false }
|
||||||
item.checked = true
|
item.checked = true
|
||||||
|
|
||||||
preferences.libraryAsList().set(item == list)
|
preferences.libraryDisplayMode().set(
|
||||||
|
when (item) {
|
||||||
|
grid -> DISPLAY_COMPACT_GRID
|
||||||
|
list -> DISPLAY_LIST
|
||||||
|
else -> DISPLAY_COMFORTABLE_GRID
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
item.group.items.forEach { adapter.notifyItemChanged(it) }
|
item.group.items.forEach { adapter.notifyItemChanged(it) }
|
||||||
}
|
}
|
||||||
|
130
app/src/main/res/layout/source_comfortable_grid_item.xml
Normal file
130
app/src/main/res/layout/source_comfortable_grid_item.xml
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/library_item_selector"
|
||||||
|
android:padding="4dp">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/rounded_rectangle">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/card"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="220dp"
|
||||||
|
android:background="@drawable/rounded_rectangle"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/thumbnail"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="?attr/colorSurface"
|
||||||
|
tools:ignore="ContentDescription"
|
||||||
|
tools:src="@mipmap/ic_launcher" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/gradient"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="bottom"
|
||||||
|
android:background="@drawable/gradient_shape" />
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
tools:layout_editor_absoluteX="7dp"
|
||||||
|
tools:layout_editor_absoluteY="7dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/unread_text"
|
||||||
|
style="@style/TextAppearance.Regular.Caption"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:background="@color/colorAccentDark"
|
||||||
|
android:paddingStart="3dp"
|
||||||
|
android:paddingTop="1dp"
|
||||||
|
android:paddingEnd="3dp"
|
||||||
|
android:paddingBottom="1dp"
|
||||||
|
android:textColor="@color/md_white_1000"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/download_text"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:text="120"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/download_text"
|
||||||
|
style="@style/TextAppearance.Regular.Caption"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:background="@color/md_red_500"
|
||||||
|
android:paddingStart="3dp"
|
||||||
|
android:paddingTop="1dp"
|
||||||
|
android:paddingEnd="3dp"
|
||||||
|
android:paddingBottom="1dp"
|
||||||
|
android:textColor="@color/md_white_1000"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/local_text"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:text="120"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/local_text"
|
||||||
|
style="@style/TextAppearance.Regular.Caption"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:background="@color/md_teal_500"
|
||||||
|
android:paddingStart="3dp"
|
||||||
|
android:paddingTop="1dp"
|
||||||
|
android:paddingEnd="3dp"
|
||||||
|
android:paddingBottom="1dp"
|
||||||
|
android:text="@string/local_source_badge"
|
||||||
|
android:textColor="@color/md_white_1000"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/progress"
|
||||||
|
style="?android:attr/progressBarStyleSmall"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/title"
|
||||||
|
style="@style/TextAppearance.Regular.Body1"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="bottom|start|end"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:fontFamily="@font/ptsans_narrow_bold"
|
||||||
|
android:lineSpacingExtra="-4dp"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:shadowColor="@color/textColorPrimaryLight"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/card"
|
||||||
|
tools:text="Sample name" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</FrameLayout>
|
@ -10,11 +10,24 @@
|
|||||||
app:showAsAction="collapseActionView|ifRoom" />
|
app:showAsAction="collapseActionView|ifRoom" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_display_mode"
|
|
||||||
android:icon="@drawable/ic_view_module_24dp"
|
android:icon="@drawable/ic_view_module_24dp"
|
||||||
android:title="@string/action_display_mode"
|
android:title="@string/action_display_mode"
|
||||||
app:iconTint="?attr/colorOnPrimary"
|
app:iconTint="?attr/colorOnPrimary"
|
||||||
app:showAsAction="ifRoom" />
|
app:showAsAction="ifRoom">
|
||||||
|
<menu>
|
||||||
|
<group android:checkableBehavior="single">
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_compact_grid"
|
||||||
|
android:title="@string/action_display_grid" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_comfortable_grid"
|
||||||
|
android:title="@string/action_display_comfortable_grid" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_list"
|
||||||
|
android:title="@string/action_display_list" />
|
||||||
|
</group>
|
||||||
|
</menu>
|
||||||
|
</item>
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_open_in_web_view"
|
android:id="@+id/action_open_in_web_view"
|
||||||
|
@ -81,8 +81,9 @@
|
|||||||
<string name="action_migrate">Migrate</string>
|
<string name="action_migrate">Migrate</string>
|
||||||
<string name="action_display_mode">Display mode</string>
|
<string name="action_display_mode">Display mode</string>
|
||||||
<string name="action_display">Display</string>
|
<string name="action_display">Display</string>
|
||||||
<string name="action_display_grid">Grid</string>
|
<string name="action_display_grid">Compact grid</string>
|
||||||
<string name="action_display_list">List</string>
|
<string name="action_display_list">List</string>
|
||||||
|
<string name="action_display_comfortable_grid">Comfortable grid</string>
|
||||||
<string name="action_display_download_badge">Download badges</string>
|
<string name="action_display_download_badge">Download badges</string>
|
||||||
<string name="action_display_unread_badge">Unread badges</string>
|
<string name="action_display_unread_badge">Unread badges</string>
|
||||||
<string name="action_hide">Hide</string>
|
<string name="action_hide">Hide</string>
|
||||||
|
Loading…
Reference in New Issue
Block a user