From fbd0f25b8f9e7ff5af30e04c3aba73501318d88f Mon Sep 17 00:00:00 2001 From: Koitharu Date: Wed, 4 Aug 2021 08:32:20 +0300 Subject: [PATCH] #8 Configure sort order for each favourites category --- app/build.gradle | 8 ++-- .../kotatsu/core/backup/BackupRepository.kt | 1 + .../kotatsu/core/backup/RestoreRepository.kt | 4 +- .../kotatsu/core/db/DatabaseModule.kt | 1 + .../koitharu/kotatsu/core/db/MangaDatabase.kt | 2 +- .../core/db/migrations/Migration8To9.kt | 12 ++++++ .../kotatsu/core/model/FavouriteCategory.kt | 3 +- .../favourites/data/FavouriteCategoriesDao.kt | 11 ++++-- .../data/FavouriteCategoryEntity.kt | 7 +++- .../kotatsu/favourites/data/FavouritesDao.kt | 36 +++++++++++++++--- .../favourites/domain/FavouritesRepository.kt | 37 +++++++++++++------ .../ui/FavouritesContainerFragment.kt | 29 +++++++++++++-- .../favourites/ui/FavouritesPagerAdapter.kt | 5 ++- .../ui/categories/CategoriesActivity.kt | 32 +++++++++++++++- .../ui/categories/CategoriesAdapter.kt | 21 +++++++++-- .../FavouritesCategoriesViewModel.kt | 14 +++++-- .../ui/list/FavouritesListViewModel.kt | 7 +++- .../koitharu/kotatsu/widget/WidgetUpdater.kt | 3 +- app/src/main/res/menu/popup_category.xml | 13 +++++++ 19 files changed, 201 insertions(+), 45 deletions(-) create mode 100644 app/src/main/java/org/koitharu/kotatsu/core/db/migrations/Migration8To9.kt diff --git a/app/build.gradle b/app/build.gradle index d8fb63356..895e66295 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -70,19 +70,19 @@ dependencies { implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.1' implementation 'androidx.core:core-ktx:1.6.0' - implementation 'androidx.activity:activity-ktx:1.2.3' - implementation 'androidx.fragment:fragment-ktx:1.3.5' + implementation 'androidx.activity:activity-ktx:1.3.0' + implementation 'androidx.fragment:fragment-ktx:1.3.6' implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1' implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1' implementation 'androidx.lifecycle:lifecycle-service:2.3.1' implementation 'androidx.lifecycle:lifecycle-process:2.3.1' - implementation 'androidx.constraintlayout:constraintlayout:2.0.4' + implementation 'androidx.constraintlayout:constraintlayout:2.1.0' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' implementation 'androidx.recyclerview:recyclerview:1.2.1' implementation 'androidx.viewpager2:viewpager2:1.1.0-alpha01' implementation 'androidx.preference:preference-ktx:1.1.1' - implementation 'androidx.work:work-runtime-ktx:2.6.0-beta01' + implementation 'androidx.work:work-runtime-ktx:2.6.0-beta02' implementation 'com.google.android.material:material:1.4.0' //noinspection LifecycleAnnotationProcessorWithJava8 kapt 'androidx.lifecycle:lifecycle-compiler:2.3.1' diff --git a/app/src/main/java/org/koitharu/kotatsu/core/backup/BackupRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/backup/BackupRepository.kt index 6d934330a..eb6cb8579 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/backup/BackupRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/backup/BackupRepository.kt @@ -118,6 +118,7 @@ class BackupRepository(private val db: MangaDatabase) { jo.put("created_at", createdAt) jo.put("sort_key", sortKey) jo.put("title", title) + jo.put("order", order) return jo } diff --git a/app/src/main/java/org/koitharu/kotatsu/core/backup/RestoreRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/backup/RestoreRepository.kt index 86b732b27..9626ddb13 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/backup/RestoreRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/backup/RestoreRepository.kt @@ -5,6 +5,7 @@ import org.json.JSONObject import org.koitharu.kotatsu.core.db.MangaDatabase import org.koitharu.kotatsu.core.db.entity.MangaEntity import org.koitharu.kotatsu.core.db.entity.TagEntity +import org.koitharu.kotatsu.core.model.SortOrder import org.koitharu.kotatsu.favourites.data.FavouriteCategoryEntity import org.koitharu.kotatsu.favourites.data.FavouriteEntity import org.koitharu.kotatsu.history.data.HistoryEntity @@ -101,7 +102,8 @@ class RestoreRepository(private val db: MangaDatabase) { categoryId = json.getInt("category_id"), createdAt = json.getLong("created_at"), sortKey = json.getInt("sort_key"), - title = json.getString("title") + title = json.getString("title"), + order = json.getStringOrNull("order") ?: SortOrder.NEWEST.name, ) private fun parseFavourite(json: JSONObject) = FavouriteEntity( diff --git a/app/src/main/java/org/koitharu/kotatsu/core/db/DatabaseModule.kt b/app/src/main/java/org/koitharu/kotatsu/core/db/DatabaseModule.kt index de6aead18..dc390f0f3 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/db/DatabaseModule.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/db/DatabaseModule.kt @@ -20,6 +20,7 @@ val databaseModule Migration5To6(), Migration6To7(), Migration7To8(), + Migration8To9(), ).addCallback( DatabasePrePopulateCallback(androidContext().resources) ).build() diff --git a/app/src/main/java/org/koitharu/kotatsu/core/db/MangaDatabase.kt b/app/src/main/java/org/koitharu/kotatsu/core/db/MangaDatabase.kt index f0844571e..04b1ba764 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/db/MangaDatabase.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/db/MangaDatabase.kt @@ -16,7 +16,7 @@ import org.koitharu.kotatsu.history.data.HistoryEntity MangaEntity::class, TagEntity::class, HistoryEntity::class, MangaTagsEntity::class, FavouriteCategoryEntity::class, FavouriteEntity::class, MangaPrefsEntity::class, TrackEntity::class, TrackLogEntity::class, SuggestionEntity::class - ], version = 8 + ], version = 9 ) abstract class MangaDatabase : RoomDatabase() { diff --git a/app/src/main/java/org/koitharu/kotatsu/core/db/migrations/Migration8To9.kt b/app/src/main/java/org/koitharu/kotatsu/core/db/migrations/Migration8To9.kt new file mode 100644 index 000000000..84eedc797 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/core/db/migrations/Migration8To9.kt @@ -0,0 +1,12 @@ +package org.koitharu.kotatsu.core.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase +import org.koitharu.kotatsu.core.model.SortOrder + +class Migration8To9 : Migration(8, 9) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE favourite_categories ADD COLUMN `order` TEXT NOT NULL DEFAULT ${SortOrder.NEWEST.name}") + } +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/core/model/FavouriteCategory.kt b/app/src/main/java/org/koitharu/kotatsu/core/model/FavouriteCategory.kt index 5eedf24b8..6998a1a08 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/model/FavouriteCategory.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/model/FavouriteCategory.kt @@ -9,5 +9,6 @@ data class FavouriteCategory( val id: Long, val title: String, val sortKey: Int, - val createdAt: Date + val order: SortOrder, + val createdAt: Date, ) : Parcelable \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/data/FavouriteCategoriesDao.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/data/FavouriteCategoriesDao.kt index e56cf6999..436dc12ea 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/data/FavouriteCategoriesDao.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/data/FavouriteCategoriesDao.kt @@ -2,7 +2,6 @@ package org.koitharu.kotatsu.favourites.data import androidx.room.* import kotlinx.coroutines.flow.Flow -import org.koitharu.kotatsu.core.model.FavouriteCategory @Dao abstract class FavouriteCategoriesDao { @@ -13,6 +12,9 @@ abstract class FavouriteCategoriesDao { @Query("SELECT * FROM favourite_categories ORDER BY sort_key") abstract fun observeAll(): Flow> + @Query("SELECT * FROM favourite_categories WHERE category_id = :id") + abstract fun observe(id: Long): Flow + @Insert(onConflict = OnConflictStrategy.ABORT) abstract suspend fun insert(category: FavouriteCategoryEntity): Long @@ -23,10 +25,13 @@ abstract class FavouriteCategoriesDao { abstract suspend fun delete(id: Long) @Query("UPDATE favourite_categories SET title = :title WHERE category_id = :id") - abstract suspend fun update(id: Long, title: String) + abstract suspend fun updateTitle(id: Long, title: String) + + @Query("UPDATE favourite_categories SET `order` = :order WHERE category_id = :id") + abstract suspend fun updateOrder(id: Long, order: String) @Query("UPDATE favourite_categories SET sort_key = :sortKey WHERE category_id = :id") - abstract suspend fun update(id: Long, sortKey: Int) + abstract suspend fun updateSortKey(id: Long, sortKey: Int) @Query("SELECT MAX(sort_key) FROM favourite_categories") protected abstract suspend fun getMaxSortKey(): Int? diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/data/FavouriteCategoryEntity.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/data/FavouriteCategoryEntity.kt index 1da715ae5..f66c87fae 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/data/FavouriteCategoryEntity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/data/FavouriteCategoryEntity.kt @@ -4,6 +4,7 @@ import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.PrimaryKey import org.koitharu.kotatsu.core.model.FavouriteCategory +import org.koitharu.kotatsu.core.model.SortOrder import java.util.* @Entity(tableName = "favourite_categories") @@ -12,13 +13,15 @@ data class FavouriteCategoryEntity( @ColumnInfo(name = "category_id") val categoryId: Int, @ColumnInfo(name = "created_at") val createdAt: Long, @ColumnInfo(name = "sort_key") val sortKey: Int, - @ColumnInfo(name = "title") val title: String + @ColumnInfo(name = "title") val title: String, + @ColumnInfo(name = "order") val order: String, ) { fun toFavouriteCategory(id: Long? = null) = FavouriteCategory( id = id ?: categoryId.toLong(), title = title, sortKey = sortKey, - createdAt = Date(createdAt) + order = SortOrder.values().find { x -> x.name == order } ?: SortOrder.NEWEST, + createdAt = Date(createdAt), ) } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/data/FavouritesDao.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/data/FavouritesDao.kt index 7928ba470..cf7d0f8f9 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/data/FavouritesDao.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/data/FavouritesDao.kt @@ -1,8 +1,11 @@ package org.koitharu.kotatsu.favourites.data import androidx.room.* +import androidx.sqlite.db.SimpleSQLiteQuery +import androidx.sqlite.db.SupportSQLiteQuery import kotlinx.coroutines.flow.Flow import org.koitharu.kotatsu.core.db.entity.MangaEntity +import org.koitharu.kotatsu.core.model.SortOrder @Dao abstract class FavouritesDao { @@ -11,9 +14,13 @@ abstract class FavouritesDao { @Query("SELECT * FROM favourites GROUP BY manga_id ORDER BY created_at DESC") abstract suspend fun findAll(): List - @Transaction - @Query("SELECT * FROM favourites GROUP BY manga_id ORDER BY created_at DESC") - abstract fun observeAll(): Flow> + fun observeAll(order: SortOrder): Flow> { + val orderBy = getOrderBy(order) + val query = SimpleSQLiteQuery( + "SELECT * FROM favourites LEFT JOIN manga ON favourites.manga_id = manga.manga_id GROUP BY favourites.manga_id ORDER BY $orderBy", + ) + return observeAllRaw(query) + } @Transaction @Query("SELECT * FROM favourites GROUP BY manga_id ORDER BY created_at DESC LIMIT :limit OFFSET :offset") @@ -23,9 +30,14 @@ abstract class FavouritesDao { @Query("SELECT * FROM favourites WHERE category_id = :categoryId GROUP BY manga_id ORDER BY created_at DESC") abstract suspend fun findAll(categoryId: Long): List - @Transaction - @Query("SELECT * FROM favourites WHERE category_id = :categoryId GROUP BY manga_id ORDER BY created_at DESC") - abstract fun observeAll(categoryId: Long): Flow> + fun observeAll(categoryId: Long, order: SortOrder): Flow> { + val orderBy = getOrderBy(order) + val query = SimpleSQLiteQuery( + "SELECT * FROM favourites LEFT JOIN manga ON favourites.manga_id = manga.manga_id WHERE category_id = ? GROUP BY favourites.manga_id ORDER BY $orderBy", + arrayOf(categoryId), + ) + return observeAllRaw(query) + } @Transaction @Query("SELECT * FROM favourites WHERE category_id = :categoryId GROUP BY manga_id ORDER BY created_at DESC LIMIT :limit OFFSET :offset") @@ -63,4 +75,16 @@ abstract class FavouritesDao { insert(entity) } } + + @Transaction + @RawQuery(observedEntities = [FavouriteEntity::class]) + protected abstract fun observeAllRaw(query: SupportSQLiteQuery): Flow> + + private fun getOrderBy(sortOrder: SortOrder) = when(sortOrder) { + SortOrder.RATING -> "rating DESC" + SortOrder.NEWEST, + SortOrder.UPDATED -> "created_at DESC" + SortOrder.ALPHABETICAL -> "title ASC" + else -> throw IllegalArgumentException("Sort order $sortOrder is not supported") + } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/domain/FavouritesRepository.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/domain/FavouritesRepository.kt index 15cefac92..48d6a34aa 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/domain/FavouritesRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/domain/FavouritesRepository.kt @@ -3,12 +3,14 @@ package org.koitharu.kotatsu.favourites.domain import androidx.room.withTransaction import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map import org.koitharu.kotatsu.core.db.MangaDatabase import org.koitharu.kotatsu.core.db.entity.MangaEntity import org.koitharu.kotatsu.core.db.entity.TagEntity import org.koitharu.kotatsu.core.model.FavouriteCategory import org.koitharu.kotatsu.core.model.Manga +import org.koitharu.kotatsu.core.model.SortOrder import org.koitharu.kotatsu.favourites.data.FavouriteCategoryEntity import org.koitharu.kotatsu.favourites.data.FavouriteEntity import org.koitharu.kotatsu.utils.ext.mapItems @@ -21,26 +23,26 @@ class FavouritesRepository(private val db: MangaDatabase) { return entities.map { it.manga.toManga(it.tags.mapToSet(TagEntity::toMangaTag)) } } - fun observeAll(): Flow> { - return db.favouritesDao.observeAll() + fun observeAll(order: SortOrder): Flow> { + return db.favouritesDao.observeAll(order) .mapItems { it.manga.toManga(it.tags.mapToSet(TagEntity::toMangaTag)) } } - suspend fun getAllManga(offset: Int): List { - val entities = db.favouritesDao.findAll(offset, 20) - return entities.map { it.manga.toManga(it.tags.mapToSet(TagEntity::toMangaTag)) } - } - suspend fun getManga(categoryId: Long): List { val entities = db.favouritesDao.findAll(categoryId) return entities.map { it.manga.toManga(it.tags.mapToSet(TagEntity::toMangaTag)) } } - fun observeAll(categoryId: Long): Flow> { - return db.favouritesDao.observeAll(categoryId) + fun observeAll(categoryId: Long, order: SortOrder): Flow> { + return db.favouritesDao.observeAll(categoryId, order) .mapItems { it.manga.toManga(it.tags.mapToSet(TagEntity::toMangaTag)) } } + fun observeAll(categoryId: Long): Flow> { + return observeOrder(categoryId) + .flatMapLatest { order -> observeAll(categoryId, order) } + } + suspend fun getManga(categoryId: Long, offset: Int): List { val entities = db.favouritesDao.findAll(categoryId, offset, 20) return entities.map { it.manga.toManga(it.tags.mapToSet(TagEntity::toMangaTag)) } @@ -77,25 +79,30 @@ class FavouritesRepository(private val db: MangaDatabase) { title = title, createdAt = System.currentTimeMillis(), sortKey = db.favouriteCategoriesDao.getNextSortKey(), - categoryId = 0 + categoryId = 0, + order = SortOrder.UPDATED.name, ) val id = db.favouriteCategoriesDao.insert(entity) return entity.toFavouriteCategory(id) } suspend fun renameCategory(id: Long, title: String) { - db.favouriteCategoriesDao.update(id, title) + db.favouriteCategoriesDao.updateTitle(id, title) } suspend fun removeCategory(id: Long) { db.favouriteCategoriesDao.delete(id) } + suspend fun setCategoryOrder(id: Long, order: SortOrder) { + db.favouriteCategoriesDao.updateOrder(id, order.name) + } + suspend fun reorderCategories(orderedIds: List) { val dao = db.favouriteCategoriesDao db.withTransaction { for ((i, id) in orderedIds.withIndex()) { - dao.update(id, i) + dao.updateSortKey(id, i) } } } @@ -117,4 +124,10 @@ class FavouritesRepository(private val db: MangaDatabase) { suspend fun removeFromFavourites(manga: Manga) { db.favouritesDao.delete(manga.id) } + + private fun observeOrder(categoryId: Long): Flow { + return db.favouriteCategoriesDao.observe(categoryId) + .map { x -> SortOrder.values().find { it.name == x.order } ?: SortOrder.NEWEST } + .distinctUntilChanged() + } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/FavouritesContainerFragment.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/FavouritesContainerFragment.kt index c20c2c6f3..43ac54837 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/FavouritesContainerFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/FavouritesContainerFragment.kt @@ -11,6 +11,7 @@ import org.koin.androidx.viewmodel.ext.android.viewModel import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseFragment import org.koitharu.kotatsu.core.model.FavouriteCategory +import org.koitharu.kotatsu.core.model.SortOrder import org.koitharu.kotatsu.databinding.FragmentFavouritesBinding import org.koitharu.kotatsu.favourites.ui.categories.CategoriesActivity import org.koitharu.kotatsu.favourites.ui.categories.CategoriesEditDelegate @@ -19,7 +20,6 @@ import org.koitharu.kotatsu.utils.RecycledViewPoolHolder import org.koitharu.kotatsu.utils.ext.getDisplayMessage import org.koitharu.kotatsu.utils.ext.showPopupMenu import java.util.* -import kotlin.collections.ArrayList class FavouritesContainerFragment : BaseFragment(), FavouritesTabLongClickListener, CategoriesEditDelegate.CategoriesEditCallback, @@ -100,11 +100,19 @@ class FavouritesContainerFragment : BaseFragment(), override fun onTabLongClick(tabView: View, category: FavouriteCategory): Boolean { val menuRes = if (category.id == 0L) R.menu.popup_category_empty else R.menu.popup_category - tabView.showPopupMenu(menuRes) { + tabView.showPopupMenu(menuRes, { menu -> + createOrderSubmenu(menu, category) + }) { when (it.itemId) { R.id.action_remove -> editDelegate.deleteCategory(category) R.id.action_rename -> editDelegate.renameCategory(category) R.id.action_create -> editDelegate.createCategory() + R.id.action_order -> return@showPopupMenu false + else -> { + val order = CategoriesActivity.SORT_ORDERS.getOrNull(it.order) + ?: return@showPopupMenu false + viewModel.setCategoryOrder(category.id, order) + } } true } @@ -125,11 +133,26 @@ class FavouritesContainerFragment : BaseFragment(), private fun wrapCategories(categories: List): List { val data = ArrayList(categories.size + 1) - data += FavouriteCategory(0L, getString(R.string.all_favourites), -1, Date()) + data += FavouriteCategory(0L, getString(R.string.all_favourites), -1, SortOrder.NEWEST, Date()) data += categories return data } + private fun createOrderSubmenu(menu: Menu, category: FavouriteCategory) { + val submenu = menu.findItem(R.id.action_order)?.subMenu ?: return + for ((i, item) in CategoriesActivity.SORT_ORDERS.withIndex()) { + val menuItem = submenu.add( + R.id.group_order, + Menu.NONE, + i, + item.titleRes + ) + menuItem.isCheckable = true + menuItem.isChecked = item == category.order + } + submenu.setGroupCheckable(R.id.group_order, true, true) + } + companion object { fun newInstance() = FavouritesContainerFragment() diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/FavouritesPagerAdapter.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/FavouritesPagerAdapter.kt index 83368d802..29de80809 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/FavouritesPagerAdapter.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/FavouritesPagerAdapter.kt @@ -36,7 +36,7 @@ class FavouritesPagerAdapter( override fun onConfigureTab(tab: TabLayout.Tab, position: Int) { val item = differ.currentList[position] tab.text = item.title - tab.view.tag = item + tab.view.tag = item.id tab.view.setOnLongClickListener(this) } @@ -45,7 +45,8 @@ class FavouritesPagerAdapter( } override fun onLongClick(v: View): Boolean { - val item = v.tag as? FavouriteCategory ?: return false + val itemId = v.tag as? Long ?: return false + val item = differ.currentList.find { x -> x.id == itemId } ?: return false return longClickListener.onTabLongClick(v, item) } diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/CategoriesActivity.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/CategoriesActivity.kt index 6b4217f77..17639f06f 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/CategoriesActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/CategoriesActivity.kt @@ -5,6 +5,7 @@ import android.content.Intent import android.content.res.ColorStateList import android.graphics.Color import android.os.Bundle +import android.view.Menu import android.view.View import android.view.ViewGroup import androidx.core.graphics.Insets @@ -20,6 +21,7 @@ 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.core.model.SortOrder import org.koitharu.kotatsu.databinding.ActivityCategoriesBinding import org.koitharu.kotatsu.utils.ext.getDisplayMessage import org.koitharu.kotatsu.utils.ext.showPopupMenu @@ -61,10 +63,17 @@ class CategoriesActivity : BaseActivity(), } override fun onItemClick(item: FavouriteCategory, view: View) { - view.showPopupMenu(R.menu.popup_category) { + view.showPopupMenu(R.menu.popup_category, { menu -> + createOrderSubmenu(menu, item) + }) { when (it.itemId) { R.id.action_remove -> editDelegate.deleteCategory(item) R.id.action_rename -> editDelegate.renameCategory(item) + R.id.action_order -> return@showPopupMenu false + else -> { + val order = SORT_ORDERS.getOrNull(it.order) ?: return@showPopupMenu false + viewModel.setCategoryOrder(item.id, order) + } } true } @@ -117,6 +126,21 @@ class CategoriesActivity : BaseActivity(), viewModel.createCategory(name) } + private fun createOrderSubmenu(menu: Menu, category: FavouriteCategory) { + val submenu = menu.findItem(R.id.action_order)?.subMenu ?: return + for ((i, item) in SORT_ORDERS.withIndex()) { + val menuItem = submenu.add( + R.id.group_order, + Menu.NONE, + i, + item.titleRes + ) + menuItem.isCheckable = true + menuItem.isChecked = item == category.order + } + submenu.setGroupCheckable(R.id.group_order, true, true) + } + private inner class ReorderHelperCallback : ItemTouchHelper.SimpleCallback( ItemTouchHelper.DOWN or ItemTouchHelper.UP, 0 ) { @@ -145,6 +169,12 @@ class CategoriesActivity : BaseActivity(), companion object { + val SORT_ORDERS = arrayOf( + SortOrder.ALPHABETICAL, + SortOrder.NEWEST, + SortOrder.RATING, + ) + fun newIntent(context: Context) = Intent(context, CategoriesActivity::class.java) } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/CategoriesAdapter.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/CategoriesAdapter.kt index 5bc88aa05..adf19ca9c 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/CategoriesAdapter.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/CategoriesAdapter.kt @@ -6,7 +6,7 @@ import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener import org.koitharu.kotatsu.core.model.FavouriteCategory class CategoriesAdapter( - onItemClickListener: OnListItemClickListener + onItemClickListener: OnListItemClickListener, ) : AsyncListDifferDelegationAdapter(DiffCallback()) { init { @@ -20,12 +20,27 @@ class CategoriesAdapter( private class DiffCallback : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: FavouriteCategory, newItem: FavouriteCategory): Boolean { + override fun areItemsTheSame( + oldItem: FavouriteCategory, + newItem: FavouriteCategory, + ): Boolean { return oldItem.id == newItem.id } - override fun areContentsTheSame(oldItem: FavouriteCategory, newItem: FavouriteCategory): Boolean { + override fun areContentsTheSame( + oldItem: FavouriteCategory, + newItem: FavouriteCategory, + ): Boolean { return oldItem.id == newItem.id && oldItem.title == newItem.title + && oldItem.order == newItem.order + } + + override fun getChangePayload( + oldItem: FavouriteCategory, + newItem: FavouriteCategory, + ): Any? = when { + oldItem.title == newItem.title && oldItem.order != newItem.order -> newItem.order + else -> super.getChangePayload(oldItem, newItem) } } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/FavouritesCategoriesViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/FavouritesCategoriesViewModel.kt index 8c648a47b..a9202d749 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/FavouritesCategoriesViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/FavouritesCategoriesViewModel.kt @@ -4,10 +4,10 @@ import androidx.lifecycle.viewModelScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import org.koitharu.kotatsu.base.ui.BaseViewModel +import org.koitharu.kotatsu.core.model.SortOrder import org.koitharu.kotatsu.favourites.domain.FavouritesRepository import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct import java.util.* -import kotlin.collections.ArrayList class FavouritesCategoriesViewModel( private val repository: FavouritesRepository @@ -19,23 +19,29 @@ class FavouritesCategoriesViewModel( .asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default) fun createCategory(name: String) { - launchJob(Dispatchers.Default) { + launchJob { repository.addCategory(name) } } fun renameCategory(id: Long, name: String) { - launchJob(Dispatchers.Default) { + launchJob { repository.renameCategory(id, name) } } fun deleteCategory(id: Long) { - launchJob(Dispatchers.Default) { + launchJob { repository.removeCategory(id) } } + fun setCategoryOrder(id: Long, order: SortOrder) { + launchJob { + repository.setCategoryOrder(id, order) + } + } + fun reorderCategories(oldPos: Int, newPos: Int) { val prevJob = reorderJob reorderJob = launchJob(Dispatchers.Default) { diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/list/FavouritesListViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/list/FavouritesListViewModel.kt index ec13864b9..344c3d5a1 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/list/FavouritesListViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/list/FavouritesListViewModel.kt @@ -6,6 +6,7 @@ import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.combine import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.model.Manga +import org.koitharu.kotatsu.core.model.SortOrder import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.favourites.domain.FavouritesRepository import org.koitharu.kotatsu.list.ui.MangaListViewModel @@ -22,7 +23,11 @@ class FavouritesListViewModel( ) : MangaListViewModel(settings) { override val content = combine( - if (categoryId == 0L) repository.observeAll() else repository.observeAll(categoryId), + if (categoryId == 0L) { + repository.observeAll(SortOrder.NEWEST) + } else { + repository.observeAll(categoryId) + }, createListModeFlow() ) { list, mode -> when { diff --git a/app/src/main/java/org/koitharu/kotatsu/widget/WidgetUpdater.kt b/app/src/main/java/org/koitharu/kotatsu/widget/WidgetUpdater.kt index 793d0d7c3..16d12ebbd 100644 --- a/app/src/main/java/org/koitharu/kotatsu/widget/WidgetUpdater.kt +++ b/app/src/main/java/org/koitharu/kotatsu/widget/WidgetUpdater.kt @@ -8,6 +8,7 @@ import kotlinx.coroutines.CancellationException import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.retry +import org.koitharu.kotatsu.core.model.SortOrder import org.koitharu.kotatsu.favourites.domain.FavouritesRepository import org.koitharu.kotatsu.history.domain.HistoryRepository import org.koitharu.kotatsu.utils.ext.processLifecycleScope @@ -17,7 +18,7 @@ import org.koitharu.kotatsu.widget.shelf.ShelfWidgetProvider class WidgetUpdater(private val context: Context) { fun subscribeToFavourites(repository: FavouritesRepository) { - repository.observeAll() + repository.observeAll(SortOrder.NEWEST) .onEach { updateWidget(ShelfWidgetProvider::class.java) } .retry { error -> error !is CancellationException } .launchIn(processLifecycleScope) diff --git a/app/src/main/res/menu/popup_category.xml b/app/src/main/res/menu/popup_category.xml index 92dd0a0e9..1c4fc96d2 100644 --- a/app/src/main/res/menu/popup_category.xml +++ b/app/src/main/res/menu/popup_category.xml @@ -10,4 +10,17 @@ android:id="@+id/action_rename" android:title="@string/rename" /> + + + + + + + + + \ No newline at end of file