Edit favourite category activity

pull/172/head
Koitharu 4 years ago
parent 345a1379ae
commit 4771882f50
No known key found for this signature in database
GPG Key ID: 8E861F8CE6E7CE27

@ -53,7 +53,8 @@
<activity <activity
android:name="org.koitharu.kotatsu.search.ui.SearchActivity" android:name="org.koitharu.kotatsu.search.ui.SearchActivity"
android:label="@string/search" /> android:label="@string/search" />
<activity android:name="org.koitharu.kotatsu.search.ui.MangaListActivity" <activity
android:name="org.koitharu.kotatsu.search.ui.MangaListActivity"
android:label="@string/search_manga" /> android:label="@string/search_manga" />
<activity <activity
android:name="org.koitharu.kotatsu.settings.SettingsActivity" android:name="org.koitharu.kotatsu.settings.SettingsActivity"
@ -78,7 +79,8 @@
<activity <activity
android:name="org.koitharu.kotatsu.widget.shelf.ShelfConfigActivity" android:name="org.koitharu.kotatsu.widget.shelf.ShelfConfigActivity"
android:exported="true" android:exported="true"
android:label="@string/manga_shelf"> android:label="@string/manga_shelf"
android:theme="@style/Theme.Kotatsu.DialogWhenLarge">
<intent-filter> <intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" /> <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
</intent-filter> </intent-filter>
@ -95,9 +97,13 @@
android:windowSoftInputMode="adjustResize" /> android:windowSoftInputMode="adjustResize" />
<activity <activity
android:name="org.koitharu.kotatsu.download.ui.DownloadsActivity" android:name="org.koitharu.kotatsu.download.ui.DownloadsActivity"
android:label="@string/downloads"
android:launchMode="singleTop" android:launchMode="singleTop"
android:label="@string/downloads" /> android:theme="@style/Theme.Kotatsu.DialogWhenLarge" />
<activity android:name="org.koitharu.kotatsu.image.ui.ImageActivity" /> <activity android:name="org.koitharu.kotatsu.image.ui.ImageActivity" />
<activity
android:name="org.koitharu.kotatsu.favourites.ui.categories.edit.FavouritesCategoryEditActivity"
android:theme="@style/Theme.Kotatsu.DialogWhenLarge" />
<service <service
android:name="org.koitharu.kotatsu.download.ui.service.DownloadService" android:name="org.koitharu.kotatsu.download.ui.service.DownloadService"

@ -4,6 +4,7 @@ import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module import org.koin.dsl.module
import org.koitharu.kotatsu.favourites.domain.FavouritesRepository import org.koitharu.kotatsu.favourites.domain.FavouritesRepository
import org.koitharu.kotatsu.favourites.ui.categories.FavouritesCategoriesViewModel import org.koitharu.kotatsu.favourites.ui.categories.FavouritesCategoriesViewModel
import org.koitharu.kotatsu.favourites.ui.categories.edit.FavouritesCategoryEditViewModel
import org.koitharu.kotatsu.favourites.ui.categories.select.MangaCategoriesViewModel import org.koitharu.kotatsu.favourites.ui.categories.select.MangaCategoriesViewModel
import org.koitharu.kotatsu.favourites.ui.list.FavouritesListViewModel import org.koitharu.kotatsu.favourites.ui.list.FavouritesListViewModel
@ -19,4 +20,5 @@ val favouritesModule
viewModel { manga -> viewModel { manga ->
MangaCategoriesViewModel(manga.get(), get()) MangaCategoriesViewModel(manga.get(), get())
} }
viewModel { params -> FavouritesCategoryEditViewModel(params[0], get(), get()) }
} }

