mirror of
https://github.com/null2264/yokai.git
synced 2025-06-21 10:44:42 +00:00
add clear db by source (#1193)
* add clear db by source * do suggested changes * fix issues * remove fabcontroller * more fixes * change invalidateOptionsMenu
This commit is contained in:
parent
23d62539b5
commit
f3dc38b629
12 changed files with 433 additions and 27 deletions
|
@ -0,0 +1,3 @@
|
|||
package eu.kanade.tachiyomi.data.database.models
|
||||
|
||||
data class SourceIdMangaCount(val source: Long, val count: Int)
|
|
@ -1,11 +1,13 @@
|
|||
package eu.kanade.tachiyomi.data.database.queries
|
||||
|
||||
import com.pushtorefresh.storio.Queries
|
||||
import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
|
||||
import com.pushtorefresh.storio.sqlite.queries.Query
|
||||
import com.pushtorefresh.storio.sqlite.queries.RawQuery
|
||||
import eu.kanade.tachiyomi.data.database.DbProvider
|
||||
import eu.kanade.tachiyomi.data.database.models.LibraryManga
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.database.models.SourceIdMangaCount
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.LibraryMangaGetResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.MangaDateAddedPutResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.MangaFavoritePutResolver
|
||||
|
@ -14,6 +16,7 @@ import eu.kanade.tachiyomi.data.database.resolvers.MangaFlagsPutResolver
|
|||
import eu.kanade.tachiyomi.data.database.resolvers.MangaInfoPutResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.MangaLastUpdatedPutResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.MangaTitlePutResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.SourceIdMangaCountGetResolver
|
||||
import eu.kanade.tachiyomi.data.database.tables.CategoryTable
|
||||
import eu.kanade.tachiyomi.data.database.tables.ChapterTable
|
||||
import eu.kanade.tachiyomi.data.database.tables.MangaCategoryTable
|
||||
|
@ -75,6 +78,17 @@ interface MangaQueries : DbProvider {
|
|||
)
|
||||
.prepare()
|
||||
|
||||
fun getSourceIdsWithNonLibraryManga() = db.get()
|
||||
.listOfObjects(SourceIdMangaCount::class.java)
|
||||
.withQuery(
|
||||
RawQuery.builder()
|
||||
.query(getSourceIdsWithNonLibraryMangaQuery())
|
||||
.observesTables(MangaTable.TABLE)
|
||||
.build()
|
||||
)
|
||||
.withGetResolver(SourceIdMangaCountGetResolver.INSTANCE)
|
||||
.prepare()
|
||||
|
||||
fun insertManga(manga: Manga) = db.put().`object`(manga).prepare()
|
||||
|
||||
fun insertMangas(mangas: List<Manga>) = db.put().objects(mangas).prepare()
|
||||
|
@ -128,12 +142,12 @@ interface MangaQueries : DbProvider {
|
|||
|
||||
fun deleteMangas(mangas: List<Manga>) = db.delete().objects(mangas).prepare()
|
||||
|
||||
fun deleteMangasNotInLibrary() = db.delete()
|
||||
fun deleteMangasNotInLibraryBySourceIds(sourceIds: List<Long>) = db.delete()
|
||||
.byQuery(
|
||||
DeleteQuery.builder()
|
||||
.table(MangaTable.TABLE)
|
||||
.where("${MangaTable.COL_FAVORITE} = ?")
|
||||
.whereArgs(0)
|
||||
.where("${MangaTable.COL_FAVORITE} = ? AND ${MangaTable.COL_SOURCE} IN (${Queries.placeholders(sourceIds.size)})")
|
||||
.whereArgs(0, *sourceIds.toTypedArray())
|
||||
.build()
|
||||
)
|
||||
.prepare()
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package eu.kanade.tachiyomi.data.database.queries
|
||||
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.SourceIdMangaCountGetResolver
|
||||
import eu.kanade.tachiyomi.ui.recents.RecentsPresenter
|
||||
import eu.kanade.tachiyomi.data.database.tables.CategoryTable as Category
|
||||
import eu.kanade.tachiyomi.data.database.tables.ChapterTable as Chapter
|
||||
|
@ -307,3 +308,14 @@ fun getCategoriesForMangaQuery() =
|
|||
${MangaCategory.TABLE}.${MangaCategory.COL_CATEGORY_ID}
|
||||
WHERE ${MangaCategory.COL_MANGA_ID} = ?
|
||||
"""
|
||||
|
||||
/** Query to get the list of sources in the database that have
|
||||
* non-library manga, and how many
|
||||
*/
|
||||
fun getSourceIdsWithNonLibraryMangaQuery() =
|
||||
"""
|
||||
SELECT ${Manga.COL_SOURCE}, COUNT(*) as ${SourceIdMangaCountGetResolver.COL_COUNT}
|
||||
FROM ${Manga.TABLE}
|
||||
WHERE ${Manga.COL_FAVORITE} = 0
|
||||
GROUP BY ${Manga.COL_SOURCE}
|
||||
"""
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
package eu.kanade.tachiyomi.data.database.resolvers
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.database.Cursor
|
||||
import com.pushtorefresh.storio.sqlite.operations.get.DefaultGetResolver
|
||||
import eu.kanade.tachiyomi.data.database.models.SourceIdMangaCount
|
||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable
|
||||
|
||||
class SourceIdMangaCountGetResolver : DefaultGetResolver<SourceIdMangaCount>() {
|
||||
|
||||
companion object {
|
||||
val INSTANCE = SourceIdMangaCountGetResolver()
|
||||
const val COL_COUNT = "manga_count"
|
||||
}
|
||||
|
||||
@SuppressLint("Range")
|
||||
override fun mapFromCursor(cursor: Cursor): SourceIdMangaCount {
|
||||
val sourceID = cursor.getLong(cursor.getColumnIndex(MangaTable.COL_SOURCE))
|
||||
val count = cursor.getInt(cursor.getColumnIndex(COL_COUNT))
|
||||
|
||||
return SourceIdMangaCount(sourceID, count)
|
||||
}
|
||||
}
|
|
@ -31,6 +31,7 @@ import eu.kanade.tachiyomi.network.PREF_DOH_GOOGLE
|
|||
import eu.kanade.tachiyomi.network.PREF_DOH_QUAD9
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||
import eu.kanade.tachiyomi.ui.setting.database.ClearDatabaseController
|
||||
import eu.kanade.tachiyomi.util.CrashLogUtil
|
||||
import eu.kanade.tachiyomi.util.system.disableItems
|
||||
import eu.kanade.tachiyomi.util.system.isPackageInstalled
|
||||
|
@ -39,6 +40,7 @@ import eu.kanade.tachiyomi.util.system.launchUI
|
|||
import eu.kanade.tachiyomi.util.system.materialAlertDialog
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import eu.kanade.tachiyomi.util.view.openInBrowser
|
||||
import eu.kanade.tachiyomi.util.view.withFadeTransaction
|
||||
import kotlinx.coroutines.CoroutineStart
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
|
@ -214,12 +216,7 @@ class SettingsAdvancedController : SettingsController() {
|
|||
key = "clear_database"
|
||||
titleRes = R.string.clear_database
|
||||
summaryRes = R.string.clear_database_summary
|
||||
|
||||
onClick {
|
||||
val ctrl = ClearDatabaseDialogController()
|
||||
ctrl.targetController = this@SettingsAdvancedController
|
||||
ctrl.showDialog(router)
|
||||
}
|
||||
onClick { router.pushController(ClearDatabaseController().withFadeTransaction()) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -410,24 +407,6 @@ class SettingsAdvancedController : SettingsController() {
|
|||
)
|
||||
}
|
||||
|
||||
class ClearDatabaseDialogController : DialogController() {
|
||||
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
|
||||
return activity!!.materialAlertDialog()
|
||||
.setMessage(R.string.clear_database_confirmation)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
(targetController as? SettingsAdvancedController)?.clearDatabase()
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.create()
|
||||
}
|
||||
}
|
||||
|
||||
private fun clearDatabase() {
|
||||
db.deleteMangasNotInLibrary().executeAsBlocking()
|
||||
db.deleteHistoryNoLastRead().executeAsBlocking()
|
||||
activity?.toast(R.string.clear_database_completed)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val CLEAR_CACHE_KEY = "pref_clear_cache_key"
|
||||
|
||||
|
|
|
@ -0,0 +1,158 @@
|
|||
package eu.kanade.tachiyomi.ui.setting.database
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.view.*
|
||||
import androidx.core.view.*
|
||||
import androidx.core.view.WindowInsetsCompat.Type.systemBars
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.Payload
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.databinding.ClearDatabaseControllerBinding
|
||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import eu.kanade.tachiyomi.util.view.activityBinding
|
||||
import eu.kanade.tachiyomi.util.view.fullAppBarHeight
|
||||
import eu.kanade.tachiyomi.util.view.liftAppbarWith
|
||||
import eu.kanade.tachiyomi.util.view.scrollViewWith
|
||||
|
||||
class ClearDatabaseController :
|
||||
NucleusController<ClearDatabaseControllerBinding, ClearDatabasePresenter>(),
|
||||
FlexibleAdapter.OnItemClickListener,
|
||||
FlexibleAdapter.OnUpdateListener {
|
||||
|
||||
private var adapter: FlexibleAdapter<ClearDatabaseSourceItem>? = null
|
||||
private var menu: Menu? = null
|
||||
|
||||
override fun createBinding(inflater: LayoutInflater): ClearDatabaseControllerBinding {
|
||||
return ClearDatabaseControllerBinding.inflate(inflater)
|
||||
}
|
||||
|
||||
override fun createPresenter(): ClearDatabasePresenter {
|
||||
return ClearDatabasePresenter()
|
||||
}
|
||||
|
||||
override fun getTitle(): String? {
|
||||
return activity?.getString(R.string.clear_database)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View) {
|
||||
super.onViewCreated(view)
|
||||
liftAppbarWith(binding.recycler, true)
|
||||
|
||||
adapter = FlexibleAdapter<ClearDatabaseSourceItem>(null, this, true)
|
||||
binding.recycler.adapter = adapter
|
||||
binding.recycler.layoutManager = LinearLayoutManager(view.context)
|
||||
binding.recycler.setHasFixedSize(true)
|
||||
adapter?.fastScroller = binding.fastScroller
|
||||
val fabBaseMarginBottom = binding.fab.marginBottom
|
||||
|
||||
scrollViewWith(
|
||||
binding.recycler,
|
||||
true,
|
||||
afterInsets = { insets ->
|
||||
binding.fab.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
bottomMargin = insets.getInsets(systemBars()).bottom + fabBaseMarginBottom
|
||||
}
|
||||
binding.recycler.updatePadding(
|
||||
bottom = binding.fab.height + binding.fab.marginBottom
|
||||
)
|
||||
binding.emptyView.updatePadding(
|
||||
top = (fullAppBarHeight ?: 0) + (activityBinding?.appBar?.paddingTop ?: 0),
|
||||
bottom = insets.getInsets(systemBars()).bottom
|
||||
)
|
||||
}
|
||||
)
|
||||
binding.fab.setOnClickListener {
|
||||
if (adapter!!.selectedItemCount > 0) {
|
||||
val ctrl = ClearDatabaseSourcesDialog()
|
||||
ctrl.targetController = this
|
||||
ctrl.showDialog(router)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView(view: View) {
|
||||
adapter = null
|
||||
super.onDestroyView(view)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.generic_selection, menu)
|
||||
this.menu = menu
|
||||
menu.forEach { menuItem -> menuItem.isVisible = (adapter?.itemCount ?: 0) > 0 }
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
val adapter = adapter ?: return false
|
||||
when (item.itemId) {
|
||||
R.id.action_select_all -> adapter.selectAll()
|
||||
R.id.action_select_inverse -> {
|
||||
val currentSelection = adapter.selectedPositionsAsSet
|
||||
val invertedSelection = (0..adapter.itemCount)
|
||||
.filterNot { currentSelection.contains(it) }
|
||||
currentSelection.clear()
|
||||
currentSelection.addAll(invertedSelection)
|
||||
}
|
||||
}
|
||||
adapter.notifyItemRangeChanged(0, adapter.itemCount, Payload.SELECTION)
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
override fun onUpdateEmptyView(size: Int) {
|
||||
if (size > 0) {
|
||||
binding.emptyView.hide()
|
||||
} else {
|
||||
binding.emptyView.show(
|
||||
R.drawable.ic_book_24dp,
|
||||
R.string.database_clean,
|
||||
)
|
||||
}
|
||||
menu?.forEach { menuItem -> menuItem.isVisible = size > 0 }
|
||||
}
|
||||
|
||||
override fun onItemClick(view: View?, position: Int): Boolean {
|
||||
val adapter = adapter ?: return false
|
||||
adapter.toggleSelection(position)
|
||||
adapter.notifyItemChanged(position, Payload.SELECTION)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onPrepareOptionsMenu(menu: Menu) {
|
||||
super.onPrepareOptionsMenu(menu)
|
||||
val size = adapter?.itemCount ?: 0
|
||||
menu.forEach { menuItem -> menuItem.isVisible = size > 0 }
|
||||
}
|
||||
|
||||
fun setItems(items: List<ClearDatabaseSourceItem>) {
|
||||
adapter?.updateDataSet(items)
|
||||
}
|
||||
|
||||
class ClearDatabaseSourcesDialog : DialogController() {
|
||||
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
|
||||
return MaterialAlertDialogBuilder(activity!!)
|
||||
.setMessage(R.string.clear_database_confirmation)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
(targetController as? ClearDatabaseController)?.clearDatabaseForSelectedSources()
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.create()
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
private fun clearDatabaseForSelectedSources() {
|
||||
val adapter = adapter ?: return
|
||||
val selectedSourceIds = adapter.selectedPositions.mapNotNull { position ->
|
||||
adapter.getItem(position)?.source?.id
|
||||
}
|
||||
presenter.clearDatabaseForSourceIds(selectedSourceIds)
|
||||
adapter.clearSelection()
|
||||
adapter.notifyDataSetChanged()
|
||||
activity?.toast(R.string.clear_database_completed)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package eu.kanade.tachiyomi.ui.setting.database
|
||||
|
||||
import android.os.Bundle
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
||||
import rx.Observable
|
||||
import rx.android.schedulers.AndroidSchedulers
|
||||
import rx.schedulers.Schedulers
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
class ClearDatabasePresenter : BasePresenter<ClearDatabaseController>() {
|
||||
|
||||
private val db = Injekt.get<DatabaseHelper>()
|
||||
|
||||
private val sourceManager = Injekt.get<SourceManager>()
|
||||
|
||||
override fun onCreate(savedState: Bundle?) {
|
||||
super.onCreate(savedState)
|
||||
getDatabaseSourcesObservable()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribeLatestCache(ClearDatabaseController::setItems)
|
||||
}
|
||||
|
||||
fun clearDatabaseForSourceIds(sources: List<Long>) {
|
||||
db.deleteMangasNotInLibraryBySourceIds(sources).executeAsBlocking()
|
||||
db.deleteHistoryNoLastRead().executeAsBlocking()
|
||||
}
|
||||
|
||||
private fun getDatabaseSourcesObservable(): Observable<List<ClearDatabaseSourceItem>> {
|
||||
return db.getSourceIdsWithNonLibraryManga().asRxObservable().observeOn(AndroidSchedulers.mainThread())
|
||||
.map { sourceCounts ->
|
||||
sourceCounts.map {
|
||||
val sourceObj = sourceManager.getOrStub(it.source)
|
||||
ClearDatabaseSourceItem(sourceObj, it.count)
|
||||
}.sortedBy { it.source.name }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package eu.kanade.tachiyomi.ui.setting.database
|
||||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.davidea.viewholders.FlexibleViewHolder
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.databinding.ClearDatabaseSourceItemBinding
|
||||
import eu.kanade.tachiyomi.source.LocalSource
|
||||
import eu.kanade.tachiyomi.source.Source
|
||||
import eu.kanade.tachiyomi.source.icon
|
||||
|
||||
data class ClearDatabaseSourceItem(val source: Source, private val mangaCount: Int) : AbstractFlexibleItem<ClearDatabaseSourceItem.Holder>() {
|
||||
|
||||
override fun getLayoutRes(): Int {
|
||||
return R.layout.clear_database_source_item
|
||||
}
|
||||
|
||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): Holder {
|
||||
return Holder(view, adapter)
|
||||
}
|
||||
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>?, holder: Holder?, position: Int, payloads: MutableList<Any>?) {
|
||||
holder?.bind(source, mangaCount)
|
||||
}
|
||||
|
||||
class Holder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter) {
|
||||
|
||||
private val binding = ClearDatabaseSourceItemBinding.bind(view)
|
||||
|
||||
fun bind(source: Source, count: Int) {
|
||||
binding.title.text = source.toString()
|
||||
binding.description.text = itemView.context.getString(R.string.clear_database_source_item_count, count)
|
||||
|
||||
itemView.post {
|
||||
when {
|
||||
source.id == LocalSource.ID -> binding.thumbnail.setImageResource(R.mipmap.ic_local_source)
|
||||
else -> binding.thumbnail.setImageDrawable(source.icon())
|
||||
}
|
||||
}
|
||||
|
||||
binding.checkbox.isChecked = (bindingAdapter as FlexibleAdapter<*>).isSelected(bindingAdapterPosition)
|
||||
}
|
||||
}
|
||||
}
|
41
app/src/main/res/layout/clear_database_controller.xml
Normal file
41
app/src/main/res/layout/clear_database_controller.xml
Normal file
|
@ -0,0 +1,41 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:choiceMode="multipleChoice"
|
||||
android:clipToPadding="false"
|
||||
android:paddingBottom="88dp"
|
||||
tools:listitem="@layout/clear_database_source_item" />
|
||||
|
||||
<eu.kanade.tachiyomi.ui.base.MaterialFastScroll
|
||||
android:id="@+id/fast_scroller"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="end"
|
||||
app:fastScrollerBubbleEnabled="false"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<eu.kanade.tachiyomi.widget.EmptyView
|
||||
android:id="@+id/empty_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fab"
|
||||
style="@style/Theme.Widget.FAB"
|
||||
android:src="@drawable/ic_delete_24dp"
|
||||
app:layout_anchor="@id/recycler"
|
||||
android:contentDescription="@string/migrate"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
</FrameLayout>
|
68
app/src/main/res/layout/clear_database_source_item.xml
Normal file
68
app/src/main/res/layout/clear_database_source_item.xml
Normal file
|
@ -0,0 +1,68 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout 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="56dp"
|
||||
android:background="@drawable/list_item_selector"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="16dp">
|
||||
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/thumbnail"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:paddingHorizontal="8dp"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
tools:ignore="ContentDescription"
|
||||
tools:src="@mipmap/ic_launcher" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
style="?textAppearanceBodyMedium"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="0dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:ellipsize="middle"
|
||||
android:maxLines="1"
|
||||
app:layout_constraintStart_toEndOf="@+id/thumbnail"
|
||||
app:layout_constraintEnd_toStartOf="@id/checkbox"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/description"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
tools:text="Source Name (LN)" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/description"
|
||||
style="?textAppearanceBodyMedium"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
app:layout_constraintStart_toEndOf="@+id/thumbnail"
|
||||
app:layout_constraintEnd_toStartOf="@id/checkbox"
|
||||
app:layout_constraintTop_toBottomOf="@id/title"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
tools:text="999 non-library manga in database" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkbox"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:clickable="false"
|
||||
android:background="@android:color/transparent"
|
||||
android:longClickable="false"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintDimensionRatio="1:2"
|
||||
/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
19
app/src/main/res/menu/generic_selection.xml
Normal file
19
app/src/main/res/menu/generic_selection.xml
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/action_select_all"
|
||||
android:icon="@drawable/ic_select_all_24dp"
|
||||
android:title="@string/select_all"
|
||||
app:iconTint="?colorOnSurface"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_select_inverse"
|
||||
android:icon="@drawable/ic_select_none_24dp"
|
||||
android:title="@string/select_none"
|
||||
app:iconTint="?colorOnSurface"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
</menu>
|
|
@ -791,9 +791,11 @@
|
|||
<string name="clear_cookies">Clear cookies</string>
|
||||
<string name="cookies_cleared">Cookies cleared</string>
|
||||
<string name="clear_database">Clear database</string>
|
||||
<string name="clear_database_source_item_count">%1$d non-library manga in database</string>
|
||||
<string name="clear_database_summary">Delete manga and chapters that are not in your library</string>
|
||||
<string name="clear_database_confirmation">Are you sure? Read chapters and progress of non-library manga will be lost</string>
|
||||
<string name="clear_database_completed">Entries deleted</string>
|
||||
<string name="database_clean">Database clean</string>
|
||||
<string name="refresh_library_metadata">Refresh library metadata</string>
|
||||
<string name="updates_covers_genres_desc">Updates covers, genres, description and manga status information</string>
|
||||
<string name="refresh_tracking_metadata">Refresh tracking metadata</string>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue