Show favourites by categories and manager categories order

This commit is contained in:
Koitharu
2020-05-16 08:49:34 +03:00
parent 01607ec1e2
commit 3588270742
31 changed files with 383 additions and 64 deletions

View File

@@ -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'

View File

@@ -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)
}

View File

@@ -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<FavouriteCategoryEntity>
@Query("SELECT * FROM favourite_categories ORDER BY sort_key")
abstract suspend fun findAll(): List<FavouriteCategoryEntity>
@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
}
}

View File

@@ -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<FavouriteManga>
@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<FavouriteManga>
@Transaction
@Query("SELECT * FROM favourites WHERE category_id = :categoryId GROUP BY manga_id ORDER BY created_at")
abstract suspend fun findAll(categoryId: Long): List<FavouriteManga>
@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<FavouriteManga>

View File

@@ -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() {

View File

@@ -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)
)
}

View File

@@ -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")
}
}

View File

@@ -8,5 +8,6 @@ import java.util.*
data class FavouriteCategory(
val id: Long,
val title: String,
val sortKey: Int,
val createdAt: Date
) : Parcelable

View File

@@ -18,18 +18,28 @@ class FavouritesRepository : KoinComponent {
private val db: MangaDatabase by inject()
suspend fun getAllManga(): List<Manga> {
val entities = db.favouritesDao.findAll()
return entities.map { it.manga.toManga(it.tags.map(TagEntity::toMangaTag).toSet()) }
}
suspend fun getAllManga(offset: Int): List<Manga> {
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<Manga> {
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<Manga> {
val entities = db.favouritesDao.findAll(categoryId, offset, 20)
return entities.map { it.manga.toManga(it.tags.map(TagEntity::toMangaTag).toSet()) }
}
suspend fun getAllCategories(): List<FavouriteCategory> {
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<Long>) {
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() }
}
}
}
}

View File

@@ -3,4 +3,6 @@ package org.koitharu.kotatsu.domain.favourites
interface OnFavouritesChangeListener {
fun onFavouritesChanged(mangaId: Long)
fun onCategoriesChanged()
}

View File

@@ -157,6 +157,8 @@ class MangaDetailsPresenter private constructor() : BasePresenter<MangaDetailsVi
}
}
override fun onCategoriesChanged() = Unit
override fun onDestroy() {
HistoryRepository.unsubscribe(this)
FavouritesRepository.unsubscribe(this)

View File

@@ -24,7 +24,7 @@ import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.domain.MangaProviderFactory
import org.koitharu.kotatsu.ui.common.BaseActivity
import org.koitharu.kotatsu.ui.main.list.favourites.FavouritesListFragment
import org.koitharu.kotatsu.ui.main.list.favourites.FavouritesContainerFragment
import org.koitharu.kotatsu.ui.main.list.history.HistoryListFragment
import org.koitharu.kotatsu.ui.main.list.local.LocalListFragment
import org.koitharu.kotatsu.ui.main.list.remote.RemoteListFragment
@@ -107,7 +107,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
setPrimaryFragment(RemoteListFragment.newInstance(source))
} else when (item.itemId) {
R.id.nav_history -> 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))

View File

@@ -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<FavouriteCategory>) {
val data = ArrayList<FavouriteCategory>(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<Int>) = 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()
}
}

View File

@@ -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<Unit>(), MangaListView<Unit> {
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)
}
}
}

View File

@@ -21,12 +21,12 @@ class FavouritesListPresenter : BasePresenter<MangaListView<Unit>>() {
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)

View File

@@ -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<FavouriteCategory>()
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<FavouriteCategory>) {
val updater = AdapterUpdater(dataSet, data, FavouriteCategory::id)
dataSet.replaceWith(data)
updater(this)
}
}

View File

