From 1813dbbf590447861591f41c225007a451bb3e83 Mon Sep 17 00:00:00 2001 From: arkon Date: Fri, 10 Jul 2020 13:08:21 -0400 Subject: [PATCH] Add ability to sort library by date added (closes #1287) --- app/build.gradle | 4 ++++ app/src/main/java/eu/kanade/tachiyomi/App.kt | 9 +++++++++ .../tachiyomi/data/database/DbOpenCallback.kt | 6 +++++- .../data/database/mappers/MangaTypeMapping.kt | 5 ++++- .../tachiyomi/data/database/models/Manga.kt | 2 ++ .../data/database/models/MangaImpl.kt | 2 ++ .../data/database/tables/MangaTable.kt | 18 +++++++++++++++++- .../browse/migration/search/SearchPresenter.kt | 9 +++++++++ .../source/browse/BrowseSourcePresenter.kt | 7 +++++++ .../tachiyomi/ui/library/LibraryPresenter.kt | 1 + .../ui/library/LibrarySettingsSheet.kt | 9 +++++++-- .../kanade/tachiyomi/ui/library/LibrarySort.kt | 1 + .../chapter/MangaInfoChaptersPresenter.kt | 5 +++++ app/src/main/res/values/strings.xml | 1 + 14 files changed, 74 insertions(+), 5 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 15a717f251..899c2ec80c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -284,6 +284,10 @@ dependencies { // 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' } buildscript { diff --git a/app/src/main/java/eu/kanade/tachiyomi/App.kt b/app/src/main/java/eu/kanade/tachiyomi/App.kt index c733fc4d41..42b5f93618 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/App.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/App.kt @@ -43,6 +43,15 @@ open class App : Application(), LifecycleObserver { super.onCreate() if (BuildConfig.DEBUG) Timber.plant(Timber.DebugTree()) + // 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 if (!WebViewUtil.supportsWebView(this)) { toast(R.string.information_webview_required, Toast.LENGTH_LONG) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/DbOpenCallback.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/DbOpenCallback.kt index a8be598305..c0841b368a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/DbOpenCallback.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/DbOpenCallback.kt @@ -20,7 +20,7 @@ class DbOpenCallback : SupportSQLiteOpenHelper.Callback(DATABASE_VERSION) { /** * Version of the database. */ - const val DATABASE_VERSION = 10 + const val DATABASE_VERSION = 11 } override fun onCreate(db: SupportSQLiteDatabase) = with(db) { @@ -78,6 +78,10 @@ class DbOpenCallback : SupportSQLiteOpenHelper.Callback(DATABASE_VERSION) { if (oldVersion < 10) { db.execSQL(MangaTable.addCoverLastModified) } + if (oldVersion < 11) { + db.execSQL(MangaTable.addDateAdded) + db.execSQL(MangaTable.backfillDateAdded) + } } override fun onConfigure(db: SupportSQLiteDatabase) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/mappers/MangaTypeMapping.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/mappers/MangaTypeMapping.kt index 910bcd67c1..34fcf560c0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/mappers/MangaTypeMapping.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/mappers/MangaTypeMapping.kt @@ -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_CHAPTER_FLAGS 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_FAVORITE import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_GENRE @@ -47,7 +48,7 @@ class MangaPutResolver : DefaultPutResolver() { .whereArgs(obj.id) .build() - override fun mapToContentValues(obj: Manga) = ContentValues(15).apply { + override fun mapToContentValues(obj: Manga) = ContentValues(17).apply { put(COL_ID, obj.id) put(COL_SOURCE, obj.source) put(COL_URL, obj.url) @@ -64,6 +65,7 @@ class MangaPutResolver : DefaultPutResolver() { put(COL_VIEWER, obj.viewer) put(COL_CHAPTER_FLAGS, obj.chapter_flags) 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)) chapter_flags = cursor.getInt(cursor.getColumnIndex(COL_CHAPTER_FLAGS)) cover_last_modified = cursor.getLong(cursor.getColumnIndex(COL_COVER_LAST_MODIFIED)) + date_added = cursor.getLong(cursor.getColumnIndex(COL_DATE_ADDED)) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Manga.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Manga.kt index fe3c074d5e..07ae1ab3e5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Manga.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Manga.kt @@ -12,6 +12,8 @@ interface Manga : SManga { var last_update: Long + var date_added: Long + var viewer: Int var chapter_flags: Int diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/MangaImpl.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/MangaImpl.kt index eda1d4abc8..32cc47309d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/MangaImpl.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/MangaImpl.kt @@ -26,6 +26,8 @@ open class MangaImpl : Manga { override var last_update: Long = 0 + override var date_added: Long = 0 + override var initialized: Boolean = false override var viewer: Int = 0 diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/tables/MangaTable.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/tables/MangaTable.kt index 1004340b25..bd6f79f4fb 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/tables/MangaTable.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/tables/MangaTable.kt @@ -28,6 +28,8 @@ object MangaTable { const val COL_LAST_UPDATE = "last_update" + const val COL_DATE_ADDED = "date_added" + const val COL_INITIALIZED = "initialized" const val COL_VIEWER = "viewer" @@ -58,7 +60,8 @@ object MangaTable { $COL_INITIALIZED BOOLEAN NOT NULL, $COL_VIEWER 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 @@ -70,4 +73,17 @@ object MangaTable { val addCoverLastModified: String 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)" } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SearchPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SearchPresenter.kt index 1fe62b2aa9..910cf148f7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SearchPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SearchPresenter.kt @@ -13,6 +13,7 @@ import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchCardItem import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchItem import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchPresenter import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource +import java.util.Date import rx.Observable import rx.android.schedulers.AndroidSchedulers import rx.schedulers.Schedulers @@ -147,6 +148,14 @@ class SearchPresenter( manga.viewer = prevManga.viewer db.updateMangaViewer(manga).executeAsBlocking() + // Update date added + if (replace) { + manga.date_added = prevManga.date_added + prevManga.date_added = 0 + } else { + manga.date_added = Date().time + } + // SearchPresenter#networkToLocalManga may have updated the manga title, so ensure db gets updated title db.updateMangaTitle(manga).executeAsBlocking() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt index baabd31f8b..7a75b0f5a4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt @@ -29,6 +29,7 @@ import eu.kanade.tachiyomi.ui.browse.source.filter.TextSectionItem import eu.kanade.tachiyomi.ui.browse.source.filter.TriStateItem import eu.kanade.tachiyomi.ui.browse.source.filter.TriStateSectionItem import eu.kanade.tachiyomi.util.removeCovers +import java.util.Date import kotlinx.coroutines.flow.subscribe import rx.Observable import rx.Subscription @@ -260,9 +261,15 @@ open class BrowseSourcePresenter( */ fun changeMangaFavorite(manga: Manga) { manga.favorite = !manga.favorite + manga.date_added = when (manga.favorite) { + true -> Date().time + false -> 0 + } + if (!manga.favorite) { manga.removeCovers(coverCache) } + db.insertManga(manga).executeAsBlocking() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt index 53ea010a9a..9b1290ffc1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt @@ -218,6 +218,7 @@ class LibraryPresenter( ?: latestChapterManga.size manga1latestChapter.compareTo(manga2latestChapter) } + LibrarySort.DATE_ADDED -> i2.manga.date_added.compareTo(i1.manga.date_added) else -> throw Exception("Unknown sorting mode") } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySettingsSheet.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySettingsSheet.kt index ae4182004d..d4b3d3af58 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySettingsSheet.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySettingsSheet.kt @@ -111,10 +111,11 @@ class LibrarySettingsSheet( 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 latestChapter = Item.MultiSort(R.string.action_sort_latest_chapter, this) + private val dateAdded = Item.MultiSort(R.string.action_sort_date_added, this) override val header = null override val items = - listOf(alphabetically, lastRead, lastChecked, unread, total, latestChapter) + listOf(alphabetically, lastRead, lastChecked, unread, total, latestChapter, dateAdded) override val footer = null override fun initModels() { @@ -133,9 +134,12 @@ class LibrarySettingsSheet( if (sorting == LibrarySort.LAST_CHECKED) order else Item.MultiSort.SORT_NONE unread.state = 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 = if (sorting == LibrarySort.LATEST_CHAPTER) order else Item.MultiSort.SORT_NONE + dateAdded.state = + if (sorting == LibrarySort.DATE_ADDED) order else Item.MultiSort.SORT_NONE } override fun onItemClicked(item: Item) { @@ -161,6 +165,7 @@ class LibrarySettingsSheet( unread -> LibrarySort.UNREAD total -> LibrarySort.TOTAL latestChapter -> LibrarySort.LATEST_CHAPTER + dateAdded -> LibrarySort.DATE_ADDED else -> throw Exception("Unknown sorting") } ) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySort.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySort.kt index 5dfdff91fe..5f6e6da8c3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySort.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySort.kt @@ -8,6 +8,7 @@ object LibrarySort { const val UNREAD = 3 const val TOTAL = 4 const val LATEST_CHAPTER = 6 + const val DATE_ADDED = 7 @Deprecated("Removed in favor of searching by source") const val SOURCE = 5 diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/MangaInfoChaptersPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/MangaInfoChaptersPresenter.kt index 0acd1da6f5..94c0c971fa 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/MangaInfoChaptersPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/MangaInfoChaptersPresenter.kt @@ -20,6 +20,7 @@ import eu.kanade.tachiyomi.util.lang.launchIO import eu.kanade.tachiyomi.util.prepUpdateCover import eu.kanade.tachiyomi.util.removeCovers import eu.kanade.tachiyomi.util.shouldDownloadNewChapters +import java.util.Date import rx.Observable import rx.Subscription import rx.android.schedulers.AndroidSchedulers @@ -149,6 +150,10 @@ class MangaInfoChaptersPresenter( */ fun toggleFavorite(): Boolean { manga.favorite = !manga.favorite + manga.date_added = when (manga.favorite) { + true -> Date().time + false -> 0 + } if (!manga.favorite) { manga.removeCovers(coverCache) } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a283ef0c23..0c604b8e59 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -42,6 +42,7 @@ Last read Last checked Latest chapter + Date added Search Global search Select all