Add ability to sort library by date added (closes #1287)

(cherry picked from commit 1813dbbf590447861591f41c225007a451bb3e83)

# Conflicts:
#	app/build.gradle
#	app/src/main/java/eu/kanade/tachiyomi/data/database/DbOpenCallback.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SearchPresenter.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySettingsSheet.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySort.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersPresenter.kt
This commit is contained in:
arkon 2020-07-10 13:08:21 -04:00 committed by Jobobby04
parent c543622268
commit c0a4f4e93a
19 changed files with 92 additions and 6 deletions

View File

@ -303,6 +303,13 @@ dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-reactive:$coroutines_version" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-reactive:$coroutines_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-rx2:$coroutines_version" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-rx2:$coroutines_version"
// For detecting memory leaks; see https://square.github.io/leakcanary/
// debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.2'
// Debug tool; see https://fbflipper.com/
// debugImplementation 'com.facebook.flipper:flipper:0.49.0'
// debugImplementation 'com.facebook.soloader:soloader:0.9.0'
// Text distance (EH) // Text distance (EH)
implementation 'info.debatty:java-string-similarity:1.2.1' implementation 'info.debatty:java-string-similarity:1.2.1'

View File

@ -63,6 +63,15 @@ open class App : Application(), LifecycleObserver {
workaroundAndroid7BrokenSSL() workaroundAndroid7BrokenSSL()
// Debug tool; see https://fbflipper.com/
// SoLoader.init(this, false)
// if (BuildConfig.DEBUG && FlipperUtils.shouldEnableFlipper(this)) {
// val client = AndroidFlipperClient.getInstance(this)
// client.addPlugin(InspectorFlipperPlugin(this, DescriptorMapping.withDefaults()))
// client.addPlugin(DatabasesFlipperPlugin(this))
// client.start()
// }
// Enforce WebView availability // Enforce WebView availability
if (!WebViewUtil.supportsWebView(this)) { if (!WebViewUtil.supportsWebView(this)) {
toast(R.string.information_webview_required, Toast.LENGTH_LONG) toast(R.string.information_webview_required, Toast.LENGTH_LONG)

View File

@ -24,7 +24,7 @@ class DbOpenCallback : SupportSQLiteOpenHelper.Callback(DATABASE_VERSION) {
/** /**
* Version of the database. * Version of the database.
*/ */
const val DATABASE_VERSION = /* SY --> */ 2 /* SY <-- */ const val DATABASE_VERSION = /* SY --> */ 3 /* SY <-- */
} }
override fun onCreate(db: SupportSQLiteDatabase) = with(db) { override fun onCreate(db: SupportSQLiteDatabase) = with(db) {
@ -66,6 +66,10 @@ class DbOpenCallback : SupportSQLiteOpenHelper.Callback(DATABASE_VERSION) {
if (oldVersion < 2) { if (oldVersion < 2) {
db.execSQL(MangaTable.addCoverLastModified) db.execSQL(MangaTable.addCoverLastModified)
} }
if (oldVersion < 3) {
db.execSQL(MangaTable.addDateAdded)
db.execSQL(MangaTable.backfillDateAdded)
}
} }
override fun onConfigure(db: SupportSQLiteDatabase) { override fun onConfigure(db: SupportSQLiteDatabase) {

View File

@ -15,6 +15,7 @@ import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_ARTIST
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_AUTHOR import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_AUTHOR
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_CHAPTER_FLAGS import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_CHAPTER_FLAGS
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_COVER_LAST_MODIFIED import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_COVER_LAST_MODIFIED
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_DATE_ADDED
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_DESCRIPTION import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_DESCRIPTION
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_FAVORITE import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_FAVORITE
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_GENRE import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_GENRE
@ -47,7 +48,7 @@ class MangaPutResolver : DefaultPutResolver<Manga>() {
.whereArgs(obj.id) .whereArgs(obj.id)
.build() .build()
override fun mapToContentValues(obj: Manga) = ContentValues(15).apply { override fun mapToContentValues(obj: Manga) = ContentValues(17).apply {
put(COL_ID, obj.id) put(COL_ID, obj.id)
put(COL_SOURCE, obj.source) put(COL_SOURCE, obj.source)
put(COL_URL, obj.url) put(COL_URL, obj.url)
@ -64,6 +65,7 @@ class MangaPutResolver : DefaultPutResolver<Manga>() {
put(COL_VIEWER, obj.viewer) put(COL_VIEWER, obj.viewer)
put(COL_CHAPTER_FLAGS, obj.chapter_flags) put(COL_CHAPTER_FLAGS, obj.chapter_flags)
put(COL_COVER_LAST_MODIFIED, obj.cover_last_modified) put(COL_COVER_LAST_MODIFIED, obj.cover_last_modified)
put(COL_DATE_ADDED, obj.date_added)
} }
} }
@ -85,6 +87,7 @@ interface BaseMangaGetResolver {
viewer = cursor.getInt(cursor.getColumnIndex(COL_VIEWER)) viewer = cursor.getInt(cursor.getColumnIndex(COL_VIEWER))
chapter_flags = cursor.getInt(cursor.getColumnIndex(COL_CHAPTER_FLAGS)) chapter_flags = cursor.getInt(cursor.getColumnIndex(COL_CHAPTER_FLAGS))
cover_last_modified = cursor.getLong(cursor.getColumnIndex(COL_COVER_LAST_MODIFIED)) cover_last_modified = cursor.getLong(cursor.getColumnIndex(COL_COVER_LAST_MODIFIED))
date_added = cursor.getLong(cursor.getColumnIndex(COL_DATE_ADDED))
} }
} }

View File

@ -12,6 +12,8 @@ interface Manga : SManga {
var last_update: Long var last_update: Long
var date_added: Long
var viewer: Int var viewer: Int
var chapter_flags: Int var chapter_flags: Int

View File

@ -48,6 +48,8 @@ open class MangaImpl : Manga {
override var last_update: Long = 0 override var last_update: Long = 0
override var date_added: Long = 0
override var initialized: Boolean = false override var initialized: Boolean = false
override var viewer: Int = 0 override var viewer: Int = 0

View File

@ -28,6 +28,8 @@ object MangaTable {
const val COL_LAST_UPDATE = "last_update" const val COL_LAST_UPDATE = "last_update"
const val COL_DATE_ADDED = "date_added"
const val COL_INITIALIZED = "initialized" const val COL_INITIALIZED = "initialized"
const val COL_VIEWER = "viewer" const val COL_VIEWER = "viewer"
@ -58,7 +60,8 @@ object MangaTable {
$COL_INITIALIZED BOOLEAN NOT NULL, $COL_INITIALIZED BOOLEAN NOT NULL,
$COL_VIEWER INTEGER NOT NULL, $COL_VIEWER INTEGER NOT NULL,
$COL_CHAPTER_FLAGS INTEGER NOT NULL, $COL_CHAPTER_FLAGS INTEGER NOT NULL,
$COL_COVER_LAST_MODIFIED LONG NOT NULL $COL_COVER_LAST_MODIFIED LONG NOT NULL,
$COL_DATE_ADDED LONG NOT NULL
)""" )"""
val createUrlIndexQuery: String val createUrlIndexQuery: String
@ -70,4 +73,17 @@ object MangaTable {
val addCoverLastModified: String val addCoverLastModified: String
get() = "ALTER TABLE $TABLE ADD COLUMN $COL_COVER_LAST_MODIFIED LONG NOT NULL DEFAULT 0" get() = "ALTER TABLE $TABLE ADD COLUMN $COL_COVER_LAST_MODIFIED LONG NOT NULL DEFAULT 0"
val addDateAdded: String
get() = "ALTER TABLE $TABLE ADD COLUMN $COL_DATE_ADDED LONG NOT NULL DEFAULT 0"
/**
* Used with addDateAdded to populate it with the oldest chapter fetch date.
*/
val backfillDateAdded: String
get() = "UPDATE $TABLE SET $COL_DATE_ADDED = " +
"(SELECT MIN(${ChapterTable.COL_DATE_FETCH}) " +
"FROM $TABLE INNER JOIN ${ChapterTable.TABLE} " +
"ON $TABLE.$COL_ID = ${ChapterTable.TABLE}.${ChapterTable.COL_MANGA_ID} " +
"GROUP BY $TABLE.$COL_ID)"
} }

View File

@ -8,6 +8,7 @@ import eu.kanade.tachiyomi.data.database.models.MangaCategory
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.ui.browse.migration.MigrationFlags import eu.kanade.tachiyomi.ui.browse.migration.MigrationFlags
import eu.kanade.tachiyomi.util.lang.launchUI import eu.kanade.tachiyomi.util.lang.launchUI
import java.util.Date
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel import kotlinx.coroutines.cancel
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -135,7 +136,10 @@ class MigrationProcessAdapter(
// Update favorite status // Update favorite status
if (replace) { if (replace) {
prevManga.favorite = false prevManga.favorite = false
manga.date_added = prevManga.date_added
db.updateMangaFavorite(prevManga).executeAsBlocking() db.updateMangaFavorite(prevManga).executeAsBlocking()
} else {
manga.date_added = Date().time
} }
manga.favorite = true manga.favorite = true

View File

@ -11,6 +11,7 @@ import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.ui.browse.migration.MigrationFlags import eu.kanade.tachiyomi.ui.browse.migration.MigrationFlags
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
import exh.debug.DebugFunctions.sourceManager import exh.debug.DebugFunctions.sourceManager
import java.util.Date
import rx.Observable import rx.Observable
import rx.android.schedulers.AndroidSchedulers import rx.android.schedulers.AndroidSchedulers
import rx.schedulers.Schedulers import rx.schedulers.Schedulers
@ -101,7 +102,11 @@ class MigrationMangaPresenter(
// Update favorite status // Update favorite status
if (replace) { if (replace) {
prevManga.favorite = false prevManga.favorite = false
manga.date_added = prevManga.date_added
prevManga.date_added = 0
db.updateMangaFavorite(prevManga).executeAsBlocking() db.updateMangaFavorite(prevManga).executeAsBlocking()
} else {
manga.date_added = Date().time
} }
manga.favorite = true manga.favorite = true
db.updateMangaFavorite(manga).executeAsBlocking() db.updateMangaFavorite(manga).executeAsBlocking()

View File

@ -39,6 +39,7 @@ import eu.kanade.tachiyomi.ui.browse.source.filter.TriStateSectionItem
import eu.kanade.tachiyomi.util.removeCovers import eu.kanade.tachiyomi.util.removeCovers
import exh.EXHSavedSearch import exh.EXHSavedSearch
import java.lang.RuntimeException import java.lang.RuntimeException
import java.util.Date
import kotlinx.coroutines.flow.subscribe import kotlinx.coroutines.flow.subscribe
import rx.Observable import rx.Observable
import rx.Subscription import rx.Subscription
@ -279,9 +280,15 @@ open class BrowseSourcePresenter(
*/ */
fun changeMangaFavorite(manga: Manga) { fun changeMangaFavorite(manga: Manga) {
manga.favorite = !manga.favorite manga.favorite = !manga.favorite
manga.date_added = when (manga.favorite) {
true -> Date().time
false -> 0
}
if (!manga.favorite) { if (!manga.favorite) {
manga.removeCovers(coverCache) manga.removeCovers(coverCache)
} }
db.insertManga(manga).executeAsBlocking() db.insertManga(manga).executeAsBlocking()
} }

View File

@ -33,6 +33,7 @@ import exh.favorites.FavoritesSyncHelper
import exh.util.isLewd import exh.util.isLewd
import java.util.Collections import java.util.Collections
import java.util.Comparator import java.util.Comparator
import java.util.Date
import rx.Observable import rx.Observable
import rx.Subscription import rx.Subscription
import rx.android.schedulers.AndroidSchedulers import rx.android.schedulers.AndroidSchedulers
@ -246,6 +247,7 @@ class LibraryPresenter(
?: latestChapterManga.size ?: latestChapterManga.size
manga1latestChapter.compareTo(manga2latestChapter) manga1latestChapter.compareTo(manga2latestChapter)
} }
LibrarySort.DATE_ADDED -> i2.manga.date_added.compareTo(i1.manga.date_added)
// SY --> // SY -->
LibrarySort.DRAG_AND_DROP -> { LibrarySort.DRAG_AND_DROP -> {
0 0
@ -481,7 +483,11 @@ class LibraryPresenter(
// Update favorite status // Update favorite status
if (replace) { if (replace) {
prevManga.favorite = false prevManga.favorite = false
manga.date_added = prevManga.date_added
prevManga.date_added = 0
db.updateMangaFavorite(prevManga).executeAsBlocking() db.updateMangaFavorite(prevManga).executeAsBlocking()
} else {
manga.date_added = Date().time
} }
manga.favorite = true manga.favorite = true
db.updateMangaFavorite(manga).executeAsBlocking() db.updateMangaFavorite(manga).executeAsBlocking()

View File

@ -149,6 +149,7 @@ class LibrarySettingsSheet(
private val lastChecked = Item.MultiSort(R.string.action_sort_last_checked, this) private val lastChecked = Item.MultiSort(R.string.action_sort_last_checked, this)
private val unread = Item.MultiSort(R.string.action_filter_unread, this) private val unread = Item.MultiSort(R.string.action_filter_unread, this)
private val latestChapter = Item.MultiSort(R.string.action_sort_latest_chapter, this) private val latestChapter = Item.MultiSort(R.string.action_sort_latest_chapter, this)
private val dateAdded = Item.MultiSort(R.string.action_sort_date_added, this)
// SY --> // SY -->
private val dragAndDrop = Item.MultiSort(R.string.action_sort_drag_and_drop, this) private val dragAndDrop = Item.MultiSort(R.string.action_sort_drag_and_drop, this)
// SY <-- // SY <--
@ -174,9 +175,12 @@ class LibrarySettingsSheet(
if (sorting == LibrarySort.LAST_CHECKED) order else Item.MultiSort.SORT_NONE if (sorting == LibrarySort.LAST_CHECKED) order else Item.MultiSort.SORT_NONE
unread.state = unread.state =
if (sorting == LibrarySort.UNREAD) order else Item.MultiSort.SORT_NONE if (sorting == LibrarySort.UNREAD) order else Item.MultiSort.SORT_NONE
total.state = if (sorting == LibrarySort.TOTAL) order else Item.MultiSort.SORT_NONE total.state =
if (sorting == LibrarySort.TOTAL) order else Item.MultiSort.SORT_NONE
latestChapter.state = latestChapter.state =
if (sorting == LibrarySort.LATEST_CHAPTER) order else Item.MultiSort.SORT_NONE if (sorting == LibrarySort.LATEST_CHAPTER) order else Item.MultiSort.SORT_NONE
dateAdded.state =
if (sorting == LibrarySort.DATE_ADDED) order else Item.MultiSort.SORT_NONE
// SY --> // SY -->
dragAndDrop.state = if (sorting == LibrarySort.DRAG_AND_DROP) order else Item.MultiSort.SORT_NONE dragAndDrop.state = if (sorting == LibrarySort.DRAG_AND_DROP) order else Item.MultiSort.SORT_NONE
// SY <-- // SY <--
@ -211,6 +215,7 @@ class LibrarySettingsSheet(
unread -> LibrarySort.UNREAD unread -> LibrarySort.UNREAD
total -> LibrarySort.TOTAL total -> LibrarySort.TOTAL
latestChapter -> LibrarySort.LATEST_CHAPTER latestChapter -> LibrarySort.LATEST_CHAPTER
dateAdded -> LibrarySort.DATE_ADDED
// SY --> // SY -->
dragAndDrop -> LibrarySort.DRAG_AND_DROP dragAndDrop -> LibrarySort.DRAG_AND_DROP
// SY <-- // SY <--

View File

@ -8,6 +8,7 @@ object LibrarySort {
const val UNREAD = 3 const val UNREAD = 3
const val TOTAL = 4 const val TOTAL = 4
const val LATEST_CHAPTER = 6 const val LATEST_CHAPTER = 6
const val DATE_ADDED = 8
// SY --> // SY -->
const val DRAG_AND_DROP = 7 const val DRAG_AND_DROP = 7
// SY <-- // SY <--

View File

@ -4,6 +4,7 @@ import android.os.Build
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys
import eu.kanade.tachiyomi.data.preference.asImmediateFlow
import eu.kanade.tachiyomi.util.preference.defaultValue import eu.kanade.tachiyomi.util.preference.defaultValue
import eu.kanade.tachiyomi.util.preference.entriesRes import eu.kanade.tachiyomi.util.preference.entriesRes
import eu.kanade.tachiyomi.util.preference.intListPreference import eu.kanade.tachiyomi.util.preference.intListPreference
@ -13,6 +14,7 @@ import eu.kanade.tachiyomi.util.preference.summaryRes
import eu.kanade.tachiyomi.util.preference.switchPreference import eu.kanade.tachiyomi.util.preference.switchPreference
import eu.kanade.tachiyomi.util.preference.titleRes import eu.kanade.tachiyomi.util.preference.titleRes
import eu.kanade.tachiyomi.util.system.hasDisplayCutout import eu.kanade.tachiyomi.util.system.hasDisplayCutout
import kotlinx.coroutines.flow.launchIn
class SettingsReaderController : SettingsController() { class SettingsReaderController : SettingsController() {

View File

@ -8,6 +8,7 @@ import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.online.UrlImportableSource import eu.kanade.tachiyomi.source.online.UrlImportableSource
import eu.kanade.tachiyomi.source.online.all.EHentai import eu.kanade.tachiyomi.source.online.all.EHentai
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
import java.util.Date
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
class GalleryAdder { class GalleryAdder {
@ -93,7 +94,10 @@ class GalleryAdder {
manga.copyFrom(newManga) manga.copyFrom(newManga)
manga.initialized = true manga.initialized = true
if (fav) manga.favorite = true if (fav) {
manga.favorite = true
manga.date_added = Date().time
}
db.insertManga(manga).executeAsBlocking() db.insertManga(manga).executeAsBlocking()

View File

@ -9,6 +9,7 @@ import eu.kanade.tachiyomi.data.database.models.MangaCategory
import exh.metadata.metadata.EHentaiSearchMetadata import exh.metadata.metadata.EHentaiSearchMetadata
import exh.metadata.metadata.base.getFlatMetadataForManga import exh.metadata.metadata.base.getFlatMetadataForManga
import java.io.File import java.io.File
import java.util.Date
import rx.Observable import rx.Observable
import rx.Single import rx.Single
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
@ -129,8 +130,12 @@ class EHentaiUpdateHelper(context: Context) {
} }
} }
toDiscard.forEach { it.manga.favorite = false } toDiscard.forEach {
it.manga.favorite = false
it.manga.date_added = 0
}
accepted.manga.favorite = true accepted.manga.favorite = true
accepted.manga.date_added = Date().time
val newAccepted = ChapterChain(accepted.manga, newChapters) val newAccepted = ChapterChain(accepted.manga, newChapters)
val rootsToMutate = toDiscard + newAccepted val rootsToMutate = toDiscard + newAccepted

View File

@ -339,6 +339,7 @@ class FavoritesSyncHelper(val context: Context) {
if (manga?.favorite == true) { if (manga?.favorite == true) {
manga.favorite = false manga.favorite = false
manga.date_added = 0
db.updateMangaFavorite(manga).executeAsBlocking() db.updateMangaFavorite(manga).executeAsBlocking()
removedManga += manga removedManga += manga
} }

View File

@ -8,6 +8,7 @@ import exh.EXH_SOURCE_ID
import exh.metadata.metadata.EHentaiSearchMetadata import exh.metadata.metadata.EHentaiSearchMetadata
import io.realm.Realm import io.realm.Realm
import io.realm.RealmConfiguration import io.realm.RealmConfiguration
import java.util.Date
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
class LocalFavoritesStorage { class LocalFavoritesStorage {
@ -41,6 +42,7 @@ class LocalFavoritesStorage {
it.fav, it.fav,
it.manga.apply { it.manga.apply {
favorite = true favorite = true
date_added = Date().time
} }
) )
} }

View File

@ -42,6 +42,7 @@
<string name="action_sort_last_read">Last read</string> <string name="action_sort_last_read">Last read</string>
<string name="action_sort_last_checked">Last checked</string> <string name="action_sort_last_checked">Last checked</string>
<string name="action_sort_latest_chapter">Latest chapter</string> <string name="action_sort_latest_chapter">Latest chapter</string>
<string name="action_sort_date_added">Date added</string>
<string name="action_search">Search</string> <string name="action_search">Search</string>
<string name="action_global_search">Global search</string> <string name="action_global_search">Global search</string>
<string name="action_select_all">Select all</string> <string name="action_select_all">Select all</string>