@@ -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<Favourite
private val presenter by moxyPresenter(factory = ::FavouriteCategoriesPresenter)
private lateinit var adapter: CategoriesAdapter
private lateinit var reorderHelper: ItemTouchHelper
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -38,6 +40,8 @@ class CategoriesActivity : BaseActivity(), OnRecyclerItemClickListener<Favourite
recyclerView.addItemDecoration(DividerItemDecoration(this, RecyclerView.VERTICAL))
recyclerView.adapter = adapter
fab_add.setOnClickListener(this)
reorderHelper = ItemTouchHelper(ReorderHelperCallback())
reorderHelper.attachToRecyclerView(recyclerView)
}
override fun onClick(v: View) {
@@ -56,6 +60,11 @@ class CategoriesActivity : BaseActivity(), OnRecyclerItemClickListener<Favourite
}
}
override fun onItemLongClick(item: FavouriteCategory, position: Int, view: View): Boolean {
reorderHelper.startDrag(recyclerView.findViewHolderForAdapterPosition(position) ?: return false)
return true
}
override fun onCategoriesChanged(categories: List<FavouriteCategory>) {
adapter.replaceData(categories)
textView_holder.isVisible = categories.isEmpty()
@@ -106,6 +115,25 @@ class CategoriesActivity : BaseActivity(), OnRecyclerItemClickListener<Favourite
.show()
}
private inner class ReorderHelperCallback : ItemTouchHelper.SimpleCallback(
ItemTouchHelper.DOWN or ItemTouchHelper.UP, 0
) {
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
val oldPos = viewHolder.bindingAdapterPosition
val newPos = target.bindingAdapterPosition
adapter.moveItem(oldPos, newPos)
presenter.storeCategoriesOrder(adapter.items.map { it.id })
return true
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) = Unit
}
companion object {
fun newIntent(context: Context) = Intent(context, CategoriesActivity::class.java)

View File

@@ -1,17 +1,40 @@
package org.koitharu.kotatsu.ui.main.list.favourites.categories
import android.annotation.SuppressLint
import android.view.MotionEvent
import android.view.ViewGroup
import kotlinx.android.synthetic.main.item_category.*
import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.ui.common.list.BaseRecyclerAdapter
import org.koitharu.kotatsu.ui.common.list.BaseViewHolder
import org.koitharu.kotatsu.ui.common.list.OnRecyclerItemClickListener
import org.koitharu.kotatsu.ui.main.list.favourites.categories.select.CategoryCheckableHolder
class CategoriesAdapter(onItemClickListener: OnRecyclerItemClickListener<FavouriteCategory>? = null) :
BaseRecyclerAdapter<FavouriteCategory, Unit>(onItemClickListener) {
class CategoriesAdapter(private val onItemClickListener: OnRecyclerItemClickListener<FavouriteCategory>) :
BaseRecyclerAdapter<FavouriteCategory, Unit>() {
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<FavouriteCategory, Unit>) {
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)
}
}

View File

@@ -10,6 +10,6 @@ class CategoryHolder(parent: ViewGroup) :
BaseViewHolder<FavouriteCategory, Unit>(parent, R.layout.item_category) {
override fun onBind(data: FavouriteCategory, extra: Unit) {
textView.text = data.title
textView_title.text = data.title
}
}

View File

