diff --git a/app/build.gradle b/app/build.gradle index fe93d40a9..cc80b8c87 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -303,6 +303,13 @@ dependencies { implementation "org.jetbrains.kotlinx:kotlinx-coroutines-reactive:$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) implementation 'info.debatty:java-string-similarity:1.2.1' diff --git a/app/src/main/java/eu/kanade/tachiyomi/App.kt b/app/src/main/java/eu/kanade/tachiyomi/App.kt index a12c169ec..2aa30bd67 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/App.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/App.kt @@ -63,6 +63,15 @@ open class App : Application(), LifecycleObserver { 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 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 6c4ef1d4a..ac23c294f 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/DbOpenCallback.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/DbOpenCallback.kt @@ -24,7 +24,7 @@ class DbOpenCallback : SupportSQLiteOpenHelper.Callback(DATABASE_VERSION) { /** * 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) { @@ -66,6 +66,10 @@ class DbOpenCallback : SupportSQLiteOpenHelper.Callback(DATABASE_VERSION) { if (oldVersion < 2) { db.execSQL(MangaTable.addCoverLastModified) } + if (oldVersion < 3) { + 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 72e078e03..98be1de12 100755 --- 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 fe3c074d5..07ae1ab3e 100755 --- 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 6d835522d..f8502d117 100755 --- 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 @@ -48,6 +48,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 1004340b2..bd6f79f4f 100755 --- 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/advanced/process/MigrationProcessAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigrationProcessAdapter.kt index 7c6b66846..20d9ec11e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigrationProcessAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/advanced/process/MigrationProcessAdapter.kt @@ -8,6 +8,7 @@ import eu.kanade.tachiyomi.data.database.models.MangaCategory import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.ui.browse.migration.MigrationFlags import eu.kanade.tachiyomi.util.lang.launchUI +import java.util.Date import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.cancel import kotlinx.coroutines.withContext @@ -135,7 +136,10 @@ class MigrationProcessAdapter( // Update favorite status if (replace) { prevManga.favorite = false + manga.date_added = prevManga.date_added db.updateMangaFavorite(prevManga).executeAsBlocking() + } else { + manga.date_added = Date().time } manga.favorite = true diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrationMangaPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrationMangaPresenter.kt index 0f6d7cfdd..ca0e929e9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrationMangaPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrationMangaPresenter.kt @@ -11,6 +11,7 @@ import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter import eu.kanade.tachiyomi.ui.browse.migration.MigrationFlags import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource import exh.debug.DebugFunctions.sourceManager +import java.util.Date import rx.Observable import rx.android.schedulers.AndroidSchedulers import rx.schedulers.Schedulers @@ -101,7 +102,11 @@ class MigrationMangaPresenter( // Update favorite status if (replace) { prevManga.favorite = false + manga.date_added = prevManga.date_added + prevManga.date_added = 0 db.updateMangaFavorite(prevManga).executeAsBlocking() + } else { + manga.date_added = Date().time } manga.favorite = true db.updateMangaFavorite(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 457049887..b8d9d337a 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 @@ -39,6 +39,7 @@ import eu.kanade.tachiyomi.ui.browse.source.filter.TriStateSectionItem import eu.kanade.tachiyomi.util.removeCovers import exh.EXHSavedSearch import java.lang.RuntimeException +import java.util.Date import kotlinx.coroutines.flow.subscribe import rx.Observable import rx.Subscription @@ -279,9 +280,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 4a7e432ba..7fa2d5238 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt @@ -33,6 +33,7 @@ import exh.favorites.FavoritesSyncHelper import exh.util.isLewd import java.util.Collections import java.util.Comparator +import java.util.Date import rx.Observable import rx.Subscription import rx.android.schedulers.AndroidSchedulers @@ -246,6 +247,7 @@ class LibraryPresenter( ?: latestChapterManga.size manga1latestChapter.compareTo(manga2latestChapter) } + LibrarySort.DATE_ADDED -> i2.manga.date_added.compareTo(i1.manga.date_added) // SY --> LibrarySort.DRAG_AND_DROP -> { 0 @@ -481,7 +483,11 @@ class LibraryPresenter( // Update favorite status if (replace) { prevManga.favorite = false + manga.date_added = prevManga.date_added + prevManga.date_added = 0 db.updateMangaFavorite(prevManga).executeAsBlocking() + } else { + manga.date_added = Date().time } manga.favorite = true db.updateMangaFavorite(manga).executeAsBlocking() 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 0a6c93520..9c97a1b9f 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 @@ -149,6 +149,7 @@ 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) // SY --> private val dragAndDrop = Item.MultiSort(R.string.action_sort_drag_and_drop, this) // SY <-- @@ -174,9 +175,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 // SY --> dragAndDrop.state = if (sorting == LibrarySort.DRAG_AND_DROP) order else Item.MultiSort.SORT_NONE // SY <-- @@ -211,6 +215,7 @@ class LibrarySettingsSheet( unread -> LibrarySort.UNREAD total -> LibrarySort.TOTAL latestChapter -> LibrarySort.LATEST_CHAPTER + dateAdded -> LibrarySort.DATE_ADDED // SY --> dragAndDrop -> LibrarySort.DRAG_AND_DROP // SY <-- 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 95268d48a..0c203fe40 100755 --- 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 = 8 // SY --> const val DRAG_AND_DROP = 7 // SY <-- diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsReaderController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsReaderController.kt index 220b4b36b..5b764e4d8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsReaderController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsReaderController.kt @@ -4,6 +4,7 @@ import android.os.Build import androidx.preference.PreferenceScreen import eu.kanade.tachiyomi.R 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.entriesRes 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.titleRes import eu.kanade.tachiyomi.util.system.hasDisplayCutout +import kotlinx.coroutines.flow.launchIn class SettingsReaderController : SettingsController() { diff --git a/app/src/main/java/exh/GalleryAdder.kt b/app/src/main/java/exh/GalleryAdder.kt index 2a410168d..c6e73a731 100755 --- a/app/src/main/java/exh/GalleryAdder.kt +++ b/app/src/main/java/exh/GalleryAdder.kt @@ -8,6 +8,7 @@ import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.online.UrlImportableSource import eu.kanade.tachiyomi.source.online.all.EHentai import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource +import java.util.Date import uy.kohesive.injekt.injectLazy class GalleryAdder { @@ -93,7 +94,10 @@ class GalleryAdder { manga.copyFrom(newManga) manga.initialized = true - if (fav) manga.favorite = true + if (fav) { + manga.favorite = true + manga.date_added = Date().time + } db.insertManga(manga).executeAsBlocking() diff --git a/app/src/main/java/exh/eh/EHentaiUpdateHelper.kt b/app/src/main/java/exh/eh/EHentaiUpdateHelper.kt index 9c5e8ed5e..d575ec92e 100644 --- a/app/src/main/java/exh/eh/EHentaiUpdateHelper.kt +++ b/app/src/main/java/exh/eh/EHentaiUpdateHelper.kt @@ -9,6 +9,7 @@ import eu.kanade.tachiyomi.data.database.models.MangaCategory import exh.metadata.metadata.EHentaiSearchMetadata import exh.metadata.metadata.base.getFlatMetadataForManga import java.io.File +import java.util.Date import rx.Observable import rx.Single 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.date_added = Date().time val newAccepted = ChapterChain(accepted.manga, newChapters) val rootsToMutate = toDiscard + newAccepted diff --git a/app/src/main/java/exh/favorites/FavoritesSyncHelper.kt b/app/src/main/java/exh/favorites/FavoritesSyncHelper.kt index 30b1960f4..d5b758866 100644 --- a/app/src/main/java/exh/favorites/FavoritesSyncHelper.kt +++ b/app/src/main/java/exh/favorites/FavoritesSyncHelper.kt @@ -339,6 +339,7 @@ class FavoritesSyncHelper(val context: Context) { if (manga?.favorite == true) { manga.favorite = false + manga.date_added = 0 db.updateMangaFavorite(manga).executeAsBlocking() removedManga += manga } diff --git a/app/src/main/java/exh/favorites/LocalFavoritesStorage.kt b/app/src/main/java/exh/favorites/LocalFavoritesStorage.kt index ca499d980..e09fc75c0 100644 --- a/app/src/main/java/exh/favorites/LocalFavoritesStorage.kt +++ b/app/src/main/java/exh/favorites/LocalFavoritesStorage.kt @@ -8,6 +8,7 @@ import exh.EXH_SOURCE_ID import exh.metadata.metadata.EHentaiSearchMetadata import io.realm.Realm import io.realm.RealmConfiguration +import java.util.Date import uy.kohesive.injekt.injectLazy class LocalFavoritesStorage { @@ -41,6 +42,7 @@ class LocalFavoritesStorage { it.fav, it.manga.apply { favorite = true + date_added = Date().time } ) } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5acbff1a4..4b71e5d38 100755 --- 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