@ -6,6 +6,9 @@ import kotlinx.coroutines.flow.Flow
@Dao @Dao
abstract class FavouriteCategoriesDao { abstract class FavouriteCategoriesDao {
@Query("SELECT * FROM favourite_categories WHERE category_id = :id")
abstract suspend fun find(id: Int): FavouriteCategoryEntity
@Query("SELECT * FROM favourite_categories ORDER BY sort_key") @Query("SELECT * FROM favourite_categories ORDER BY sort_key")
abstract suspend fun findAll(): List<FavouriteCategoryEntity> abstract suspend fun findAll(): List<FavouriteCategoryEntity>
@ -27,6 +30,9 @@ abstract class FavouriteCategoriesDao {
@Query("UPDATE favourite_categories SET title = :title WHERE category_id = :id") @Query("UPDATE favourite_categories SET title = :title WHERE category_id = :id")
abstract suspend fun updateTitle(id: Long, title: String) abstract suspend fun updateTitle(id: Long, title: String)
@Query("UPDATE favourite_categories SET title = :title, `order` = :order, `track` = :tracker WHERE category_id = :id")
abstract suspend fun update(id: Long, title: String, order: String, tracker: Boolean)
@Query("UPDATE favourite_categories SET `order` = :order WHERE category_id = :id") @Query("UPDATE favourite_categories SET `order` = :order WHERE category_id = :id")
abstract suspend fun updateOrder(id: Long, order: String) abstract suspend fun updateOrder(id: Long, order: String)

@ -52,6 +52,11 @@ class FavouritesRepository(
}.distinctUntilChanged() }.distinctUntilChanged()
} }
fun observeCategory(id: Long): Flow<FavouriteCategory> {
return db.favouriteCategoriesDao.observe(id)
.map { it.toFavouriteCategory() }
}
fun observeCategories(mangaId: Long): Flow<List<FavouriteCategory>> { fun observeCategories(mangaId: Long): Flow<List<FavouriteCategory>> {
return db.favouritesDao.observe(mangaId).map { entity -> return db.favouritesDao.observe(mangaId).map { entity ->
entity?.categories?.map { it.toFavouriteCategory() }.orEmpty() entity?.categories?.map { it.toFavouriteCategory() }.orEmpty()
@ -62,6 +67,29 @@ class FavouritesRepository(
return db.favouritesDao.observeIds(mangaId).map { it.toSet() } return db.favouritesDao.observeIds(mangaId).map { it.toSet() }
} }
suspend fun getCategory(id: Long): FavouriteCategory {
return db.favouriteCategoriesDao.find(id.toInt()).toFavouriteCategory()
}
suspend fun createCategory(title: String, sortOrder: SortOrder, isTrackerEnabled: Boolean): FavouriteCategory {
val entity = FavouriteCategoryEntity(
title = title,
createdAt = System.currentTimeMillis(),
sortKey = db.favouriteCategoriesDao.getNextSortKey(),
categoryId = 0,
order = sortOrder.name,
track = isTrackerEnabled,
)
val id = db.favouriteCategoriesDao.insert(entity)
val category = entity.toFavouriteCategory(id)
channels.createChannel(category)
return category
}
suspend fun updateCategory(id: Long, title: String, sortOrder: SortOrder, isTrackerEnabled: Boolean) {
db.favouriteCategoriesDao.update(id, title, sortOrder.name, isTrackerEnabled)
}
suspend fun addCategory(title: String): FavouriteCategory { suspend fun addCategory(title: String): FavouriteCategory {
val entity = FavouriteCategoryEntity( val entity = FavouriteCategoryEntity(
title = title, title = title,

@ -16,12 +16,12 @@ import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseFragment import org.koitharu.kotatsu.base.ui.BaseFragment
import org.koitharu.kotatsu.base.ui.util.ActionModeListener import org.koitharu.kotatsu.base.ui.util.ActionModeListener
import org.koitharu.kotatsu.core.model.FavouriteCategory import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.core.ui.titleRes
import org.koitharu.kotatsu.databinding.FragmentFavouritesBinding import org.koitharu.kotatsu.databinding.FragmentFavouritesBinding
import org.koitharu.kotatsu.favourites.ui.categories.CategoriesActivity import org.koitharu.kotatsu.favourites.ui.categories.CategoriesActivity
import org.koitharu.kotatsu.favourites.ui.categories.CategoriesEditDelegate import org.koitharu.kotatsu.favourites.ui.categories.CategoriesEditDelegate
import org.koitharu.kotatsu.favourites.ui.categories.FavouritesCategoriesViewModel import org.koitharu.kotatsu.favourites.ui.categories.FavouritesCategoriesViewModel
import org.koitharu.kotatsu.favourites.ui.categories.adapter.CategoryListModel import org.koitharu.kotatsu.favourites.ui.categories.adapter.CategoryListModel
import org.koitharu.kotatsu.favourites.ui.categories.edit.FavouritesCategoryEditActivity
import org.koitharu.kotatsu.main.ui.AppBarOwner import org.koitharu.kotatsu.main.ui.AppBarOwner
import org.koitharu.kotatsu.utils.ext.getDisplayMessage import org.koitharu.kotatsu.utils.ext.getDisplayMessage
import org.koitharu.kotatsu.utils.ext.measureHeight import org.koitharu.kotatsu.utils.ext.measureHeight
@ -134,28 +134,6 @@ class FavouritesContainerFragment :
viewModel.deleteCategory(category.id) viewModel.deleteCategory(category.id)
} }
override fun onRenameCategory(category: FavouriteCategory, newName: String) {
viewModel.renameCategory(category.id, newName)
}
override fun onCreateCategory(name: String) {
viewModel.createCategory(name)
}
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)
menu.findItem(R.id.action_tracking)?.run {
isVisible = viewModel.isFavouritesTrackerEnabled
isChecked = category.isTrackingEnabled
}
}
private fun TabLayout.setTabsEnabled(enabled: Boolean) { private fun TabLayout.setTabsEnabled(enabled: Boolean) {
val tabStrip = getChildAt(0) as? ViewGroup ?: return val tabStrip = getChildAt(0) as? ViewGroup ?: return
for (tab in tabStrip.children) { for (tab in tabStrip.children) {
@ -166,19 +144,11 @@ class FavouritesContainerFragment :
private fun showCategoryMenu(tabView: View, category: FavouriteCategory) { private fun showCategoryMenu(tabView: View, category: FavouriteCategory) {
val menu = PopupMenu(tabView.context, tabView) val menu = PopupMenu(tabView.context, tabView)
menu.inflate(R.menu.popup_category) menu.inflate(R.menu.popup_category)
createOrderSubmenu(menu.menu, category)
menu.setOnMenuItemClickListener { menu.setOnMenuItemClickListener {
when (it.itemId) { when (it.itemId) {
R.id.action_remove -> editDelegate.deleteCategory(category) R.id.action_remove -> editDelegate.deleteCategory(category)
R.id.action_rename -> editDelegate.renameCategory(category) R.id.action_edit -> FavouritesCategoryEditActivity.newIntent(tabView.context, category.id)
R.id.action_create -> editDelegate.createCategory() else -> return@setOnMenuItemClickListener false
R.id.action_tracking -> viewModel.setCategoryTracking(category.id, !category.isTrackingEnabled)
R.id.action_order -> return@setOnMenuItemClickListener false
else -> {
val order = CategoriesActivity.SORT_ORDERS.getOrNull(it.order)
?: return@setOnMenuItemClickListener false
viewModel.setCategoryOrder(category.id, order)
}
} }
true true
} }
@ -190,7 +160,7 @@ class FavouritesContainerFragment :
menu.inflate(R.menu.popup_category_all) menu.inflate(R.menu.popup_category_all)
menu.setOnMenuItemClickListener { menu.setOnMenuItemClickListener {
when (it.itemId) { when (it.itemId) {
R.id.action_create -> editDelegate.createCategory() R.id.action_create -> FavouritesCategoryEditActivity.newIntent(requireContext())
R.id.action_hide -> viewModel.setAllCategoriesVisible(false) R.id.action_hide -> viewModel.setAllCategoriesVisible(false)
} }
true true

@ -3,7 +3,6 @@ package org.koitharu.kotatsu.favourites.ui.categories
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.Menu
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
@ -19,9 +18,9 @@ import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseActivity import org.koitharu.kotatsu.base.ui.BaseActivity
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.core.model.FavouriteCategory import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.core.ui.titleRes
import org.koitharu.kotatsu.databinding.ActivityCategoriesBinding import org.koitharu.kotatsu.databinding.ActivityCategoriesBinding
import org.koitharu.kotatsu.favourites.ui.categories.adapter.CategoryListModel import org.koitharu.kotatsu.favourites.ui.categories.adapter.CategoryListModel
import org.koitharu.kotatsu.favourites.ui.categories.edit.FavouritesCategoryEditActivity
import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.parsers.model.SortOrder
import org.koitharu.kotatsu.utils.ext.getDisplayMessage import org.koitharu.kotatsu.utils.ext.getDisplayMessage
import org.koitharu.kotatsu.utils.ext.measureHeight import org.koitharu.kotatsu.utils.ext.measureHeight
@ -57,24 +56,17 @@ class CategoriesActivity :
override fun onClick(v: View) { override fun onClick(v: View) {
when (v.id) { when (v.id) {
R.id.fab_add -> editDelegate.createCategory() R.id.fab_add -> startActivity(FavouritesCategoryEditActivity.newIntent(this))
} }
} }
override fun onItemClick(item: FavouriteCategory, view: View) { 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.inflate(R.menu.popup_category)
prepareCategoryMenu(menu.menu, item)
menu.setOnMenuItemClickListener { menuItem -> menu.setOnMenuItemClickListener { menuItem ->
when (menuItem.itemId) { when (menuItem.itemId) {
R.id.action_remove -> editDelegate.deleteCategory(item) R.id.action_remove -> editDelegate.deleteCategory(item)
R.id.action_rename -> editDelegate.renameCategory(item) R.id.action_edit -> startActivity(FavouritesCategoryEditActivity.newIntent(this, item.id))
R.id.action_tracking -> viewModel.setCategoryTracking(item.id, !item.isTrackingEnabled)
R.id.action_order -> return@setOnMenuItemClickListener false
else -> {
val order = SORT_ORDERS.getOrNull(menuItem.order) ?: return@setOnMenuItemClickListener false
viewModel.setCategoryOrder(item.id, order)
}
} }
true true
} }
@ -118,33 +110,6 @@ class CategoriesActivity :
viewModel.deleteCategory(category.id) viewModel.deleteCategory(category.id)
} }
override fun onRenameCategory(category: FavouriteCategory, newName: String) {
viewModel.renameCategory(category.id, newName)
}
override fun onCreateCategory(name: String) {
viewModel.createCategory(name)
}
private fun prepareCategoryMenu(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)
menu.findItem(R.id.action_tracking)?.run {
isVisible = viewModel.isFavouritesTrackerEnabled
isChecked = category.isTrackingEnabled
}
}
private inner class ReorderHelperCallback : ItemTouchHelper.SimpleCallback( private inner class ReorderHelperCallback : ItemTouchHelper.SimpleCallback(
ItemTouchHelper.DOWN or ItemTouchHelper.UP, 0 ItemTouchHelper.DOWN or ItemTouchHelper.UP, 0
) { ) {

@ -1,15 +1,10 @@
package org.koitharu.kotatsu.favourites.ui.categories package org.koitharu.kotatsu.favourites.ui.categories
import android.content.Context import android.content.Context
import android.text.InputType
import android.widget.Toast
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.dialog.TextInputDialog
import org.koitharu.kotatsu.core.model.FavouriteCategory import org.koitharu.kotatsu.core.model.FavouriteCategory
private const val MAX_TITLE_LENGTH = 24
class CategoriesEditDelegate( class CategoriesEditDelegate(
private val context: Context, private val context: Context,
private val callback: CategoriesEditCallback private val callback: CategoriesEditCallback
@ -26,49 +21,8 @@ class CategoriesEditDelegate(
.show() .show()
} }
fun renameCategory(category: FavouriteCategory) {
TextInputDialog.Builder(context)
.setTitle(R.string.rename)
.setText(category.title)
.setHint(R.string.enter_category_name)
.setInputType(InputType.TYPE_TEXT_VARIATION_PERSON_NAME or InputType.TYPE_TEXT_FLAG_CAP_SENTENCES)
.setNegativeButton(android.R.string.cancel)
.setMaxLength(MAX_TITLE_LENGTH, false)
.setPositiveButton(R.string.rename) { _, name ->
val trimmed = name.trim()
if (trimmed.isEmpty()) {
Toast.makeText(context, R.string.error_empty_name, Toast.LENGTH_SHORT).show()
} else {
callback.onRenameCategory(category, name)
}
}.create()
.show()
}
fun createCategory() {
TextInputDialog.Builder(context)
.setTitle(R.string.add_new_category)
.setHint(R.string.enter_category_name)
.setInputType(InputType.TYPE_TEXT_VARIATION_PERSON_NAME or InputType.TYPE_TEXT_FLAG_CAP_SENTENCES)
.setNegativeButton(android.R.string.cancel)
.setMaxLength(MAX_TITLE_LENGTH, false)
.setPositiveButton(R.string.add) { _, name ->
val trimmed = name.trim()
if (trimmed.isEmpty()) {
Toast.makeText(context, R.string.error_empty_name, Toast.LENGTH_SHORT).show()
} else {
callback.onCreateCategory(trimmed)
}
}.create()
.show()
}
interface CategoriesEditCallback { interface CategoriesEditCallback {
fun onDeleteCategory(category: FavouriteCategory) fun onDeleteCategory(category: FavouriteCategory)
fun onRenameCategory(category: FavouriteCategory, newName: String)
fun onCreateCategory(name: String)
} }
} }

@ -1,7 +1,6 @@
package org.koitharu.kotatsu.favourites.ui.categories package org.koitharu.kotatsu.favourites.ui.categories
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import java.util.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
@ -10,8 +9,8 @@ import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.favourites.domain.FavouritesRepository import org.koitharu.kotatsu.favourites.domain.FavouritesRepository
import org.koitharu.kotatsu.favourites.ui.categories.adapter.CategoryListModel import org.koitharu.kotatsu.favourites.ui.categories.adapter.CategoryListModel
import org.koitharu.kotatsu.parsers.model.SortOrder
import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct
import java.util.*
class FavouritesCategoriesViewModel( class FavouritesCategoriesViewModel(
private val repository: FavouritesRepository, private val repository: FavouritesRepository,
@ -34,39 +33,12 @@ class FavouritesCategoriesViewModel(
mapCategories(list, showAll, showAll) mapCategories(list, showAll, showAll)
}.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default) }.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default)
val isFavouritesTrackerEnabled: Boolean
get() = settings.isTrackerEnabled && AppSettings.TRACK_FAVOURITES in settings.trackSources
fun createCategory(name: String) {
launchJob {
repository.addCategory(name)
}
}
fun renameCategory(id: Long, name: String) {
launchJob {
repository.renameCategory(id, name)
}
}
fun deleteCategory(id: Long) { fun deleteCategory(id: Long) {
launchJob { launchJob {
repository.removeCategory(id) repository.removeCategory(id)
} }
} }
fun setCategoryOrder(id: Long, order: SortOrder) {
launchJob {
repository.setCategoryOrder(id, order)
}
}
fun setCategoryTracking(id: Long, isEnabled: Boolean) {
launchJob {
repository.setCategoryTracking(id, isEnabled)
}
}
fun setAllCategoriesVisible(isVisible: Boolean) { fun setAllCategoriesVisible(isVisible: Boolean) {
settings.isAllFavouritesVisible = isVisible settings.isAllFavouritesVisible = isVisible
} }

@ -0,0 +1,147 @@
package org.koitharu.kotatsu.favourites.ui.categories.edit
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.AdapterView
import android.widget.ArrayAdapter
import androidx.core.graphics.Insets
import androidx.core.view.isVisible
import androidx.core.view.updatePadding
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
import org.koitharu.kotatsu.R
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.parsers.model.SortOrder
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
class FavouritesCategoryEditActivity : BaseActivity<ActivityCategoryEditBinding>(), AdapterView.OnItemClickListener {
private val viewModel by viewModel<FavouritesCategoryEditViewModel> {
parametersOf(intent.getLongExtra(EXTRA_ID, NO_ID))
}
private var selectedSortOrder: SortOrder? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(ActivityCategoryEditBinding.inflate(layoutInflater))
supportActionBar?.run {
setDisplayHomeAsUpEnabled(true)
setHomeAsUpIndicator(com.google.android.material.R.drawable.abc_ic_clear_material)
}
initSortSpinner()
viewModel.onSaved.observe(this) { finishAfterTransition() }
viewModel.category.observe(this, ::onCategoryChanged)
viewModel.isLoading.observe(this, ::onLoadingStateChanged)
viewModel.onError.observe(this, ::onError)
viewModel.isTrackerEnabled.observe(this) {
binding.switchTracker.isVisible = it
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putSerializable(KEY_SORT_ORDER, selectedSortOrder)
}
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
val order = savedInstanceState.getSerializable(KEY_SORT_ORDER)
if (order != null && order is SortOrder) {
selectedSortOrder = order
}
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.opt_config, menu)
menu.findItem(R.id.action_done)?.setTitle(R.string.save)
return super.onCreateOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
R.id.action_done -> {
viewModel.save(
title = binding.editName.text?.toString().orEmpty(),
sortOrder = getSelectedSortOrder(),
isTrackerEnabled = binding.switchTracker.isChecked,
)
true
}
else -> super.onOptionsItemSelected(item)
}
override fun onWindowInsetsChanged(insets: Insets) {
binding.scrollView.updatePadding(
left = insets.left,
right = insets.right,
bottom = insets.bottom,
)
binding.toolbar.updatePadding(
top = insets.top,
)
}
override fun onItemClick(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
selectedSortOrder = CategoriesActivity.SORT_ORDERS.getOrNull(position)
}
private fun onCategoryChanged(category: FavouriteCategory?) {
setTitle(if (category == null) R.string.create_category else R.string.edit_category)
if (selectedSortOrder != null) {
return
}
binding.editName.setText(category?.title)
selectedSortOrder = category?.order
val sortText = getString((category?.order ?: SortOrder.NEWEST).titleRes)
binding.editSort.setText(sortText, false)
binding.switchTracker.isChecked = category?.isTrackingEnabled ?: true
}
private fun onError(e: Throwable) {
binding.textViewError.text = e.getDisplayMessage(resources)
binding.textViewError.isVisible = true
}
private fun onLoadingStateChanged(isLoading: Boolean) {
binding.editSort.isEnabled = !isLoading
binding.editName.isEnabled = !isLoading
binding.switchTracker.isEnabled = !isLoading
if (isLoading) {
binding.textViewError.isVisible = false
}
}
private fun initSortSpinner() {
val entries = CategoriesActivity.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
}
private fun getSelectedSortOrder(): SortOrder {
selectedSortOrder?.let { return it }
val entries = CategoriesActivity.SORT_ORDERS.map { getString(it.titleRes) }
val index = entries.indexOf(binding.editSort.text.toString())
return CategoriesActivity.SORT_ORDERS.getOrNull(index) ?: SortOrder.NEWEST
}
companion object {
private const val EXTRA_ID = "id"
private const val KEY_SORT_ORDER = "sort"
private const val NO_ID = -1L
fun newIntent(context: Context, id: Long = NO_ID): Intent {
return Intent(context, FavouritesCategoryEditActivity::class.java)
.putExtra(EXTRA_ID, id)
}
}
}

@ -0,0 +1,53 @@
package org.koitharu.kotatsu.favourites.ui.categories.edit
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.liveData
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
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.favourites.domain.FavouritesRepository
import org.koitharu.kotatsu.parsers.model.SortOrder
import org.koitharu.kotatsu.utils.SingleLiveEvent
private const val NO_ID = -1L
class FavouritesCategoryEditViewModel(
private val categoryId: Long,
private val repository: FavouritesRepository,
private val settings: AppSettings,
) : BaseViewModel() {
val onSaved = SingleLiveEvent<Unit>()
val category = MutableLiveData<FavouriteCategory?>()
val isTrackerEnabled = liveData(viewModelScope.coroutineContext + Dispatchers.Default) {
emit(settings.isTrackerEnabled && AppSettings.TRACK_FAVOURITES in settings.trackSources)
}
init {
launchLoadingJob {
category.value = if (categoryId != NO_ID) {
repository.getCategory(categoryId)
} else {
null
}
}
}
fun save(
title: String,
sortOrder: SortOrder,
isTrackerEnabled: Boolean,
) {
launchLoadingJob {
if (categoryId == NO_ID) {
repository.createCategory(title, sortOrder, isTrackerEnabled)
} else {
repository.updateCategory(categoryId, title, sortOrder, isTrackerEnabled)
}
onSaved.call(Unit)
}
}
}

@ -17,6 +17,7 @@ import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga
import org.koitharu.kotatsu.databinding.DialogFavoriteCategoriesBinding import org.koitharu.kotatsu.databinding.DialogFavoriteCategoriesBinding
import org.koitharu.kotatsu.favourites.ui.categories.CategoriesEditDelegate import org.koitharu.kotatsu.favourites.ui.categories.CategoriesEditDelegate
import org.koitharu.kotatsu.favourites.ui.categories.edit.FavouritesCategoryEditActivity
import org.koitharu.kotatsu.favourites.ui.categories.select.adapter.MangaCategoriesAdapter import org.koitharu.kotatsu.favourites.ui.categories.select.adapter.MangaCategoriesAdapter
import org.koitharu.kotatsu.favourites.ui.categories.select.model.MangaCategoryItem import org.koitharu.kotatsu.favourites.ui.categories.select.model.MangaCategoryItem
import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.Manga
@ -34,9 +35,6 @@ class FavouriteCategoriesDialog :
} }
private var adapter: MangaCategoriesAdapter? = null private var adapter: MangaCategoriesAdapter? = null
private val editDelegate by lazy(LazyThreadSafetyMode.NONE) {
CategoriesEditDelegate(requireContext(), this@FavouriteCategoriesDialog)
}
override fun onInflateView( override fun onInflateView(
inflater: LayoutInflater, inflater: LayoutInflater,
@ -61,7 +59,7 @@ class FavouriteCategoriesDialog :
override fun onMenuItemClick(item: MenuItem): Boolean { override fun onMenuItemClick(item: MenuItem): Boolean {
return when (item.itemId) { return when (item.itemId) {
R.id.action_create -> { R.id.action_create -> {
editDelegate.createCategory() FavouritesCategoryEditActivity.newIntent(requireContext())
true true
} }
else -> false else -> false
@ -74,12 +72,6 @@ class FavouriteCategoriesDialog :
override fun onDeleteCategory(category: FavouriteCategory) = Unit override fun onDeleteCategory(category: FavouriteCategory) = Unit
override fun onRenameCategory(category: FavouriteCategory, newName: String) = Unit
override fun onCreateCategory(name: String) {
viewModel.createCategory(name)
}
private fun onContentChanged(categories: List<MangaCategoryItem>) { private fun onContentChanged(categories: List<MangaCategoryItem>) {
adapter?.items = categories adapter?.items = categories
} }

@ -38,12 +38,6 @@ class MangaCategoriesViewModel(
} }
} }
fun createCategory(name: String) {
launchJob(Dispatchers.Default) {
favouritesRepository.addCategory(name)
}
}
private fun observeCategoriesIds() = if (manga.size == 1) { private fun observeCategoriesIds() = if (manga.size == 1) {
// Fast path // Fast path
favouritesRepository.observeCategoriesIds(manga[0].id) favouritesRepository.observeCategoriesIds(manga[0].id)

@ -1,11 +1,17 @@
package org.koitharu.kotatsu.favourites.ui.list package org.koitharu.kotatsu.favourites.ui.list
import android.os.Bundle
import android.view.Menu import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View
import androidx.appcompat.view.ActionMode import androidx.appcompat.view.ActionMode
import androidx.core.view.iterator
import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf import org.koin.core.parameter.parametersOf
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.ui.titleRes
import org.koitharu.kotatsu.favourites.ui.categories.CategoriesActivity
import org.koitharu.kotatsu.list.ui.MangaListFragment import org.koitharu.kotatsu.list.ui.MangaListFragment
import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.utils.ext.withArgs import org.koitharu.kotatsu.utils.ext.withArgs
@ -17,12 +23,54 @@ class FavouritesListFragment : MangaListFragment() {
} }
private val categoryId: Long private val categoryId: Long
get() = arguments?.getLong(ARG_CATEGORY_ID) ?: 0L get() = arguments?.getLong(ARG_CATEGORY_ID) ?: NO_ID
override val isSwipeRefreshEnabled = false override val isSwipeRefreshEnabled = false
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.sortOrder.observe(viewLifecycleOwner) { activity?.invalidateOptionsMenu() }
}
override fun onScrolledToEnd() = Unit override fun onScrolledToEnd() = Unit
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
if (categoryId != NO_ID) {
inflater.inflate(R.menu.opt_favourites_list, menu)
menu.findItem(R.id.action_order)?.subMenu?.let { submenu ->
for ((i, item) in CategoriesActivity.SORT_ORDERS.withIndex()) {
val menuItem = submenu.add(R.id.group_order, Menu.NONE, i, item.titleRes)
menuItem.isCheckable = true
}
submenu.setGroupCheckable(R.id.group_order, true, true)
}
}
}
override fun onPrepareOptionsMenu(menu: Menu) {
super.onPrepareOptionsMenu(menu)
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)
item.isChecked = order == selectedOrder
}
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when {
item.itemId == R.id.action_order -> false
item.groupId == R.id.group_order -> {
val order = CategoriesActivity.SORT_ORDERS.getOrNull(item.order) ?: return false
viewModel.setSortOrder(order)
true
}
else -> super.onOptionsItemSelected(item)
}
}
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean { override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
mode.menuInflater.inflate(R.menu.mode_favourites, menu) mode.menuInflater.inflate(R.menu.mode_favourites, menu)
return super.onCreateActionMode(mode, menu) return super.onCreateActionMode(mode, menu)
@ -48,6 +96,7 @@ class FavouritesListFragment : MangaListFragment() {
companion object { companion object {
const val NO_ID = 0L
private const val ARG_CATEGORY_ID = "category_id" private const val ARG_CATEGORY_ID = "category_id"
fun newInstance(categoryId: Long) = FavouritesListFragment().withArgs(1) { fun newInstance(categoryId: Long) = FavouritesListFragment().withArgs(1) {

@ -1,12 +1,16 @@
package org.koitharu.kotatsu.favourites.ui.list package org.koitharu.kotatsu.favourites.ui.list
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.favourites.domain.FavouritesRepository import org.koitharu.kotatsu.favourites.domain.FavouritesRepository
import org.koitharu.kotatsu.favourites.ui.list.FavouritesListFragment.Companion.NO_ID
import org.koitharu.kotatsu.list.domain.CountersProvider import org.koitharu.kotatsu.list.domain.CountersProvider
import org.koitharu.kotatsu.list.ui.MangaListViewModel import org.koitharu.kotatsu.list.ui.MangaListViewModel
import org.koitharu.kotatsu.list.ui.model.EmptyState import org.koitharu.kotatsu.list.ui.model.EmptyState
@ -24,8 +28,16 @@ class FavouritesListViewModel(
settings: AppSettings, settings: AppSettings,
) : MangaListViewModel(settings), CountersProvider { ) : MangaListViewModel(settings), CountersProvider {
var sortOrder: LiveData<SortOrder> = if (categoryId == NO_ID) {
MutableLiveData(null)
} else {
repository.observeCategory(categoryId)
.map { it.order }
.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default)
}
override val content = combine( override val content = combine(
if (categoryId == 0L) { if (categoryId == NO_ID) {
repository.observeAll(SortOrder.NEWEST) repository.observeAll(SortOrder.NEWEST)
} else { } else {
repository.observeAll(categoryId) repository.observeAll(categoryId)
@ -37,7 +49,7 @@ class FavouritesListViewModel(
EmptyState( EmptyState(
icon = R.drawable.ic_heart_outline, icon = R.drawable.ic_heart_outline,
textPrimary = R.string.text_empty_holder_primary, textPrimary = R.string.text_empty_holder_primary,
textSecondary = if (categoryId == 0L) { textSecondary = if (categoryId == NO_ID) {
R.string.you_have_not_favourites_yet R.string.you_have_not_favourites_yet
} else { } else {
R.string.favourites_category_empty R.string.favourites_category_empty
@ -60,7 +72,7 @@ class FavouritesListViewModel(
return return
} }
launchJob { launchJob {
if (categoryId == 0L) { if (categoryId == NO_ID) {
repository.removeFromFavourites(ids) repository.removeFromFavourites(ids)
} else { } else {
repository.removeFromCategory(categoryId, ids) repository.removeFromCategory(categoryId, ids)
@ -68,6 +80,15 @@ class FavouritesListViewModel(
} }
} }
fun setSortOrder(order: SortOrder) {
if (categoryId == NO_ID) {
return
}
launchJob {
repository.setCategoryOrder(categoryId, order)
}
}
override suspend fun getCounter(mangaId: Long): Int { override suspend fun getCounter(mangaId: Long): Int {
return trackingRepository.getNewChaptersCount(mangaId) return trackingRepository.getNewChaptersCount(mangaId)
} }

@ -62,6 +62,7 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@null" android:background="@null"
android:drawablePadding="16dp" android:drawablePadding="16dp"
android:layout_marginEnd="4dp"
android:gravity="center_vertical" android:gravity="center_vertical"
android:hint="@string/search_manga" android:hint="@string/search_manga"
android:imeOptions="actionSearch" android:imeOptions="actionSearch"

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
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="match_parent"
android:orientation="vertical">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<ScrollView
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false"
android:overScrollMode="ifContentScrolls">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/layout_name"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/edit_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/name" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/layout_sort"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
app:endIconMode="dropdown_menu">
<AutoCompleteTextView
android:id="@+id/edit_sort"
style="?editTextStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/sort_order"
android:inputType="none" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/switch_tracker"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/check_for_new_chapters"
android:visibility="gone"
tools:visibility="visible" />
<TextView
android:id="@+id/textView_error"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:textColor="?colorError"
android:visibility="gone"
tools:text="@tools:sample/lorem[4]"
tools:visibility="visible" />
</LinearLayout>
</ScrollView>
</LinearLayout>

@ -51,7 +51,7 @@
style="@style/Widget.Kotatsu.SearchView" style="@style/Widget.Kotatsu.SearchView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_marginEnd="2dp" android:layout_marginEnd="4dp"
android:background="@null" android:background="@null"
android:gravity="center_vertical" android:gravity="center_vertical"
android:hint="@string/search_manga" android:hint="@string/search_manga"

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/action_order"
android:orderInCategory="30"
android:title="@string/sort_order">
<menu>
<group
android:id="@+id/group_order"
android:checkableBehavior="single" />
</menu>
</item>
</menu>

@ -7,25 +7,7 @@
android:title="@string/remove" /> android:title="@string/remove" />
<item <item
android:id="@+id/action_rename" android:id="@+id/action_edit"
android:title="@string/rename" /> android:title="@string/edit" />
<item
android:id="@+id/action_order"
android:title="@string/sort_order">
<menu>
<group
android:id="@+id/group_order"
android:checkableBehavior="single" />
</menu>
</item>
<item
android:id="@+id/action_tracking"
android:title="@string/check_for_new_chapters"
android:checkable="true" />
</menu> </menu>

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.Kotatsu.DialogWhenLarge">
<item name="windowFixedWidthMajor">@dimen/abc_dialog_fixed_width_major</item>
<item name="windowFixedWidthMinor">@dimen/abc_dialog_fixed_width_minor</item>
<item name="windowFixedHeightMajor">@dimen/abc_dialog_fixed_height_major</item>
<item name="windowFixedHeightMinor">@dimen/abc_dialog_fixed_height_minor</item>
<item name="android:windowElevation">@dimen/abc_floating_window_z</item>
<item name="android:colorBackground">?attr/colorBackgroundFloating</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowFrame">@null</item>
<item name="android:windowTitleStyle">@style/RtlOverlay.DialogWindowTitle.AppCompat</item>
<item name="android:windowTitleBackgroundStyle">@style/Base.DialogWindowTitleBackground.AppCompat</item>
<item name="android:windowBackground">@drawable/abc_dialog_material_background</item>
<item name="android:windowIsFloating">true</item>
<item name="android:backgroundDimEnabled">true</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowAnimationStyle">@style/Animation.AppCompat.Dialog</item>
<item name="android:windowSoftInputMode">stateUnspecified|adjustResize</item>
<item name="windowActionBar">false</item>
<item name="windowActionModeOverlay">true</item>
<item name="listPreferredItemPaddingLeft">24dip</item>
<item name="listPreferredItemPaddingRight">24dip</item>
<item name="android:listDivider">@null</item>
<item name="android:buttonBarStyle">@style/Widget.AppCompat.ButtonBar.AlertDialog</item>
<item name="android:borderlessButtonStyle">@style/Widget.AppCompat.Button.Borderless</item>
<item name="android:windowCloseOnTouchOutside">true</item>
</style>
</resources>

@ -281,4 +281,7 @@
<string name="show_notification_new_chapters_on">Вы будете получать уведомления об обновлении манги, которую Вы читаете</string> <string name="show_notification_new_chapters_on">Вы будете получать уведомления об обновлении манги, которую Вы читаете</string>
<string name="show_notification_new_chapters_off">Вы не будете получать уведомления, но новые главы будут отображаться в списке</string> <string name="show_notification_new_chapters_off">Вы не будете получать уведомления, но новые главы будут отображаться в списке</string>
<string name="notifications_enable">Включить уведомления</string> <string name="notifications_enable">Включить уведомления</string>
<string name="name">Название</string>
<string name="edit">Изменить</string>
<string name="edit_category">Изменить категорию</string>
</resources> </resources>

@ -284,4 +284,7 @@
<string name="show_notification_new_chapters_on">You will receive notifications about updates of manga you are reading</string> <string name="show_notification_new_chapters_on">You will receive notifications about updates of manga you are reading</string>
<string name="show_notification_new_chapters_off">You will not receive notifications but new chapters will be highlighted in the lists</string> <string name="show_notification_new_chapters_off">You will not receive notifications but new chapters will be highlighted in the lists</string>
<string name="notifications_enable">Enable notifications</string> <string name="notifications_enable">Enable notifications</string>
<string name="name">Name</string>
<string name="edit">Edit</string>
<string name="edit_category">Edit category</string>
</resources> </resources>

@ -119,6 +119,8 @@
<style name="TextAppearance.Kotatsu.Preference.Secondary" parent="TextAppearance.Material3.BodySmall" /> <style name="TextAppearance.Kotatsu.Preference.Secondary" parent="TextAppearance.Material3.BodySmall" />
<style name="TextAppearance.Kotatsu.Menu" parent="TextAppearance.Material3.BodyLarge" />
<style name="TextAppearance.Kotatsu.SectionHeader" parent="TextAppearance.Material3.LabelLarge"> <style name="TextAppearance.Kotatsu.SectionHeader" parent="TextAppearance.Material3.LabelLarge">
<item name="android:textColor">?android:attr/textColorSecondary</item> <item name="android:textColor">?android:attr/textColorSecondary</item>
</style> </style>

@ -71,7 +71,8 @@
<item name="recyclerViewStyle">@style/Widget.Kotatsu.RecyclerView</item> <item name="recyclerViewStyle">@style/Widget.Kotatsu.RecyclerView</item>
<item name="listItemTextViewStyle">@style/Widget.Kotatsu.ListItemTextView</item> <item name="listItemTextViewStyle">@style/Widget.Kotatsu.ListItemTextView</item>
<!-- Preference text appearance --> <!-- Text appearance -->
<item name="actionMenuTextAppearance">@style/TextAppearance.Kotatsu.Menu</item>
<item name="android:textAppearanceListItem">?attr/textAppearanceBodyLarge</item> <item name="android:textAppearanceListItem">?attr/textAppearanceBodyLarge</item>
<item name="android:textAppearanceListItemSecondary">@style/TextAppearance.Kotatsu.Preference.Secondary</item> <item name="android:textAppearanceListItemSecondary">@style/TextAppearance.Kotatsu.Preference.Secondary</item>
@ -80,6 +81,8 @@
<!--== Default Theme ==--> <!--== Default Theme ==-->
<style name="Theme.Kotatsu" parent="Base.Theme.Kotatsu" /> <style name="Theme.Kotatsu" parent="Base.Theme.Kotatsu" />
<style name="Theme.Kotatsu.DialogWhenLarge" />
<!-- Monet theme only support S+ --> <!-- Monet theme only support S+ -->
<style name="Theme.Kotatsu.Monet" /> <style name="Theme.Kotatsu.Monet" />

Loading…
Cancel
Save