@@ -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<FavouriteCategoriesView>() {
private lateinit var repository: FavouritesRepository
private val reorderMutex by lazy {
Mutex()
}
override fun onFirstViewAttach() {
repository = FavouritesRepository()
@@ -28,7 +33,7 @@ class FavouriteCategoriesPresenter : BasePresenter<FavouriteCategoriesView>() {
repository.getAllCategories()
}
viewState.onCategoriesChanged(categories)
} catch (e: Exception) {
} catch (e: Throwable) {
if (BuildConfig.DEBUG) {
e.printStackTrace()
}
@@ -44,7 +49,7 @@ class FavouriteCategoriesPresenter : BasePresenter<FavouriteCategoriesView>() {
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<FavouriteCategoriesView>() {
repository.getAllCategories()
}
viewState.onCategoriesChanged(categories)
} catch (e: Exception) {
} catch (e: Throwable) {
if (BuildConfig.DEBUG) {
e.printStackTrace()
}
@@ -78,7 +83,7 @@ class FavouriteCategoriesPresenter : BasePresenter<FavouriteCategoriesView>() {
repository.getAllCategories()
}
viewState.onCategoriesChanged(categories)
} catch (e: Exception) {
} catch (e: Throwable) {
if (BuildConfig.DEBUG) {
e.printStackTrace()
}
@@ -95,7 +100,22 @@ class FavouriteCategoriesPresenter : BasePresenter<FavouriteCategoriesView>() {
repository.getAllCategories()
}
viewState.onCategoriesChanged(categories)
} catch (e: Exception) {
} catch (e: Throwable) {
if (BuildConfig.DEBUG) {
e.printStackTrace()
}
viewState.onError(e)
}
}
}
fun storeCategoriesOrder(orderedIds: List<Long>) {
presenterScope.launch {
try {
reorderMutex.withLock {
repository.reorderCategories(orderedIds)
}
} catch (e: Throwable) {
if (BuildConfig.DEBUG) {
e.printStackTrace()
}
@@ -110,7 +130,7 @@ class FavouriteCategoriesPresenter : BasePresenter<FavouriteCategoriesView>() {
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<FavouriteCategoriesView>() {
withContext(Dispatchers.IO) {
repository.removeFromCategory(manga, categoryId)
}
} catch (e: Exception) {
} catch (e: Throwable) {
if (BuildConfig.DEBUG) {
e.printStackTrace()
}

View File

@@ -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
}
}

View File

@@ -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

View File

@@ -80,7 +80,7 @@ class ShelfConfigActivity : BaseActivity(), FavouriteCategoriesView,
override fun onCategoriesChanged(categories: List<FavouriteCategory>) {
val data = ArrayList<FavouriteCategory>(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)
}

View File

@@ -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)
}

View File

@@ -0,0 +1,11 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z" />
</vector>

View File

@@ -23,6 +23,7 @@
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabGravity="center"
app:tabMode="scrollable" />
</com.google.android.material.appbar.AppBarLayout>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical">
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabGravity="center"
app:tabMode="scrollable"/>
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

View File

@@ -1,14 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="?android:listPreferredItemHeight"
android:background="?android:selectableItemBackground"
android:gravity="start|center_vertical"
android:paddingStart="?android:listPreferredItemPaddingStart"
android:paddingEnd="?android:listPreferredItemPaddingEnd"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:textColor="?android:textColorPrimary"
tools:text="@tools:sample/lorem[4]" />
android:layout_height="?android:listPreferredItemHeightSmall"
android:background="?android:windowBackground"
android:gravity="center_vertical"
android:orientation="horizontal">
<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" />
<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:singleLine="true"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="?android:attr/textColorPrimary"
tools:text="@tools:sample/lorem[1]" />
<ImageView
android:id="@+id/imageView_more"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?selectableItemBackgroundBorderless"
android:padding="?listPreferredItemPaddingEnd"
android:scaleType="center"
android:src="@drawable/ic_more" />
</LinearLayout>

View File

@@ -132,4 +132,6 @@
<string name="other_storage">Другое хранилище</string>
<string name="use_ssl">Защищённое соединение (HTTPS)</string>
<string name="done">Готово</string>
<string name="all_favourites">Всё избранное</string>
<string name="favourites_category_empty">В этой категории ничего нет</string>
</resources>

View File

@@ -133,4 +133,6 @@
<string name="other_storage">Other storage</string>
<string name="use_ssl">Use secure connection (HTTPS)</string>
<string name="done">Done</string>
<string name="all_favourites">All favourites</string>
<string name="favourites_category_empty">This category is empty</string>
</resources>

View File

@@ -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