Merge branch 'devel' into feature/shikimori
This commit is contained in:
@@ -24,7 +24,7 @@ import org.koitharu.kotatsu.base.ui.BaseFragment
|
|||||||
import org.koitharu.kotatsu.base.ui.widgets.ChipsView
|
import org.koitharu.kotatsu.base.ui.widgets.ChipsView
|
||||||
import org.koitharu.kotatsu.core.model.MangaHistory
|
import org.koitharu.kotatsu.core.model.MangaHistory
|
||||||
import org.koitharu.kotatsu.databinding.FragmentDetailsBinding
|
import org.koitharu.kotatsu.databinding.FragmentDetailsBinding
|
||||||
import org.koitharu.kotatsu.favourites.ui.categories.select.FavouriteCategoriesDialog
|
import org.koitharu.kotatsu.favourites.ui.categories.select.FavouriteCategoriesBottomSheet
|
||||||
import org.koitharu.kotatsu.image.ui.ImageActivity
|
import org.koitharu.kotatsu.image.ui.ImageActivity
|
||||||
import org.koitharu.kotatsu.parsers.model.Manga
|
import org.koitharu.kotatsu.parsers.model.Manga
|
||||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||||
@@ -181,7 +181,7 @@ class DetailsFragment :
|
|||||||
val manga = viewModel.manga.value ?: return
|
val manga = viewModel.manga.value ?: return
|
||||||
when (v.id) {
|
when (v.id) {
|
||||||
R.id.button_favorite -> {
|
R.id.button_favorite -> {
|
||||||
FavouriteCategoriesDialog.show(childFragmentManager, manga)
|
FavouriteCategoriesBottomSheet.show(childFragmentManager, manga)
|
||||||
}
|
}
|
||||||
R.id.button_read -> {
|
R.id.button_read -> {
|
||||||
val chapterId = viewModel.readingHistory.value?.chapterId
|
val chapterId = viewModel.readingHistory.value?.chapterId
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ abstract class FavouriteCategoriesDao {
|
|||||||
abstract fun observeAll(): Flow<List<FavouriteCategoryEntity>>
|
abstract fun observeAll(): Flow<List<FavouriteCategoryEntity>>
|
||||||
|
|
||||||
@Query("SELECT * FROM favourite_categories WHERE category_id = :id")
|
@Query("SELECT * FROM favourite_categories WHERE category_id = :id")
|
||||||
abstract fun observe(id: Long): Flow<FavouriteCategoryEntity>
|
abstract fun observe(id: Long): Flow<FavouriteCategoryEntity?>
|
||||||
|
|
||||||
@Insert(onConflict = OnConflictStrategy.ABORT)
|
@Insert(onConflict = OnConflictStrategy.ABORT)
|
||||||
abstract suspend fun insert(category: FavouriteCategoryEntity): Long
|
abstract suspend fun insert(category: FavouriteCategoryEntity): Long
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
package org.koitharu.kotatsu.favourites.domain
|
package org.koitharu.kotatsu.favourites.domain
|
||||||
|
|
||||||
import androidx.room.withTransaction
|
import androidx.room.withTransaction
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.*
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
|
||||||
import kotlinx.coroutines.flow.flatMapLatest
|
|
||||||
import kotlinx.coroutines.flow.map
|
|
||||||
import org.koitharu.kotatsu.core.db.MangaDatabase
|
import org.koitharu.kotatsu.core.db.MangaDatabase
|
||||||
import org.koitharu.kotatsu.core.db.entity.*
|
import org.koitharu.kotatsu.core.db.entity.*
|
||||||
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||||
@@ -52,9 +49,9 @@ class FavouritesRepository(
|
|||||||
}.distinctUntilChanged()
|
}.distinctUntilChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun observeCategory(id: Long): Flow<FavouriteCategory> {
|
fun observeCategory(id: Long): Flow<FavouriteCategory?> {
|
||||||
return db.favouriteCategoriesDao.observe(id)
|
return db.favouriteCategoriesDao.observe(id)
|
||||||
.map { it.toFavouriteCategory() }
|
.map { it?.toFavouriteCategory() }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun observeCategories(mangaId: Long): Flow<List<FavouriteCategory>> {
|
fun observeCategories(mangaId: Long): Flow<List<FavouriteCategory>> {
|
||||||
@@ -162,6 +159,7 @@ class FavouritesRepository(
|
|||||||
|
|
||||||
private fun observeOrder(categoryId: Long): Flow<SortOrder> {
|
private fun observeOrder(categoryId: Long): Flow<SortOrder> {
|
||||||
return db.favouriteCategoriesDao.observe(categoryId)
|
return db.favouriteCategoriesDao.observe(categoryId)
|
||||||
|
.filterNotNull()
|
||||||
.map { x -> SortOrder(x.order, SortOrder.NEWEST) }
|
.map { x -> SortOrder(x.order, SortOrder.NEWEST) }
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import androidx.appcompat.view.ActionMode
|
|||||||
import androidx.appcompat.widget.PopupMenu
|
import androidx.appcompat.widget.PopupMenu
|
||||||
import androidx.core.graphics.Insets
|
import androidx.core.graphics.Insets
|
||||||
import androidx.core.view.children
|
import androidx.core.view.children
|
||||||
|
import androidx.core.view.isVisible
|
||||||
import androidx.core.view.updateLayoutParams
|
import androidx.core.view.updateLayoutParams
|
||||||
import androidx.core.view.updatePadding
|
import androidx.core.view.updatePadding
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
@@ -17,6 +18,7 @@ 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.databinding.FragmentFavouritesBinding
|
import org.koitharu.kotatsu.databinding.FragmentFavouritesBinding
|
||||||
|
import org.koitharu.kotatsu.databinding.ItemEmptyStateBinding
|
||||||
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
|
||||||
@@ -31,13 +33,15 @@ class FavouritesContainerFragment :
|
|||||||
BaseFragment<FragmentFavouritesBinding>(),
|
BaseFragment<FragmentFavouritesBinding>(),
|
||||||
FavouritesTabLongClickListener,
|
FavouritesTabLongClickListener,
|
||||||
CategoriesEditDelegate.CategoriesEditCallback,
|
CategoriesEditDelegate.CategoriesEditCallback,
|
||||||
ActionModeListener {
|
ActionModeListener,
|
||||||
|
View.OnClickListener {
|
||||||
|
|
||||||
private val viewModel by viewModel<FavouritesCategoriesViewModel>()
|
private val viewModel by viewModel<FavouritesCategoriesViewModel>()
|
||||||
private val editDelegate by lazy(LazyThreadSafetyMode.NONE) {
|
private val editDelegate by lazy(LazyThreadSafetyMode.NONE) {
|
||||||
CategoriesEditDelegate(requireContext(), this)
|
CategoriesEditDelegate(requireContext(), this)
|
||||||
}
|
}
|
||||||
private var pagerAdapter: FavouritesPagerAdapter? = null
|
private var pagerAdapter: FavouritesPagerAdapter? = null
|
||||||
|
private var stubBinding: ItemEmptyStateBinding? = null
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@@ -52,9 +56,7 @@ class FavouritesContainerFragment :
|
|||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
val adapter = FavouritesPagerAdapter(this, this)
|
val adapter = FavouritesPagerAdapter(this, this)
|
||||||
viewModel.visibleCategories.value?.let {
|
viewModel.visibleCategories.value?.let(::onCategoriesChanged)
|
||||||
adapter.replaceData(it)
|
|
||||||
}
|
|
||||||
binding.pager.adapter = adapter
|
binding.pager.adapter = adapter
|
||||||
pagerAdapter = adapter
|
pagerAdapter = adapter
|
||||||
TabLayoutMediator(binding.tabs, binding.pager, adapter).attach()
|
TabLayoutMediator(binding.tabs, binding.pager, adapter).attach()
|
||||||
@@ -66,6 +68,7 @@ class FavouritesContainerFragment :
|
|||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
pagerAdapter = null
|
pagerAdapter = null
|
||||||
|
stubBinding = null
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,6 +104,15 @@ class FavouritesContainerFragment :
|
|||||||
|
|
||||||
private fun onCategoriesChanged(categories: List<CategoryListModel>) {
|
private fun onCategoriesChanged(categories: List<CategoryListModel>) {
|
||||||
pagerAdapter?.replaceData(categories)
|
pagerAdapter?.replaceData(categories)
|
||||||
|
if (categories.isEmpty()) {
|
||||||
|
binding.pager.isVisible = false
|
||||||
|
binding.tabs.isVisible = false
|
||||||
|
showStub()
|
||||||
|
} else {
|
||||||
|
binding.pager.isVisible = true
|
||||||
|
binding.tabs.isVisible = true
|
||||||
|
(stubBinding?.root ?: binding.stubEmptyState).isVisible = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||||
@@ -130,6 +142,12 @@ class FavouritesContainerFragment :
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onClick(v: View) {
|
||||||
|
when (v.id) {
|
||||||
|
R.id.button_retry -> startActivity(FavouritesCategoryEditActivity.newIntent(v.context))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onDeleteCategory(category: FavouriteCategory) {
|
override fun onDeleteCategory(category: FavouriteCategory) {
|
||||||
viewModel.deleteCategory(category.id)
|
viewModel.deleteCategory(category.id)
|
||||||
}
|
}
|
||||||
@@ -147,7 +165,7 @@ class FavouritesContainerFragment :
|
|||||||
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_edit -> FavouritesCategoryEditActivity.newIntent(tabView.context, category.id)
|
R.id.action_edit -> startActivity(FavouritesCategoryEditActivity.newIntent(tabView.context, category.id))
|
||||||
else -> return@setOnMenuItemClickListener false
|
else -> return@setOnMenuItemClickListener false
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
@@ -160,7 +178,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 -> FavouritesCategoryEditActivity.newIntent(requireContext())
|
R.id.action_create -> startActivity(FavouritesCategoryEditActivity.newIntent(requireContext()))
|
||||||
R.id.action_hide -> viewModel.setAllCategoriesVisible(false)
|
R.id.action_hide -> viewModel.setAllCategoriesVisible(false)
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
@@ -168,6 +186,18 @@ class FavouritesContainerFragment :
|
|||||||
menu.show()
|
menu.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun showStub() {
|
||||||
|
val stub = stubBinding ?: ItemEmptyStateBinding.bind(binding.stubEmptyState.inflate())
|
||||||
|
stub.root.isVisible = true
|
||||||
|
stub.icon.setImageResource(R.drawable.ic_heart_outline)
|
||||||
|
stub.textPrimary.setText(R.string.text_empty_holder_primary)
|
||||||
|
stub.textSecondary.setText(R.string.empty_favourite_categories)
|
||||||
|
stub.buttonRetry.setText(R.string.add)
|
||||||
|
stub.buttonRetry.isVisible = true
|
||||||
|
stub.buttonRetry.setOnClickListener(this)
|
||||||
|
stubBinding = stub
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
fun newInstance() = FavouritesContainerFragment()
|
fun newInstance() = FavouritesContainerFragment()
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ class FavouritesCategoriesViewModel(
|
|||||||
repository.observeCategories(),
|
repository.observeCategories(),
|
||||||
observeAllCategoriesVisible(),
|
observeAllCategoriesVisible(),
|
||||||
) { list, showAll ->
|
) { list, showAll ->
|
||||||
mapCategories(list, showAll, showAll)
|
mapCategories(list, showAll, showAll && list.isNotEmpty())
|
||||||
}.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default)
|
}.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default)
|
||||||
|
|
||||||
fun deleteCategory(id: Long) {
|
fun deleteCategory(id: Long) {
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import org.koitharu.kotatsu.parsers.model.Manga
|
|||||||
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
||||||
import org.koitharu.kotatsu.utils.ext.withArgs
|
import org.koitharu.kotatsu.utils.ext.withArgs
|
||||||
|
|
||||||
class FavouriteCategoriesDialog :
|
class FavouriteCategoriesBottomSheet :
|
||||||
BaseBottomSheet<DialogFavoriteCategoriesBinding>(),
|
BaseBottomSheet<DialogFavoriteCategoriesBinding>(),
|
||||||
OnListItemClickListener<MangaCategoryItem>,
|
OnListItemClickListener<MangaCategoryItem>,
|
||||||
CategoriesEditDelegate.CategoriesEditCallback,
|
CategoriesEditDelegate.CategoriesEditCallback,
|
||||||
@@ -59,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 -> {
|
||||||
FavouritesCategoryEditActivity.newIntent(requireContext())
|
startActivity(FavouritesCategoryEditActivity.newIntent(requireContext()))
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
else -> false
|
else -> false
|
||||||
@@ -87,7 +87,7 @@ class FavouriteCategoriesDialog :
|
|||||||
|
|
||||||
fun show(fm: FragmentManager, manga: Manga) = Companion.show(fm, listOf(manga))
|
fun show(fm: FragmentManager, manga: Manga) = Companion.show(fm, listOf(manga))
|
||||||
|
|
||||||
fun show(fm: FragmentManager, manga: Collection<Manga>) = FavouriteCategoriesDialog().withArgs(1) {
|
fun show(fm: FragmentManager, manga: Collection<Manga>) = FavouriteCategoriesBottomSheet().withArgs(1) {
|
||||||
putParcelableArrayList(
|
putParcelableArrayList(
|
||||||
KEY_MANGA_LIST,
|
KEY_MANGA_LIST,
|
||||||
manga.mapTo(ArrayList(manga.size)) { ParcelableManga(it, withChapters = false) }
|
manga.mapTo(ArrayList(manga.size)) { ParcelableManga(it, withChapters = false) }
|
||||||
@@ -28,11 +28,11 @@ class FavouritesListViewModel(
|
|||||||
settings: AppSettings,
|
settings: AppSettings,
|
||||||
) : MangaListViewModel(settings), CountersProvider {
|
) : MangaListViewModel(settings), CountersProvider {
|
||||||
|
|
||||||
var sortOrder: LiveData<SortOrder> = if (categoryId == NO_ID) {
|
var sortOrder: LiveData<SortOrder?> = if (categoryId == NO_ID) {
|
||||||
MutableLiveData(null)
|
MutableLiveData(null)
|
||||||
} else {
|
} else {
|
||||||
repository.observeCategory(categoryId)
|
repository.observeCategory(categoryId)
|
||||||
.map { it.order }
|
.map { it?.order }
|
||||||
.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default)
|
.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import org.koitharu.kotatsu.core.prefs.ListMode
|
|||||||
import org.koitharu.kotatsu.databinding.FragmentListBinding
|
import org.koitharu.kotatsu.databinding.FragmentListBinding
|
||||||
import org.koitharu.kotatsu.details.ui.DetailsActivity
|
import org.koitharu.kotatsu.details.ui.DetailsActivity
|
||||||
import org.koitharu.kotatsu.download.ui.service.DownloadService
|
import org.koitharu.kotatsu.download.ui.service.DownloadService
|
||||||
import org.koitharu.kotatsu.favourites.ui.categories.select.FavouriteCategoriesDialog
|
import org.koitharu.kotatsu.favourites.ui.categories.select.FavouriteCategoriesBottomSheet
|
||||||
import org.koitharu.kotatsu.list.ui.adapter.MangaListAdapter
|
import org.koitharu.kotatsu.list.ui.adapter.MangaListAdapter
|
||||||
import org.koitharu.kotatsu.list.ui.adapter.MangaListAdapter.Companion.ITEM_TYPE_MANGA_GRID
|
import org.koitharu.kotatsu.list.ui.adapter.MangaListAdapter.Companion.ITEM_TYPE_MANGA_GRID
|
||||||
import org.koitharu.kotatsu.list.ui.adapter.MangaListListener
|
import org.koitharu.kotatsu.list.ui.adapter.MangaListListener
|
||||||
@@ -297,7 +297,7 @@ abstract class MangaListFragment :
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.action_favourite -> {
|
R.id.action_favourite -> {
|
||||||
FavouriteCategoriesDialog.show(childFragmentManager, selectedItems)
|
FavouriteCategoriesBottomSheet.show(childFragmentManager, selectedItems)
|
||||||
mode.finish()
|
mode.finish()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,6 +65,9 @@ class FeedViewModel(
|
|||||||
if (loadingJob?.isActive == true) {
|
if (loadingJob?.isActive == true) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (append && !hasNextPage.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
loadingJob = launchLoadingJob(Dispatchers.Default) {
|
loadingJob = launchLoadingJob(Dispatchers.Default) {
|
||||||
val offset = if (append) logList.value?.size ?: 0 else 0
|
val offset = if (append) logList.value?.size ?: 0 else 0
|
||||||
val list = repository.getTrackingLog(offset, 20)
|
val list = repository.getTrackingLog(offset, 20)
|
||||||
|
|||||||
@@ -34,7 +34,9 @@
|
|||||||
android:id="@+id/edit_name"
|
android:id="@+id/edit_name"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:hint="@string/name" />
|
android:hint="@string/name"
|
||||||
|
android:imeOptions="actionDone"
|
||||||
|
android:inputType="textCapSentences" />
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:overScrollMode="never"
|
android:overScrollMode="never"
|
||||||
|
android:paddingBottom="@dimen/list_spacing"
|
||||||
android:scrollbars="vertical"
|
android:scrollbars="vertical"
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
tools:listitem="@layout/item_checkable_new" />
|
tools:listitem="@layout/item_checkable_new" />
|
||||||
|
|||||||
@@ -17,4 +17,10 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent" />
|
android:layout_height="match_parent" />
|
||||||
|
|
||||||
|
<ViewStub
|
||||||
|
android:id="@+id/stub_empty_state"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout="@layout/item_empty_state" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -285,4 +285,5 @@
|
|||||||
<string name="edit">Изменить</string>
|
<string name="edit">Изменить</string>
|
||||||
<string name="edit_category">Изменить категорию</string>
|
<string name="edit_category">Изменить категорию</string>
|
||||||
<string name="tracking">Отслеживание</string>
|
<string name="tracking">Отслеживание</string>
|
||||||
|
<string name="empty_favourite_categories">Нет категорий избранного</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -290,4 +290,5 @@
|
|||||||
<string name="edit">Edit</string>
|
<string name="edit">Edit</string>
|
||||||
<string name="edit_category">Edit category</string>
|
<string name="edit_category">Edit category</string>
|
||||||
<string name="tracking">Tracking</string>
|
<string name="tracking">Tracking</string>
|
||||||
|
<string name="empty_favourite_categories">No favourite categories</string>
|
||||||
</resources>
|
</resources>
|
||||||
Reference in New Issue
Block a user