From 3588270742fd6851fe1d9f224bf18d4c9a9b0f27 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Sat, 16 May 2020 08:49:34 +0300 Subject: [PATCH] Show favourites by categories and manager categories order --- app/build.gradle | 6 +- .../java/org/koitharu/kotatsu/KotatsuApp.kt | 3 +- .../kotatsu/core/db/FavouriteCategoriesDao.kt | 14 ++- .../koitharu/kotatsu/core/db/FavouritesDao.kt | 8 ++ .../koitharu/kotatsu/core/db/MangaDatabase.kt | 2 +- .../core/db/entity/FavouriteCategoryEntity.kt | 2 + .../core/db/migrations/Migration4To5.kt | 11 +++ .../kotatsu/core/model/FavouriteCategory.kt | 1 + .../domain/favourites/FavouritesRepository.kt | 33 ++++++- .../favourites/OnFavouritesChangeListener.kt | 2 + .../ui/details/MangaDetailsPresenter.kt | 2 + .../koitharu/kotatsu/ui/main/MainActivity.kt | 4 +- .../favourites/FavouritesContainerFragment.kt | 88 +++++++++++++++++++ .../list/favourites/FavouritesListFragment.kt | 41 ++++----- .../favourites/FavouritesListPresenter.kt | 4 +- .../list/favourites/FavouritesPagerAdapter.kt | 32 +++++++ .../categories/CategoriesActivity.kt | 28 ++++++ .../categories/CategoriesAdapter.kt | 29 +++++- .../favourites/categories/CategoryHolder.kt | 2 +- .../FavouriteCategoriesPresenter.kt | 34 +++++-- .../sources/SourcesSettingsFragment.kt | 2 +- .../kotatsu/ui/widget/WidgetUpdater.kt | 2 + .../ui/widget/shelf/ShelfConfigActivity.kt | 2 +- .../ui/widget/shelf/ShelfListFactory.kt | 8 +- app/src/main/res/drawable/ic_more.xml | 11 +++ app/src/main/res/layout/activity_details.xml | 1 + .../main/res/layout/fragment_favourites.xml | 21 +++++ app/src/main/res/layout/item_category.xml | 46 +++++++--- app/src/main/res/values-ru/strings.xml | 2 + app/src/main/res/values/strings.xml | 2 + gradle/wrapper/gradle-wrapper.properties | 4 +- 31 files changed, 383 insertions(+), 64 deletions(-) create mode 100644 app/src/main/java/org/koitharu/kotatsu/core/db/migrations/Migration4To5.kt create mode 100644 app/src/main/java/org/koitharu/kotatsu/ui/main/list/favourites/FavouritesContainerFragment.kt create mode 100644 app/src/main/java/org/koitharu/kotatsu/ui/main/list/favourites/FavouritesPagerAdapter.kt create mode 100644 app/src/main/res/drawable/ic_more.xml create mode 100644 app/src/main/res/layout/fragment_favourites.xml diff --git a/app/build.gradle b/app/build.gradle index 4896f54be..dcff8142a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -60,8 +60,8 @@ androidExtensions { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar']) implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.5' - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.5' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.6' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.6' implementation 'androidx.core:core-ktx:1.3.0-rc01' implementation 'androidx.appcompat:appcompat:1.2.0-beta01' @@ -91,7 +91,7 @@ dependencies { implementation 'org.jsoup:jsoup:1.13.1' implementation 'org.koin:koin-android:2.1.5' - implementation 'io.coil-kt:coil:0.10.1' + implementation 'io.coil-kt:coil:0.11.0' implementation 'com.davemorrissey.labs:subsampling-scale-image-view:3.10.0' implementation 'com.tomclaw.cache:cache:1.0' diff --git a/app/src/main/java/org/koitharu/kotatsu/KotatsuApp.kt b/app/src/main/java/org/koitharu/kotatsu/KotatsuApp.kt index 47b68319d..50f28cec9 100644 --- a/app/src/main/java/org/koitharu/kotatsu/KotatsuApp.kt +++ b/app/src/main/java/org/koitharu/kotatsu/KotatsuApp.kt @@ -18,6 +18,7 @@ import org.koitharu.kotatsu.core.db.MangaDatabase import org.koitharu.kotatsu.core.db.migrations.Migration1To2 import org.koitharu.kotatsu.core.db.migrations.Migration2To3 import org.koitharu.kotatsu.core.db.migrations.Migration3To4 +import org.koitharu.kotatsu.core.db.migrations.Migration4To5 import org.koitharu.kotatsu.core.local.CbzFetcher import org.koitharu.kotatsu.core.local.PagesCache import org.koitharu.kotatsu.core.local.cookies.PersistentCookieJar @@ -124,5 +125,5 @@ class KotatsuApp : Application() { applicationContext, MangaDatabase::class.java, "kotatsu-db" - ).addMigrations(Migration1To2, Migration2To3, Migration3To4) + ).addMigrations(Migration1To2, Migration2To3, Migration3To4, Migration4To5) } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/core/db/FavouriteCategoriesDao.kt b/app/src/main/java/org/koitharu/kotatsu/core/db/FavouriteCategoriesDao.kt index cfd397c83..8753431f7 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/db/FavouriteCategoriesDao.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/db/FavouriteCategoriesDao.kt @@ -9,8 +9,8 @@ import org.koitharu.kotatsu.core.db.entity.FavouriteCategoryEntity @Dao abstract class FavouriteCategoriesDao { - @Query("SELECT category_id,title,created_at FROM favourite_categories ORDER BY :orderBy") - abstract suspend fun findAll(orderBy: String): List + @Query("SELECT * FROM favourite_categories ORDER BY sort_key") + abstract suspend fun findAll(): List @Insert(onConflict = OnConflictStrategy.ABORT) abstract suspend fun insert(category: FavouriteCategoryEntity): Long @@ -20,4 +20,14 @@ abstract class FavouriteCategoriesDao { @Query("UPDATE favourite_categories SET title = :title WHERE category_id = :id") abstract suspend fun update(id: Long, title: String) + + @Query("UPDATE favourite_categories SET sort_key = :sortKey WHERE category_id = :id") + abstract suspend fun update(id: Long, sortKey: Int) + + @Query("SELECT MAX(sort_key) FROM favourite_categories") + protected abstract suspend fun getMaxSortKey(): Int? + + suspend fun getNextSortKey(): Int { + return (getMaxSortKey() ?: 0) + 1 + } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/core/db/FavouritesDao.kt b/app/src/main/java/org/koitharu/kotatsu/core/db/FavouritesDao.kt index b1f93dea3..78e6c1a05 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/db/FavouritesDao.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/db/FavouritesDao.kt @@ -8,10 +8,18 @@ import org.koitharu.kotatsu.core.db.entity.MangaEntity @Dao abstract class FavouritesDao { + @Transaction + @Query("SELECT * FROM favourites GROUP BY manga_id ORDER BY created_at") + abstract suspend fun findAll(): List + @Transaction @Query("SELECT * FROM favourites GROUP BY manga_id ORDER BY created_at LIMIT :limit OFFSET :offset") abstract suspend fun findAll(offset: Int, limit: Int): List + @Transaction + @Query("SELECT * FROM favourites WHERE category_id = :categoryId GROUP BY manga_id ORDER BY created_at") + abstract suspend fun findAll(categoryId: Long): List + @Transaction @Query("SELECT * FROM favourites WHERE category_id = :categoryId GROUP BY manga_id ORDER BY created_at LIMIT :limit OFFSET :offset") abstract suspend fun findAll(categoryId: Long, offset: Int, limit: Int): List 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 180a0e648..6c4a9843c 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 @@ -8,7 +8,7 @@ import org.koitharu.kotatsu.core.db.entity.* entities = [ MangaEntity::class, TagEntity::class, HistoryEntity::class, MangaTagsEntity::class, FavouriteCategoryEntity::class, FavouriteEntity::class, MangaPrefsEntity::class, TrackEntity::class - ], version = 4 + ], version = 5 ) abstract class MangaDatabase : RoomDatabase() { diff --git a/app/src/main/java/org/koitharu/kotatsu/core/db/entity/FavouriteCategoryEntity.kt b/app/src/main/java/org/koitharu/kotatsu/core/db/entity/FavouriteCategoryEntity.kt index 4fa94c461..d495a718f 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/db/entity/FavouriteCategoryEntity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/db/entity/FavouriteCategoryEntity.kt @@ -11,12 +11,14 @@ data class FavouriteCategoryEntity( @PrimaryKey(autoGenerate = true) @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 ) { fun toFavouriteCategory(id: Long? = null) = FavouriteCategory( id = id ?: categoryId.toLong(), title = title, + sortKey = sortKey, createdAt = Date(createdAt) ) } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/core/db/migrations/Migration4To5.kt b/app/src/main/java/org/koitharu/kotatsu/core/db/migrations/Migration4To5.kt new file mode 100644 index 000000000..81f7b0a94 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/core/db/migrations/Migration4To5.kt @@ -0,0 +1,11 @@ +package org.koitharu.kotatsu.core.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +object Migration4To5 : Migration(4, 5) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE favourite_categories ADD COLUMN sort_key INTEGER NOT NULL DEFAULT 0") + } +} \ 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 0ab093179..3b69d6c51 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 @@ -8,5 +8,6 @@ import java.util.* data class FavouriteCategory( val id: Long, val title: String, + val sortKey: Int, val createdAt: Date ) : Parcelable \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/domain/favourites/FavouritesRepository.kt b/app/src/main/java/org/koitharu/kotatsu/domain/favourites/FavouritesRepository.kt index 56be7dc1a..78f95b5d2 100644 --- a/app/src/main/java/org/koitharu/kotatsu/domain/favourites/FavouritesRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/domain/favourites/FavouritesRepository.kt @@ -18,18 +18,28 @@ class FavouritesRepository : KoinComponent { private val db: MangaDatabase by inject() + suspend fun getAllManga(): List { + val entities = db.favouritesDao.findAll() + return entities.map { it.manga.toManga(it.tags.map(TagEntity::toMangaTag).toSet()) } + } + suspend fun getAllManga(offset: Int): List { val entities = db.favouritesDao.findAll(offset, 20) return entities.map { it.manga.toManga(it.tags.map(TagEntity::toMangaTag).toSet()) } } + suspend fun getManga(categoryId: Long): List { + val entities = db.favouritesDao.findAll(categoryId) + return entities.map { it.manga.toManga(it.tags.map(TagEntity::toMangaTag).toSet()) } + } + suspend fun getManga(categoryId: Long, offset: Int): List { val entities = db.favouritesDao.findAll(categoryId, offset, 20) return entities.map { it.manga.toManga(it.tags.map(TagEntity::toMangaTag).toSet()) } } suspend fun getAllCategories(): List { - val entities = db.favouriteCategoriesDao.findAll("created_at") + val entities = db.favouriteCategoriesDao.findAll() return entities.map { it.toFavouriteCategory() } } @@ -42,17 +52,32 @@ class FavouritesRepository : KoinComponent { val entity = FavouriteCategoryEntity( title = title, createdAt = System.currentTimeMillis(), + sortKey = db.favouriteCategoriesDao.getNextSortKey(), categoryId = 0 ) val id = db.favouriteCategoriesDao.insert(entity) + notifyCategoriesChanged() return entity.toFavouriteCategory(id) } + suspend fun renameCategory(id: Long, title: String) { db.favouriteCategoriesDao.update(id, title) + notifyCategoriesChanged() } suspend fun removeCategory(id: Long) { db.favouriteCategoriesDao.delete(id) + notifyCategoriesChanged() + } + + suspend fun reorderCategories(orderedIds: List) { + val dao = db.favouriteCategoriesDao + db.withTransaction { + for ((i, id) in orderedIds.withIndex()) { + dao.update(id, i) + } + } + notifyCategoriesChanged() } suspend fun addToCategory(manga: Manga, categoryId: Long) { @@ -88,5 +113,11 @@ class FavouritesRepository : KoinComponent { listeners.forEach { x -> x.onFavouritesChanged(mangaId) } } } + + private suspend fun notifyCategoriesChanged() { + withContext(Dispatchers.Main) { + listeners.forEach { x -> x.onCategoriesChanged() } + } + } } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/domain/favourites/OnFavouritesChangeListener.kt b/app/src/main/java/org/koitharu/kotatsu/domain/favourites/OnFavouritesChangeListener.kt index 6f15a662a..5021e96d7 100644 --- a/app/src/main/java/org/koitharu/kotatsu/domain/favourites/OnFavouritesChangeListener.kt +++ b/app/src/main/java/org/koitharu/kotatsu/domain/favourites/OnFavouritesChangeListener.kt @@ -3,4 +3,6 @@ package org.koitharu.kotatsu.domain.favourites interface OnFavouritesChangeListener { fun onFavouritesChanged(mangaId: Long) + + fun onCategoriesChanged() } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/details/MangaDetailsPresenter.kt b/app/src/main/java/org/koitharu/kotatsu/ui/details/MangaDetailsPresenter.kt index 46eba2c09..6b276e273 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/details/MangaDetailsPresenter.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/details/MangaDetailsPresenter.kt @@ -157,6 +157,8 @@ class MangaDetailsPresenter private constructor() : BasePresenter setPrimaryFragment(HistoryListFragment.newInstance()) - R.id.nav_favourites -> setPrimaryFragment(FavouritesListFragment.newInstance()) + R.id.nav_favourites -> setPrimaryFragment(FavouritesContainerFragment.newInstance()) R.id.nav_local_storage -> setPrimaryFragment(LocalListFragment.newInstance()) R.id.nav_action_settings -> { startActivity(SettingsActivity.newIntent(this)) diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/main/list/favourites/FavouritesContainerFragment.kt b/app/src/main/java/org/koitharu/kotatsu/ui/main/list/favourites/FavouritesContainerFragment.kt new file mode 100644 index 000000000..39849637c --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/ui/main/list/favourites/FavouritesContainerFragment.kt @@ -0,0 +1,88 @@ +package org.koitharu.kotatsu.ui.main.list.favourites + +import android.os.Bundle +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import android.view.View +import com.google.android.material.snackbar.Snackbar +import com.google.android.material.tabs.TabLayoutMediator +import kotlinx.android.synthetic.main.fragment_favourites.* +import moxy.ktx.moxyPresenter +import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.model.FavouriteCategory +import org.koitharu.kotatsu.domain.favourites.FavouritesRepository +import org.koitharu.kotatsu.domain.favourites.OnFavouritesChangeListener +import org.koitharu.kotatsu.ui.common.BaseFragment +import org.koitharu.kotatsu.ui.main.list.favourites.categories.CategoriesActivity +import org.koitharu.kotatsu.ui.main.list.favourites.categories.FavouriteCategoriesPresenter +import org.koitharu.kotatsu.ui.main.list.favourites.categories.FavouriteCategoriesView +import java.util.* +import kotlin.collections.ArrayList + +class FavouritesContainerFragment : BaseFragment(R.layout.fragment_favourites), FavouriteCategoriesView, + OnFavouritesChangeListener { + + private val presenter by moxyPresenter(factory = ::FavouriteCategoriesPresenter) + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setHasOptionsMenu(true) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + val adapter = FavouritesPagerAdapter(this) + pager.adapter = adapter + TabLayoutMediator(tabs, pager, adapter).attach() + FavouritesRepository.subscribe(this) + } + + override fun onDestroyView() { + FavouritesRepository.unsubscribe(this) + super.onDestroyView() + } + + override fun onCategoriesChanged(categories: List) { + val data = ArrayList(categories.size + 1) + data += FavouriteCategory(0L, getString(R.string.all_favourites), -1, Date()) + data += categories + (pager.adapter as? FavouritesPagerAdapter)?.replaceData(data) + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + inflater.inflate(R.menu.opt_favourites, menu) + super.onCreateOptionsMenu(menu, inflater) + } + + override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) { + R.id.action_categories -> { + context?.let { + startActivity(CategoriesActivity.newIntent(it)) + } + true + } + else -> super.onOptionsItemSelected(item) + } + + override fun getTitle(): CharSequence? { + return getString(R.string.favourites) + } + + override fun onCheckedCategoriesChanged(checkedIds: Set) = Unit + + override fun onError(e: Throwable) { + Snackbar.make(pager, e.message ?: return, Snackbar.LENGTH_LONG).show() + } + + override fun onFavouritesChanged(mangaId: Long) = Unit + + override fun onCategoriesChanged() { + presenter.loadAllCategories() + } + + companion object { + + fun newInstance() = FavouritesContainerFragment() + } +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/main/list/favourites/FavouritesListFragment.kt b/app/src/main/java/org/koitharu/kotatsu/ui/main/list/favourites/FavouritesListFragment.kt index b61836f3b..00fa6f679 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/main/list/favourites/FavouritesListFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/main/list/favourites/FavouritesListFragment.kt @@ -1,49 +1,38 @@ package org.koitharu.kotatsu.ui.main.list.favourites -import android.view.Menu -import android.view.MenuInflater -import android.view.MenuItem import kotlinx.android.synthetic.main.fragment_list.* import moxy.ktx.moxyPresenter import org.koitharu.kotatsu.R import org.koitharu.kotatsu.ui.main.list.MangaListFragment import org.koitharu.kotatsu.ui.main.list.MangaListView -import org.koitharu.kotatsu.ui.main.list.favourites.categories.CategoriesActivity +import org.koitharu.kotatsu.utils.ext.withArgs class FavouritesListFragment : MangaListFragment(), MangaListView { private val presenter by moxyPresenter(factory = ::FavouritesListPresenter) + private val categoryId: Long + get() = arguments?.getLong(ARG_CATEGORY_ID) ?: 0L + override fun onRequestMoreItems(offset: Int) { - presenter.loadList(offset) - } - - override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { - inflater.inflate(R.menu.opt_favourites, menu) - super.onCreateOptionsMenu(menu, inflater) - } - - override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) { - R.id.action_categories -> { - context?.let { - startActivity(CategoriesActivity.newIntent(it)) - } - true - } - else -> super.onOptionsItemSelected(item) - } - - override fun getTitle(): CharSequence? { - return getString(R.string.favourites) + presenter.loadList(categoryId, offset) } override fun setUpEmptyListHolder() { - textView_holder.setText(R.string.you_have_not_favourites_yet) + textView_holder.setText(if (categoryId == 0L) { + R.string.you_have_not_favourites_yet + } else { + R.string.favourites_category_empty + }) textView_holder.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, 0, 0) } companion object { - fun newInstance() = FavouritesListFragment() + private const val ARG_CATEGORY_ID = "category_id" + + fun newInstance(categoryId: Long) = FavouritesListFragment().withArgs(1) { + putLong(ARG_CATEGORY_ID, categoryId) + } } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/main/list/favourites/FavouritesListPresenter.kt b/app/src/main/java/org/koitharu/kotatsu/ui/main/list/favourites/FavouritesListPresenter.kt index c5f4a191c..75e2b45bd 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/main/list/favourites/FavouritesListPresenter.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/main/list/favourites/FavouritesListPresenter.kt @@ -21,12 +21,12 @@ class FavouritesListPresenter : BasePresenter>() { super.onFirstViewAttach() } - fun loadList(offset: Int) { + fun loadList(categoryId: Long, offset: Int) { presenterScope.launch { viewState.onLoadingStateChanged(true) try { val list = withContext(Dispatchers.IO) { - repository.getAllManga(offset = offset) + repository.getManga(categoryId = categoryId, offset = offset) } if (offset == 0) { viewState.onListChanged(list) diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/main/list/favourites/FavouritesPagerAdapter.kt b/app/src/main/java/org/koitharu/kotatsu/ui/main/list/favourites/FavouritesPagerAdapter.kt new file mode 100644 index 000000000..724dd2b85 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/ui/main/list/favourites/FavouritesPagerAdapter.kt @@ -0,0 +1,32 @@ +package org.koitharu.kotatsu.ui.main.list.favourites + +import androidx.fragment.app.Fragment +import androidx.viewpager2.adapter.FragmentStateAdapter +import com.google.android.material.tabs.TabLayout +import com.google.android.material.tabs.TabLayoutMediator +import org.koitharu.kotatsu.core.model.FavouriteCategory +import org.koitharu.kotatsu.ui.common.list.AdapterUpdater +import org.koitharu.kotatsu.utils.ext.replaceWith + +class FavouritesPagerAdapter(fragment: Fragment) : FragmentStateAdapter(fragment), TabLayoutMediator.TabConfigurationStrategy { + + private val dataSet = ArrayList() + + override fun getItemCount() = dataSet.size + + override fun createFragment(position: Int): Fragment { + val item = dataSet[position] + return FavouritesListFragment.newInstance(item.id) + } + + override fun onConfigureTab(tab: TabLayout.Tab, position: Int) { + val item = dataSet[position] + tab.text = item.title + } + + fun replaceData(data: List) { + val updater = AdapterUpdater(dataSet, data, FavouriteCategory::id) + dataSet.replaceWith(data) + updater(this) + } +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/main/list/favourites/categories/CategoriesActivity.kt b/app/src/main/java/org/koitharu/kotatsu/ui/main/list/favourites/categories/CategoriesActivity.kt index f3e77211d..61f2aee3e 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/main/list/favourites/categories/CategoriesActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/main/list/favourites/categories/CategoriesActivity.kt @@ -10,6 +10,7 @@ import android.view.View import androidx.appcompat.app.AlertDialog import androidx.core.view.isVisible import androidx.recyclerview.widget.DividerItemDecoration +import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.RecyclerView import com.google.android.material.snackbar.Snackbar import kotlinx.android.synthetic.main.activity_categories.* @@ -28,6 +29,7 @@ class CategoriesActivity : BaseActivity(), OnRecyclerItemClickListener) { adapter.replaceData(categories) textView_holder.isVisible = categories.isEmpty() @@ -106,6 +115,25 @@ class CategoriesActivity : BaseActivity(), OnRecyclerItemClickListener? = null) : - BaseRecyclerAdapter(onItemClickListener) { +class CategoriesAdapter(private val onItemClickListener: OnRecyclerItemClickListener) : + BaseRecyclerAdapter() { override fun onCreateViewHolder(parent: ViewGroup) = CategoryHolder(parent) override fun onGetItemId(item: FavouriteCategory) = item.id override fun getExtra(item: FavouriteCategory, position: Int) = Unit + + @SuppressLint("ClickableViewAccessibility") + override fun onViewHolderCreated(holder: BaseViewHolder) { + holder.imageView_more.setOnClickListener { v -> + onItemClickListener.onItemClick(holder.requireData(), holder.bindingAdapterPosition, v) + } + holder.imageView_handle.setOnTouchListener { v, event -> + if (event.actionMasked == MotionEvent.ACTION_DOWN) { + onItemClickListener.onItemLongClick(holder.requireData(), holder.bindingAdapterPosition, v) + } else { + false + } + } + } + + fun moveItem(oldPos: Int, newPos: Int) { + val item = dataSet.removeAt(oldPos) + dataSet.add(newPos, item) + notifyItemMoved(oldPos, newPos) + } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/main/list/favourites/categories/CategoryHolder.kt b/app/src/main/java/org/koitharu/kotatsu/ui/main/list/favourites/categories/CategoryHolder.kt index 3b2d16fe4..90a8b830a 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/main/list/favourites/categories/CategoryHolder.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/main/list/favourites/categories/CategoryHolder.kt @@ -10,6 +10,6 @@ class CategoryHolder(parent: ViewGroup) : BaseViewHolder(parent, R.layout.item_category) { override fun onBind(data: FavouriteCategory, extra: Unit) { - textView.text = data.title + textView_title.text = data.title } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/main/list/favourites/categories/FavouriteCategoriesPresenter.kt b/app/src/main/java/org/koitharu/kotatsu/ui/main/list/favourites/categories/FavouriteCategoriesPresenter.kt index fd54af9fd..296a6e9fc 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/main/list/favourites/categories/FavouriteCategoriesPresenter.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/main/list/favourites/categories/FavouriteCategoriesPresenter.kt @@ -2,6 +2,8 @@ package org.koitharu.kotatsu.ui.main.list.favourites.categories import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext import moxy.InjectViewState import moxy.presenterScope @@ -14,6 +16,9 @@ import org.koitharu.kotatsu.ui.common.BasePresenter class FavouriteCategoriesPresenter : BasePresenter() { private lateinit var repository: FavouritesRepository + private val reorderMutex by lazy { + Mutex() + } override fun onFirstViewAttach() { repository = FavouritesRepository() @@ -28,7 +33,7 @@ class FavouriteCategoriesPresenter : BasePresenter() { repository.getAllCategories() } viewState.onCategoriesChanged(categories) - } catch (e: Exception) { + } catch (e: Throwable) { if (BuildConfig.DEBUG) { e.printStackTrace() } @@ -44,7 +49,7 @@ class FavouriteCategoriesPresenter : BasePresenter() { repository.getCategories(manga.id) } viewState.onCheckedCategoriesChanged(categories.map { it.id.toInt() }.toSet()) - } catch (e: Exception) { + } catch (e: Throwable) { if (BuildConfig.DEBUG) { e.printStackTrace() } @@ -61,7 +66,7 @@ class FavouriteCategoriesPresenter : BasePresenter() { repository.getAllCategories() } viewState.onCategoriesChanged(categories) - } catch (e: Exception) { + } catch (e: Throwable) { if (BuildConfig.DEBUG) { e.printStackTrace() } @@ -78,7 +83,7 @@ class FavouriteCategoriesPresenter : BasePresenter() { repository.getAllCategories() } viewState.onCategoriesChanged(categories) - } catch (e: Exception) { + } catch (e: Throwable) { if (BuildConfig.DEBUG) { e.printStackTrace() } @@ -95,7 +100,22 @@ class FavouriteCategoriesPresenter : BasePresenter() { repository.getAllCategories() } viewState.onCategoriesChanged(categories) - } catch (e: Exception) { + } catch (e: Throwable) { + if (BuildConfig.DEBUG) { + e.printStackTrace() + } + viewState.onError(e) + } + } + } + + fun storeCategoriesOrder(orderedIds: List) { + presenterScope.launch { + try { + reorderMutex.withLock { + repository.reorderCategories(orderedIds) + } + } catch (e: Throwable) { if (BuildConfig.DEBUG) { e.printStackTrace() } @@ -110,7 +130,7 @@ class FavouriteCategoriesPresenter : BasePresenter() { withContext(Dispatchers.IO) { repository.addToCategory(manga,categoryId) } - } catch (e: Exception) { + } catch (e: Throwable) { if (BuildConfig.DEBUG) { e.printStackTrace() } @@ -125,7 +145,7 @@ class FavouriteCategoriesPresenter : BasePresenter() { withContext(Dispatchers.IO) { repository.removeFromCategory(manga, categoryId) } - } catch (e: Exception) { + } catch (e: Throwable) { if (BuildConfig.DEBUG) { e.printStackTrace() } diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/settings/sources/SourcesSettingsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/ui/settings/sources/SourcesSettingsFragment.kt index bf8e04e87..264c821b1 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/settings/sources/SourcesSettingsFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/settings/sources/SourcesSettingsFragment.kt @@ -42,6 +42,6 @@ class SourcesSettingsFragment : BaseFragment(R.layout.fragment_settings_sources) override fun onItemLongClick(item: MangaSource, position: Int, view: View): Boolean { reorderHelper.startDrag(recyclerView.findViewHolderForAdapterPosition(position) ?: return false) - return super.onItemLongClick(item, position, view) + return true } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/widget/WidgetUpdater.kt b/app/src/main/java/org/koitharu/kotatsu/ui/widget/WidgetUpdater.kt index ac762dc3c..68bc02675 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/widget/WidgetUpdater.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/widget/WidgetUpdater.kt @@ -20,6 +20,8 @@ class WidgetUpdater(private val context: Context) : OnFavouritesChangeListener, updateWidget(RecentWidgetProvider::class.java) } + override fun onCategoriesChanged() = Unit + private fun updateWidget(cls: Class<*>) { val intent = Intent(context, cls) intent.action = AppWidgetManager.ACTION_APPWIDGET_UPDATE diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/widget/shelf/ShelfConfigActivity.kt b/app/src/main/java/org/koitharu/kotatsu/ui/widget/shelf/ShelfConfigActivity.kt index 2aa0964f4..f633021b8 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/widget/shelf/ShelfConfigActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/widget/shelf/ShelfConfigActivity.kt @@ -80,7 +80,7 @@ class ShelfConfigActivity : BaseActivity(), FavouriteCategoriesView, override fun onCategoriesChanged(categories: List) { val data = ArrayList(categories.size + 1) - data += FavouriteCategory(0L, getString(R.string.favourites), Date()) + data += FavouriteCategory(0L, getString(R.string.all_favourites), -1, Date()) data += categories adapter.replaceData(data) } diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/widget/shelf/ShelfListFactory.kt b/app/src/main/java/org/koitharu/kotatsu/ui/widget/shelf/ShelfListFactory.kt index 429d13816..594b3c6fb 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/widget/shelf/ShelfListFactory.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/widget/shelf/ShelfListFactory.kt @@ -30,7 +30,13 @@ class ShelfListFactory(private val context: Context, widgetId: Int) : RemoteView override fun onDataSetChanged() { dataSet.clear() val data = runBlocking { - FavouritesRepository().getManga(config.categoryId, 0) + val category = config.categoryId + val repo = FavouritesRepository() + if (category == 0L) { + repo.getAllManga() + } else { + repo.getManga(category) + } } dataSet.addAll(data) } diff --git a/app/src/main/res/drawable/ic_more.xml b/app/src/main/res/drawable/ic_more.xml new file mode 100644 index 000000000..bea301f96 --- /dev/null +++ b/app/src/main/res/drawable/ic_more.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/layout/activity_details.xml b/app/src/main/res/layout/activity_details.xml index 2680d6b34..ec1423b24 100644 --- a/app/src/main/res/layout/activity_details.xml +++ b/app/src/main/res/layout/activity_details.xml @@ -23,6 +23,7 @@ android:id="@+id/tabs" android:layout_width="match_parent" android:layout_height="wrap_content" + app:tabGravity="center" app:tabMode="scrollable" /> diff --git a/app/src/main/res/layout/fragment_favourites.xml b/app/src/main/res/layout/fragment_favourites.xml new file mode 100644 index 000000000..528cebe8a --- /dev/null +++ b/app/src/main/res/layout/fragment_favourites.xml @@ -0,0 +1,21 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_category.xml b/app/src/main/res/layout/item_category.xml index c0739d7cc..024889453 100644 --- a/app/src/main/res/layout/item_category.xml +++ b/app/src/main/res/layout/item_category.xml @@ -1,14 +1,40 @@ - \ No newline at end of file + android:layout_height="?android:listPreferredItemHeightSmall" + android:background="?android:windowBackground" + android:gravity="center_vertical" + android:orientation="horizontal"> + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 9a4a99844..5e8e03390 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -132,4 +132,6 @@ Другое хранилище Защищённое соединение (HTTPS) Готово + Всё избранное + В этой категории ничего нет \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 13e7eff61..af289b312 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -133,4 +133,6 @@ Other storage Use secure connection (HTTPS) Done + All favourites + This category is empty \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 200f2e0d5..5ac04b8e9 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sun May 10 11:33:24 EEST 2020 +#Sat May 16 07:20:00 EEST 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.4.1-all.zip