Favourites categories screen
This commit is contained in:
@@ -91,7 +91,7 @@
|
||||
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
|
||||
android:windowSoftInputMode="adjustResize" />
|
||||
<activity
|
||||
android:name="org.koitharu.kotatsu.favourites.ui.categories.CategoriesActivity"
|
||||
android:name="org.koitharu.kotatsu.favourites.ui.categories.FavouriteCategoriesActivity"
|
||||
android:label="@string/favourites_categories"
|
||||
android:windowSoftInputMode="stateAlwaysHidden" />
|
||||
<activity
|
||||
|
||||
@@ -20,7 +20,7 @@ import org.koitharu.kotatsu.details.ui.DetailsActivity
|
||||
import org.koitharu.kotatsu.explore.ui.adapter.ExploreAdapter
|
||||
import org.koitharu.kotatsu.explore.ui.adapter.ExploreListEventListener
|
||||
import org.koitharu.kotatsu.explore.ui.model.ExploreItem
|
||||
import org.koitharu.kotatsu.favourites.ui.categories.CategoriesActivity
|
||||
import org.koitharu.kotatsu.favourites.ui.categories.FavouriteCategoriesActivity
|
||||
import org.koitharu.kotatsu.history.ui.HistoryActivity
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
@@ -88,7 +88,7 @@ class ExploreFragment : BaseFragment<FragmentExploreBinding>(),
|
||||
R.id.button_local -> MangaListActivity.newIntent(v.context, MangaSource.LOCAL)
|
||||
R.id.button_bookmarks -> BookmarksActivity.newIntent(v.context)
|
||||
R.id.button_suggestions -> SuggestionsActivity.newIntent(v.context)
|
||||
R.id.button_favourites -> CategoriesActivity.newIntent(v.context)
|
||||
R.id.button_favourites -> FavouriteCategoriesActivity.newIntent(v.context)
|
||||
R.id.button_random -> {
|
||||
viewModel.openRandom()
|
||||
return
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package org.koitharu.kotatsu.favourites.data
|
||||
|
||||
import java.util.*
|
||||
import org.koitharu.kotatsu.core.db.entity.SortOrder
|
||||
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||
import org.koitharu.kotatsu.parsers.model.SortOrder
|
||||
import java.util.*
|
||||
|
||||
fun FavouriteCategoryEntity.toFavouriteCategory(id: Long = categoryId.toLong()) = FavouriteCategory(
|
||||
id = id,
|
||||
|
||||
@@ -15,6 +15,17 @@ abstract class FavouriteCategoriesDao {
|
||||
@Query("SELECT * FROM favourite_categories ORDER BY sort_key")
|
||||
abstract fun observeAll(): Flow<List<FavouriteCategoryEntity>>
|
||||
|
||||
@MapInfo(valueColumn = "cover")
|
||||
@Query(
|
||||
"""
|
||||
SELECT favourite_categories.*, manga.cover_url AS cover
|
||||
FROM favourite_categories JOIN manga ON manga.manga_id IN
|
||||
(SELECT manga_id FROM favourites WHERE favourites.category_id == favourite_categories.category_id)
|
||||
ORDER BY favourite_categories.sort_key
|
||||
"""
|
||||
)
|
||||
abstract fun observeAllWithDetails(): Flow<Map<FavouriteCategoryEntity, List<String>>>
|
||||
|
||||
@Query("SELECT * FROM favourite_categories WHERE category_id = :id")
|
||||
abstract fun observe(id: Long): Flow<FavouriteCategoryEntity?>
|
||||
|
||||
|
||||
@@ -13,4 +13,31 @@ class FavouriteCategoryEntity(
|
||||
@ColumnInfo(name = "title") val title: String,
|
||||
@ColumnInfo(name = "order") val order: String,
|
||||
@ColumnInfo(name = "track") val track: Boolean,
|
||||
)
|
||||
) {
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as FavouriteCategoryEntity
|
||||
|
||||
if (categoryId != other.categoryId) return false
|
||||
if (createdAt != other.createdAt) return false
|
||||
if (sortKey != other.sortKey) return false
|
||||
if (title != other.title) return false
|
||||
if (order != other.order) return false
|
||||
if (track != other.track) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = categoryId
|
||||
result = 31 * result + createdAt.hashCode()
|
||||
result = 31 * result + sortKey
|
||||
result = 31 * result + title.hashCode()
|
||||
result = 31 * result + order.hashCode()
|
||||
result = 31 * result + track.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -5,10 +5,7 @@ import kotlinx.coroutines.flow.*
|
||||
import org.koitharu.kotatsu.core.db.MangaDatabase
|
||||
import org.koitharu.kotatsu.core.db.entity.*
|
||||
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||
import org.koitharu.kotatsu.favourites.data.FavouriteCategoryEntity
|
||||
import org.koitharu.kotatsu.favourites.data.FavouriteEntity
|
||||
import org.koitharu.kotatsu.favourites.data.FavouriteManga
|
||||
import org.koitharu.kotatsu.favourites.data.toFavouriteCategory
|
||||
import org.koitharu.kotatsu.favourites.data.*
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.SortOrder
|
||||
import org.koitharu.kotatsu.tracker.work.TrackerNotificationChannels
|
||||
@@ -55,6 +52,13 @@ class FavouritesRepository(
|
||||
}.distinctUntilChanged()
|
||||
}
|
||||
|
||||
fun observeCategoriesWithDetails(): Flow<Map<FavouriteCategory, List<String>>> {
|
||||
return db.favouriteCategoriesDao.observeAllWithDetails()
|
||||
.map {
|
||||
it.mapKeys { (k, _) -> k.toFavouriteCategory() }
|
||||
}
|
||||
}
|
||||
|
||||
fun observeCategory(id: Long): Flow<FavouriteCategory?> {
|
||||
return db.favouriteCategoriesDao.observe(id)
|
||||
.map { it?.toFavouriteCategory() }
|
||||
|
||||
@@ -49,14 +49,14 @@ class FavouritesContainerFragment :
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val adapter = FavouritesPagerAdapter(this, this)
|
||||
viewModel.visibleCategories.value?.let(::onCategoriesChanged)
|
||||
viewModel.allCategories.value?.let(::onCategoriesChanged)
|
||||
binding.pager.adapter = adapter
|
||||
pagerAdapter = adapter
|
||||
TabLayoutMediator(binding.tabs, binding.pager, adapter).attach()
|
||||
actionModeDelegate.addListener(this, viewLifecycleOwner)
|
||||
addMenuProvider(FavouritesContainerMenuProvider(view.context))
|
||||
|
||||
viewModel.visibleCategories.observe(viewLifecycleOwner, ::onCategoriesChanged)
|
||||
viewModel.allCategories.observe(viewLifecycleOwner, ::onCategoriesChanged)
|
||||
viewModel.onError.observe(viewLifecycleOwner, ::onError)
|
||||
}
|
||||
|
||||
@@ -103,10 +103,10 @@ class FavouritesContainerFragment :
|
||||
}
|
||||
|
||||
override fun onTabLongClick(tabView: View, item: CategoryListModel): Boolean {
|
||||
when (item) {
|
||||
/*when (item) {
|
||||
is CategoryListModel.All -> showAllCategoriesMenu(tabView)
|
||||
is CategoryListModel.CategoryItem -> showCategoryMenu(tabView, item.category)
|
||||
}
|
||||
}*/
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -133,7 +133,12 @@ class FavouritesContainerFragment :
|
||||
menu.setOnMenuItemClickListener {
|
||||
when (it.itemId) {
|
||||
R.id.action_remove -> editDelegate.deleteCategory(category)
|
||||
R.id.action_edit -> startActivity(FavouritesCategoryEditActivity.newIntent(tabView.context, category.id))
|
||||
R.id.action_edit -> startActivity(
|
||||
FavouritesCategoryEditActivity.newIntent(
|
||||
tabView.context,
|
||||
category.id
|
||||
)
|
||||
)
|
||||
else -> return@setOnMenuItemClickListener false
|
||||
}
|
||||
true
|
||||
|
||||
@@ -6,7 +6,7 @@ import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import androidx.core.view.MenuProvider
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.favourites.ui.categories.CategoriesActivity
|
||||
import org.koitharu.kotatsu.favourites.ui.categories.FavouriteCategoriesActivity
|
||||
|
||||
class FavouritesContainerMenuProvider(
|
||||
private val context: Context,
|
||||
@@ -19,7 +19,7 @@ class FavouritesContainerMenuProvider(
|
||||
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
|
||||
return when (menuItem.itemId) {
|
||||
R.id.action_categories -> {
|
||||
context.startActivity(CategoriesActivity.newIntent(context))
|
||||
context.startActivity(FavouriteCategoriesActivity.newIntent(context))
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.koitharu.kotatsu.favourites.ui
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.View
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.AsyncListDiffer
|
||||
@@ -7,7 +8,6 @@ import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.favourites.ui.categories.adapter.CategoryListModel
|
||||
import org.koitharu.kotatsu.favourites.ui.list.FavouritesListFragment
|
||||
|
||||
@@ -24,24 +24,21 @@ class FavouritesPagerAdapter(
|
||||
|
||||
override fun createFragment(position: Int): Fragment {
|
||||
val item = differ.currentList[position]
|
||||
return FavouritesListFragment.newInstance(item.id)
|
||||
return FavouritesListFragment.newInstance(item.category.id)
|
||||
}
|
||||
|
||||
override fun getItemId(position: Int): Long {
|
||||
return differ.currentList[position].id
|
||||
return differ.currentList[position].category.id
|
||||
}
|
||||
|
||||
override fun containsItem(itemId: Long): Boolean {
|
||||
return differ.currentList.any { it.id == itemId }
|
||||
return differ.currentList.any { it.category.id == itemId }
|
||||
}
|
||||
|
||||
override fun onConfigureTab(tab: TabLayout.Tab, position: Int) {
|
||||
val item = differ.currentList[position]
|
||||
tab.text = when (item) {
|
||||
is CategoryListModel.All -> tab.view.context.getString(R.string.all_favourites)
|
||||
is CategoryListModel.CategoryItem -> item.category.title
|
||||
}
|
||||
tab.view.tag = item.id
|
||||
tab.text = item.category.title
|
||||
tab.view.tag = item.category.id
|
||||
tab.view.setOnLongClickListener(this)
|
||||
}
|
||||
|
||||
@@ -51,7 +48,7 @@ class FavouritesPagerAdapter(
|
||||
|
||||
override fun onLongClick(v: View): Boolean {
|
||||
val itemId = v.tag as? Long ?: return false
|
||||
val item = differ.currentList.find { x -> x.id == itemId } ?: return false
|
||||
val item = differ.currentList.find { x -> x.category.id == itemId } ?: return false
|
||||
return longClickListener.onTabLongClick(v, item)
|
||||
}
|
||||
|
||||
@@ -60,14 +57,11 @@ class FavouritesPagerAdapter(
|
||||
override fun areItemsTheSame(
|
||||
oldItem: CategoryListModel,
|
||||
newItem: CategoryListModel
|
||||
): Boolean = when {
|
||||
oldItem is CategoryListModel.All && newItem is CategoryListModel.All -> true
|
||||
oldItem is CategoryListModel.CategoryItem && newItem is CategoryListModel.CategoryItem -> {
|
||||
oldItem.category.id == newItem.category.id
|
||||
}
|
||||
else -> false
|
||||
): Boolean {
|
||||
return oldItem.category.id == newItem.category.id
|
||||
}
|
||||
|
||||
@SuppressLint("DiffUtilEquals")
|
||||
override fun areContentsTheSame(
|
||||
oldItem: CategoryListModel,
|
||||
newItem: CategoryListModel
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
package org.koitharu.kotatsu.favourites.ui.categories
|
||||
|
||||
interface AllCategoriesToggleListener {
|
||||
|
||||
fun onAllCategoriesToggle(isVisible: Boolean)
|
||||
}
|
||||
@@ -1,49 +1,49 @@
|
||||
package org.koitharu.kotatsu.favourites.ui.categories
|
||||
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import coil.ImageLoader
|
||||
import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter
|
||||
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
||||
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||
import org.koitharu.kotatsu.favourites.ui.categories.adapter.CategoryListModel
|
||||
import org.koitharu.kotatsu.favourites.ui.categories.adapter.allCategoriesAD
|
||||
import org.koitharu.kotatsu.favourites.ui.categories.adapter.categoryAD
|
||||
import org.koitharu.kotatsu.list.ui.adapter.ListStateHolderListener
|
||||
import org.koitharu.kotatsu.list.ui.adapter.emptyStateListAD
|
||||
import org.koitharu.kotatsu.list.ui.adapter.loadingStateAD
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import kotlin.jvm.internal.Intrinsics
|
||||
|
||||
class CategoriesAdapter(
|
||||
coil: ImageLoader,
|
||||
lifecycleOwner: LifecycleOwner,
|
||||
onItemClickListener: OnListItemClickListener<FavouriteCategory>,
|
||||
allCategoriesToggleListener: AllCategoriesToggleListener,
|
||||
) : AsyncListDifferDelegationAdapter<CategoryListModel>(DiffCallback()) {
|
||||
listListener: ListStateHolderListener,
|
||||
) : AsyncListDifferDelegationAdapter<ListModel>(DiffCallback()) {
|
||||
|
||||
init {
|
||||
delegatesManager.addDelegate(categoryAD(onItemClickListener))
|
||||
.addDelegate(allCategoriesAD(allCategoriesToggleListener))
|
||||
setHasStableIds(true)
|
||||
delegatesManager.addDelegate(categoryAD(coil, lifecycleOwner, onItemClickListener))
|
||||
.addDelegate(emptyStateListAD(listListener))
|
||||
.addDelegate(loadingStateAD())
|
||||
}
|
||||
|
||||
override fun getItemId(position: Int): Long {
|
||||
return items[position].id
|
||||
}
|
||||
private class DiffCallback : DiffUtil.ItemCallback<ListModel>() {
|
||||
|
||||
private class DiffCallback : DiffUtil.ItemCallback<CategoryListModel>() {
|
||||
override fun areItemsTheSame(oldItem: ListModel, newItem: ListModel): Boolean {
|
||||
return when {
|
||||
oldItem is CategoryListModel && newItem is CategoryListModel -> {
|
||||
oldItem.category.id == newItem.category.id
|
||||
}
|
||||
else -> oldItem.javaClass == newItem.javaClass
|
||||
}
|
||||
}
|
||||
|
||||
override fun areItemsTheSame(
|
||||
oldItem: CategoryListModel,
|
||||
newItem: CategoryListModel,
|
||||
): Boolean = oldItem.id == newItem.id
|
||||
override fun areContentsTheSame(oldItem: ListModel, newItem: ListModel): Boolean {
|
||||
return Intrinsics.areEqual(oldItem, newItem)
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(
|
||||
oldItem: CategoryListModel,
|
||||
newItem: CategoryListModel,
|
||||
): Boolean = oldItem == newItem
|
||||
|
||||
override fun getChangePayload(
|
||||
oldItem: CategoryListModel,
|
||||
newItem: CategoryListModel,
|
||||
): Any? = when {
|
||||
oldItem is CategoryListModel.All && newItem is CategoryListModel.All -> Unit
|
||||
oldItem is CategoryListModel.CategoryItem &&
|
||||
newItem is CategoryListModel.CategoryItem &&
|
||||
oldItem.category.title != newItem.category.title -> null
|
||||
else -> Unit
|
||||
override fun getChangePayload(oldItem: ListModel, newItem: ListModel): Any? {
|
||||
return super.getChangePayload(oldItem, newItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +1,38 @@
|
||||
package org.koitharu.kotatsu.favourites.ui.categories
|
||||
|
||||
import android.app.ActivityOptions
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.core.graphics.Insets
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import org.koin.android.ext.android.get
|
||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.ui.BaseActivity
|
||||
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
||||
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||
import org.koitharu.kotatsu.databinding.ActivityCategoriesBinding
|
||||
import org.koitharu.kotatsu.favourites.ui.categories.adapter.CategoryListModel
|
||||
import org.koitharu.kotatsu.favourites.ui.FavouritesActivity
|
||||
import org.koitharu.kotatsu.favourites.ui.categories.edit.FavouritesCategoryEditActivity
|
||||
import org.koitharu.kotatsu.list.ui.adapter.ListStateHolderListener
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.parsers.model.SortOrder
|
||||
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
||||
import org.koitharu.kotatsu.utils.ext.measureHeight
|
||||
|
||||
class CategoriesActivity :
|
||||
class FavouriteCategoriesActivity :
|
||||
BaseActivity<ActivityCategoriesBinding>(),
|
||||
OnListItemClickListener<FavouriteCategory>,
|
||||
View.OnClickListener,
|
||||
CategoriesEditDelegate.CategoriesEditCallback,
|
||||
AllCategoriesToggleListener {
|
||||
ListStateHolderListener {
|
||||
|
||||
private val viewModel by viewModel<FavouritesCategoriesViewModel>()
|
||||
|
||||
@@ -42,7 +44,7 @@ class CategoriesActivity :
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(ActivityCategoriesBinding.inflate(layoutInflater))
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
adapter = CategoriesAdapter(this, this)
|
||||
adapter = CategoriesAdapter(get(), this, this, this)
|
||||
editDelegate = CategoriesEditDelegate(this, this)
|
||||
binding.recyclerView.setHasFixedSize(true)
|
||||
binding.recyclerView.adapter = adapter
|
||||
@@ -50,7 +52,7 @@ class CategoriesActivity :
|
||||
reorderHelper = ItemTouchHelper(ReorderHelperCallback())
|
||||
reorderHelper.attachToRecyclerView(binding.recyclerView)
|
||||
|
||||
viewModel.allCategories.observe(this, ::onCategoriesChanged)
|
||||
viewModel.detalizedCategories.observe(this, ::onCategoriesChanged)
|
||||
viewModel.onError.observe(this, ::onError)
|
||||
}
|
||||
|
||||
@@ -61,7 +63,7 @@ class CategoriesActivity :
|
||||
}
|
||||
|
||||
override fun onItemClick(item: FavouriteCategory, view: View) {
|
||||
val menu = PopupMenu(view.context, view)
|
||||
/*val menu = PopupMenu(view.context, view)
|
||||
menu.inflate(R.menu.popup_category)
|
||||
menu.setOnMenuItemClickListener { menuItem ->
|
||||
when (menuItem.itemId) {
|
||||
@@ -70,7 +72,11 @@ class CategoriesActivity :
|
||||
}
|
||||
true
|
||||
}
|
||||
menu.show()
|
||||
menu.show()*/
|
||||
val intent = FavouritesActivity.newIntent(this, item)
|
||||
val options =
|
||||
ActivityOptions.makeScaleUpAnimation(view, view.width / 2, view.height / 2, view.width, view.height)
|
||||
startActivity(intent, options.toBundle())
|
||||
}
|
||||
|
||||
override fun onItemLongClick(item: FavouriteCategory, view: View): Boolean {
|
||||
@@ -79,9 +85,9 @@ class CategoriesActivity :
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onAllCategoriesToggle(isVisible: Boolean) {
|
||||
viewModel.setAllCategoriesVisible(isVisible)
|
||||
}
|
||||
override fun onRetryClick(error: Throwable) = Unit
|
||||
|
||||
override fun onEmptyActionClick() = Unit
|
||||
|
||||
override fun onWindowInsetsChanged(insets: Insets) {
|
||||
binding.fabAdd.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
@@ -96,9 +102,8 @@ class CategoriesActivity :
|
||||
)
|
||||
}
|
||||
|
||||
private fun onCategoriesChanged(categories: List<CategoryListModel>) {
|
||||
private fun onCategoriesChanged(categories: List<ListModel>) {
|
||||
adapter.items = categories
|
||||
binding.textViewHolder.isVisible = categories.isEmpty()
|
||||
}
|
||||
|
||||
private fun onError(e: Throwable) {
|
||||
@@ -152,6 +157,6 @@ class CategoriesActivity :
|
||||
SortOrder.RATING,
|
||||
)
|
||||
|
||||
fun newIntent(context: Context) = Intent(context, CategoriesActivity::class.java)
|
||||
fun newIntent(context: Context) = Intent(context, FavouriteCategoriesActivity::class.java)
|
||||
}
|
||||
}
|
||||
@@ -3,14 +3,15 @@ package org.koitharu.kotatsu.favourites.ui.categories
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.map
|
||||
import org.koitharu.kotatsu.base.ui.BaseViewModel
|
||||
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.prefs.observeAsFlow
|
||||
import org.koitharu.kotatsu.favourites.domain.FavouritesRepository
|
||||
import org.koitharu.kotatsu.favourites.ui.categories.adapter.CategoryListModel
|
||||
import org.koitharu.kotatsu.list.ui.model.LoadingState
|
||||
import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct
|
||||
import org.koitharu.kotatsu.utils.ext.mapItems
|
||||
import org.koitharu.kotatsu.utils.ext.requireValue
|
||||
import java.util.*
|
||||
|
||||
class FavouritesCategoriesViewModel(
|
||||
@@ -20,19 +21,25 @@ class FavouritesCategoriesViewModel(
|
||||
|
||||
private var reorderJob: Job? = null
|
||||
|
||||
val allCategories = combine(
|
||||
repository.observeCategories(),
|
||||
observeAllCategoriesVisible(),
|
||||
) { list, showAll ->
|
||||
mapCategories(list, showAll, true)
|
||||
}.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default)
|
||||
|
||||
val visibleCategories = combine(
|
||||
repository.observeCategories(),
|
||||
observeAllCategoriesVisible(),
|
||||
) { list, showAll ->
|
||||
mapCategories(list, showAll, showAll && list.isNotEmpty())
|
||||
}.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default)
|
||||
val allCategories = repository.observeCategories()
|
||||
.mapItems {
|
||||
CategoryListModel(
|
||||
mangaCount = 0,
|
||||
covers = listOf(),
|
||||
category = it,
|
||||
)
|
||||
}.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default, emptyList())
|
||||
|
||||
val detalizedCategories = repository.observeCategoriesWithDetails()
|
||||
.map {
|
||||
it.map { (category, covers) ->
|
||||
CategoryListModel(
|
||||
mangaCount = covers.size,
|
||||
covers = covers.take(3),
|
||||
category = category,
|
||||
)
|
||||
}
|
||||
}.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default, listOf(LoadingState))
|
||||
|
||||
fun deleteCategory(id: Long) {
|
||||
launchJob {
|
||||
@@ -48,30 +55,13 @@ class FavouritesCategoriesViewModel(
|
||||
val prevJob = reorderJob
|
||||
reorderJob = launchJob(Dispatchers.Default) {
|
||||
prevJob?.join()
|
||||
val items = allCategories.value ?: error("This should not happen")
|
||||
val ids = items.mapTo(ArrayList(items.size)) { it.id }
|
||||
val items = detalizedCategories.requireValue()
|
||||
val ids = items.mapNotNullTo(ArrayList(items.size)) {
|
||||
(it as? CategoryListModel)?.category?.id
|
||||
}
|
||||
Collections.swap(ids, oldPos, newPos)
|
||||
ids.remove(0L)
|
||||
repository.reorderCategories(ids)
|
||||
}
|
||||
}
|
||||
|
||||
private fun mapCategories(
|
||||
categories: List<FavouriteCategory>,
|
||||
isAllCategoriesVisible: Boolean,
|
||||
withAllCategoriesItem: Boolean,
|
||||
): List<CategoryListModel> {
|
||||
val result = ArrayList<CategoryListModel>(categories.size + 1)
|
||||
if (withAllCategoriesItem) {
|
||||
result.add(CategoryListModel.All(isAllCategoriesVisible))
|
||||
}
|
||||
categories.mapTo(result) {
|
||||
CategoryListModel.CategoryItem(it)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun observeAllCategoriesVisible() = settings.observeAsFlow(AppSettings.KEY_ALL_FAVOURITES_VISIBLE) {
|
||||
isAllFavouritesVisible
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package org.koitharu.kotatsu.favourites.ui.categories.adapter
|
||||
|
||||
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
||||
import org.koitharu.kotatsu.databinding.ItemCategoriesAllBinding
|
||||
import org.koitharu.kotatsu.favourites.ui.categories.AllCategoriesToggleListener
|
||||
|
||||
fun allCategoriesAD(
|
||||
allCategoriesToggleListener: AllCategoriesToggleListener,
|
||||
) = adapterDelegateViewBinding<CategoryListModel.All, CategoryListModel, ItemCategoriesAllBinding>(
|
||||
{ inflater, parent -> ItemCategoriesAllBinding.inflate(inflater, parent, false) }
|
||||
) {
|
||||
|
||||
binding.imageViewToggle.setOnClickListener {
|
||||
allCategoriesToggleListener.onAllCategoriesToggle(!item.isVisible)
|
||||
}
|
||||
|
||||
bind {
|
||||
binding.imageViewToggle.isChecked = item.isVisible
|
||||
}
|
||||
}
|
||||
@@ -1,30 +1,65 @@
|
||||
package org.koitharu.kotatsu.favourites.ui.categories.adapter
|
||||
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.View.OnClickListener
|
||||
import android.view.View.OnLongClickListener
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import coil.ImageLoader
|
||||
import coil.request.Disposable
|
||||
import coil.size.Scale
|
||||
import coil.util.CoilUtils
|
||||
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
||||
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||
import org.koitharu.kotatsu.databinding.ItemCategoryBinding
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.utils.ext.enqueueWith
|
||||
import org.koitharu.kotatsu.utils.ext.newImageRequest
|
||||
|
||||
fun categoryAD(
|
||||
coil: ImageLoader,
|
||||
lifecycleOwner: LifecycleOwner,
|
||||
clickListener: OnListItemClickListener<FavouriteCategory>
|
||||
) = adapterDelegateViewBinding<CategoryListModel.CategoryItem, CategoryListModel, ItemCategoryBinding>(
|
||||
) = adapterDelegateViewBinding<CategoryListModel, ListModel, ItemCategoryBinding>(
|
||||
{ inflater, parent -> ItemCategoryBinding.inflate(inflater, parent, false) }
|
||||
) {
|
||||
|
||||
binding.imageViewMore.setOnClickListener {
|
||||
clickListener.onItemClick(item.category, it)
|
||||
val eventListener = object : OnClickListener, OnLongClickListener {
|
||||
override fun onClick(v: View) = clickListener.onItemClick(item.category, v)
|
||||
override fun onLongClick(v: View) = clickListener.onItemLongClick(item.category, v)
|
||||
}
|
||||
@Suppress("ClickableViewAccessibility")
|
||||
binding.imageViewHandle.setOnTouchListener { _, event ->
|
||||
if (event.actionMasked == MotionEvent.ACTION_DOWN) {
|
||||
clickListener.onItemLongClick(item.category, itemView)
|
||||
} else {
|
||||
false
|
||||
val coverViews = arrayOf(binding.imageViewCover1, binding.imageViewCover2, binding.imageViewCover3)
|
||||
val imageRequests = arrayOfNulls<Disposable?>(coverViews.size)
|
||||
itemView.setOnClickListener(eventListener)
|
||||
itemView.setOnLongClickListener(eventListener)
|
||||
|
||||
bind {
|
||||
imageRequests.forEach { it?.dispose() }
|
||||
binding.textViewTitle.text = item.category.title
|
||||
binding.textViewSubtitle.text = context.resources.getQuantityString(
|
||||
R.plurals.items,
|
||||
item.mangaCount,
|
||||
item.mangaCount,
|
||||
)
|
||||
repeat(coverViews.size) { i ->
|
||||
imageRequests[i] = coverViews[i].newImageRequest(item.covers.getOrNull(i))
|
||||
.placeholder(R.drawable.ic_placeholder)
|
||||
.fallback(null)
|
||||
.error(R.drawable.ic_placeholder)
|
||||
.scale(Scale.FILL)
|
||||
.allowRgb565(true)
|
||||
.lifecycle(lifecycleOwner)
|
||||
.enqueueWith(coil)
|
||||
}
|
||||
}
|
||||
|
||||
bind {
|
||||
binding.textViewTitle.text = item.category.title
|
||||
onViewRecycled {
|
||||
repeat(coverViews.size) { i ->
|
||||
imageRequests[i]?.dispose()
|
||||
imageRequests[i] = null
|
||||
CoilUtils.dispose(coverViews[i])
|
||||
coverViews[i].setImageDrawable(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,59 +3,33 @@ package org.koitharu.kotatsu.favourites.ui.categories.adapter
|
||||
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
|
||||
sealed interface CategoryListModel : ListModel {
|
||||
class CategoryListModel(
|
||||
val mangaCount: Int,
|
||||
val covers: List<String>,
|
||||
val category: FavouriteCategory,
|
||||
) : ListModel {
|
||||
|
||||
val id: Long
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
class All(
|
||||
val isVisible: Boolean,
|
||||
) : CategoryListModel {
|
||||
other as CategoryListModel
|
||||
|
||||
override val id: Long = 0L
|
||||
if (mangaCount != other.mangaCount) return false
|
||||
if (covers != other.covers) return false
|
||||
if (category.id != other.category.id) return false
|
||||
if (category.title != other.category.title) return false
|
||||
if (category.order != other.category.order) return false
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as All
|
||||
|
||||
if (isVisible != other.isVisible) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return isVisible.hashCode()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
class CategoryItem(
|
||||
val category: FavouriteCategory,
|
||||
) : CategoryListModel {
|
||||
|
||||
override val id: Long
|
||||
get() = category.id
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as CategoryItem
|
||||
|
||||
if (category.id != other.category.id) return false
|
||||
if (category.title != other.category.title) return false
|
||||
if (category.order != other.category.order) return false
|
||||
if (category.isTrackingEnabled != other.category.isTrackingEnabled) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = category.id.hashCode()
|
||||
result = 31 * result + category.title.hashCode()
|
||||
result = 31 * result + category.order.hashCode()
|
||||
result = 31 * result + category.isTrackingEnabled.hashCode()
|
||||
return result
|
||||
}
|
||||
override fun hashCode(): Int {
|
||||
var result = mangaCount
|
||||
result = 31 * result + covers.hashCode()
|
||||
result = 31 * result + category.id.hashCode()
|
||||
result = 31 * result + category.title.hashCode()
|
||||
result = 31 * result + category.order.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ import org.koitharu.kotatsu.base.ui.BaseActivity
|
||||
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||
import org.koitharu.kotatsu.core.ui.titleRes
|
||||
import org.koitharu.kotatsu.databinding.ActivityCategoryEditBinding
|
||||
import org.koitharu.kotatsu.favourites.ui.categories.CategoriesActivity
|
||||
import org.koitharu.kotatsu.favourites.ui.categories.FavouriteCategoriesActivity
|
||||
import org.koitharu.kotatsu.parsers.model.SortOrder
|
||||
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
||||
|
||||
@@ -84,7 +84,7 @@ class FavouritesCategoryEditActivity : BaseActivity<ActivityCategoryEditBinding>
|
||||
}
|
||||
|
||||
override fun onItemClick(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
||||
selectedSortOrder = CategoriesActivity.SORT_ORDERS.getOrNull(position)
|
||||
selectedSortOrder = FavouriteCategoriesActivity.SORT_ORDERS.getOrNull(position)
|
||||
}
|
||||
|
||||
private fun onCategoryChanged(category: FavouriteCategory?) {
|
||||
@@ -114,7 +114,7 @@ class FavouritesCategoryEditActivity : BaseActivity<ActivityCategoryEditBinding>
|
||||
}
|
||||
|
||||
private fun initSortSpinner() {
|
||||
val entries = CategoriesActivity.SORT_ORDERS.map { getString(it.titleRes) }
|
||||
val entries = FavouriteCategoriesActivity.SORT_ORDERS.map { getString(it.titleRes) }
|
||||
val adapter = ArrayAdapter(this, android.R.layout.simple_spinner_dropdown_item, entries)
|
||||
binding.editSort.setAdapter(adapter)
|
||||
binding.editSort.onItemClickListener = this
|
||||
@@ -122,9 +122,9 @@ class FavouritesCategoryEditActivity : BaseActivity<ActivityCategoryEditBinding>
|
||||
|
||||
private fun getSelectedSortOrder(): SortOrder {
|
||||
selectedSortOrder?.let { return it }
|
||||
val entries = CategoriesActivity.SORT_ORDERS.map { getString(it.titleRes) }
|
||||
val entries = FavouriteCategoriesActivity.SORT_ORDERS.map { getString(it.titleRes) }
|
||||
val index = entries.indexOf(binding.editSort.text.toString())
|
||||
return CategoriesActivity.SORT_ORDERS.getOrNull(index) ?: SortOrder.NEWEST
|
||||
return FavouriteCategoriesActivity.SORT_ORDERS.getOrNull(index) ?: SortOrder.NEWEST
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -7,7 +7,7 @@ import androidx.core.view.MenuProvider
|
||||
import androidx.core.view.iterator
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.ui.titleRes
|
||||
import org.koitharu.kotatsu.favourites.ui.categories.CategoriesActivity
|
||||
import org.koitharu.kotatsu.favourites.ui.categories.FavouriteCategoriesActivity
|
||||
|
||||
class FavouritesListMenuProvider(
|
||||
private val viewModel: FavouritesListViewModel,
|
||||
@@ -16,7 +16,7 @@ class FavouritesListMenuProvider(
|
||||
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
|
||||
menuInflater.inflate(R.menu.opt_favourites_list, menu)
|
||||
menu.findItem(R.id.action_order)?.subMenu?.let { submenu ->
|
||||
for ((i, item) in CategoriesActivity.SORT_ORDERS.withIndex()) {
|
||||
for ((i, item) in FavouriteCategoriesActivity.SORT_ORDERS.withIndex()) {
|
||||
val menuItem = submenu.add(R.id.group_order, Menu.NONE, i, item.titleRes)
|
||||
menuItem.isCheckable = true
|
||||
}
|
||||
@@ -28,7 +28,7 @@ class FavouritesListMenuProvider(
|
||||
menu.findItem(R.id.action_order)?.subMenu?.let { submenu ->
|
||||
val selectedOrder = viewModel.sortOrder.value
|
||||
for (item in submenu) {
|
||||
val order = CategoriesActivity.SORT_ORDERS.getOrNull(item.order)
|
||||
val order = FavouriteCategoriesActivity.SORT_ORDERS.getOrNull(item.order)
|
||||
item.isChecked = order == selectedOrder
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,7 @@ class FavouritesListMenuProvider(
|
||||
return when {
|
||||
menuItem.itemId == R.id.action_order -> false
|
||||
menuItem.groupId == R.id.group_order -> {
|
||||
val order = CategoriesActivity.SORT_ORDERS.getOrNull(menuItem.order) ?: return false
|
||||
val order = FavouriteCategoriesActivity.SORT_ORDERS.getOrNull(menuItem.order) ?: return false
|
||||
viewModel.setSortOrder(order)
|
||||
true
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import androidx.core.view.MenuProvider
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.ui.dialog.RememberSelectionDialogListener
|
||||
import org.koitharu.kotatsu.favourites.ui.categories.CategoriesActivity
|
||||
import org.koitharu.kotatsu.favourites.ui.categories.FavouriteCategoriesActivity
|
||||
import org.koitharu.kotatsu.utils.ext.startOfDay
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
@@ -30,7 +30,7 @@ class LibraryMenuProvider(
|
||||
true
|
||||
}
|
||||
R.id.action_categories -> {
|
||||
context.startActivity(CategoriesActivity.newIntent(context))
|
||||
context.startActivity(FavouriteCategoriesActivity.newIntent(context))
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
|
||||
@@ -23,7 +23,7 @@ import org.koin.android.ext.android.inject
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.ui.BasePreferenceFragment
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.favourites.ui.categories.CategoriesActivity
|
||||
import org.koitharu.kotatsu.favourites.ui.categories.FavouriteCategoriesActivity
|
||||
import org.koitharu.kotatsu.settings.utils.MultiSummaryProvider
|
||||
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
|
||||
import org.koitharu.kotatsu.tracker.work.TrackerNotificationChannels
|
||||
@@ -103,7 +103,7 @@ class TrackerSettingsFragment :
|
||||
}
|
||||
}
|
||||
AppSettings.KEY_TRACK_CATEGORIES -> {
|
||||
startActivity(CategoriesActivity.newIntent(preference.context))
|
||||
startActivity(FavouriteCategoriesActivity.newIntent(preference.context))
|
||||
true
|
||||
}
|
||||
KEY_IGNORE_DOZE -> {
|
||||
|
||||
@@ -51,18 +51,6 @@
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_holder"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_margin="20dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/text_categories_holder"
|
||||
android:textAppearance="?attr/textAppearanceBody2"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||
android:id="@+id/fab_add"
|
||||
android:layout_width="wrap_content"
|
||||
|
||||
@@ -1,41 +1,85 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
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="?android:listPreferredItemHeightSmall"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:background="?android:windowBackground"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
tools:ignore="Overdraw">
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/list_selector"
|
||||
android:paddingVertical="12dp"
|
||||
android:paddingStart="?android:listPreferredItemPaddingStart"
|
||||
android:paddingEnd="?listPreferredItemPaddingEnd">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView_handle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="?listPreferredItemPaddingStart"
|
||||
android:scaleType="center"
|
||||
android:src="@drawable/ic_reorder_handle" />
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/imageView_cover3"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="64dp"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:scaleType="centerCrop"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintDimensionRatio="W,13:18"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Kotatsu.Cover.Small"
|
||||
tools:src="@tools:sample/backgrounds/scenic" />
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/imageView_cover2"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="64dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:alpha="50"
|
||||
android:scaleType="centerCrop"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintDimensionRatio="W,13:18"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Kotatsu.Cover.Small"
|
||||
tools:src="@tools:sample/backgrounds/scenic" />
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/imageView_cover1"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="64dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:alpha="120"
|
||||
android:scaleType="centerCrop"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintDimensionRatio="W,13:18"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Kotatsu.Cover.Small"
|
||||
tools:src="@tools:sample/backgrounds/scenic" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="marquee"
|
||||
android:fadingEdge="horizontal"
|
||||
android:layout_marginStart="@dimen/margin_normal"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?attr/textAppearanceBodyLarge"
|
||||
app:layout_constraintBottom_toTopOf="@id/textView_subtitle"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/imageView_cover3"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
tools:text="@tools:sample/lorem[1]" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView_more"
|
||||
android:layout_width="wrap_content"
|
||||
<TextView
|
||||
android:id="@+id/textView_subtitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:padding="?listPreferredItemPaddingEnd"
|
||||
android:scaleType="center"
|
||||
app:srcCompat="@drawable/abc_ic_menu_overflow_material" />
|
||||
android:layout_marginStart="@dimen/margin_normal"
|
||||
android:layout_marginTop="4dp"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?attr/textAppearanceBodySmall"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/imageView_cover3"
|
||||
app:layout_constraintTop_toBottomOf="@id/textView_title"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
tools:text="@tools:sample/lorem[1]" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
Reference in New Issue
Block a user