mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-30 22:07:57 +01:00 
			
		
		
		
	Added option to sort library (#536)
* Initial code * Added all sort options * Fixes * Removed sort by added. Some renaming * Removed date added database calls * Fixes
This commit is contained in:
		
				
					committed by
					
						 inorichi
						inorichi
					
				
			
			
				
	
			
			
			
						parent
						
							d971768056
						
					
				
				
					commit
					aba528b227
				
			| @@ -6,4 +6,8 @@ object Constants { | ||||
|     const val NOTIFICATION_DOWNLOAD_CHAPTER_ID = 3 | ||||
|     const val NOTIFICATION_DOWNLOAD_CHAPTER_ERROR_ID = 4 | ||||
|     const val NOTIFICATION_DOWNLOAD_IMAGE_ID = 5 | ||||
|  | ||||
|     const val SORT_LIBRARY_ALPHA = 0 | ||||
|     const val SORT_LIBRARY_LAST_READ = 1 | ||||
|     const val SORT_LIBRARY_LAST_UPDATED = 2 | ||||
| } | ||||
|   | ||||
| @@ -40,6 +40,15 @@ interface HistoryQueries : DbProvider { | ||||
|                     .build()) | ||||
|             .prepare() | ||||
|  | ||||
|     fun getLastHistoryByMangaId(mangaId: Long) = db.get() | ||||
|             .`object`(History::class.java) | ||||
|             .withQuery(RawQuery.builder() | ||||
|                     .query(getLastHistoryByMangaId()) | ||||
|                     .args(mangaId) | ||||
|                     .observesTables(HistoryTable.TABLE) | ||||
|                     .build()) | ||||
|             .prepare() | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Updates the history last read. | ||||
|   | ||||
| @@ -7,6 +7,7 @@ import eu.kanade.tachiyomi.data.database.DbProvider | ||||
| import eu.kanade.tachiyomi.data.database.models.Manga | ||||
| import eu.kanade.tachiyomi.data.database.resolvers.LibraryMangaGetResolver | ||||
| import eu.kanade.tachiyomi.data.database.resolvers.MangaFlagsPutResolver | ||||
| import eu.kanade.tachiyomi.data.database.resolvers.MangaLastUpdatedPutResolver | ||||
| import eu.kanade.tachiyomi.data.database.tables.ChapterTable | ||||
| import eu.kanade.tachiyomi.data.database.tables.MangaCategoryTable | ||||
| import eu.kanade.tachiyomi.data.database.tables.MangaTable | ||||
| @@ -29,7 +30,7 @@ interface MangaQueries : DbProvider { | ||||
|             .withGetResolver(LibraryMangaGetResolver.INSTANCE) | ||||
|             .prepare() | ||||
|  | ||||
|     open fun getFavoriteMangas() = db.get() | ||||
|     fun getFavoriteMangas() = db.get() | ||||
|             .listOfObjects(Manga::class.java) | ||||
|             .withQuery(Query.builder() | ||||
|                     .table(MangaTable.TABLE) | ||||
| @@ -66,6 +67,11 @@ interface MangaQueries : DbProvider { | ||||
|             .withPutResolver(MangaFlagsPutResolver()) | ||||
|             .prepare() | ||||
|  | ||||
|     fun updateLastUpdated(manga: Manga) = db.put() | ||||
|             .`object`(manga) | ||||
|             .withPutResolver(MangaLastUpdatedPutResolver()) | ||||
|             .prepare() | ||||
|  | ||||
|     fun deleteManga(manga: Manga) = db.delete().`object`(manga).prepare() | ||||
|  | ||||
|     fun deleteMangas(mangas: List<Manga>) = db.delete().objects(mangas).prepare() | ||||
|   | ||||
| @@ -73,6 +73,19 @@ fun getHistoryByMangaId() = """ | ||||
|     WHERE ${Chapter.TABLE}.${Chapter.COL_MANGA_ID} = ? AND ${History.TABLE}.${History.COL_CHAPTER_ID} = ${Chapter.TABLE}.${Chapter.COL_ID} | ||||
| """ | ||||
|  | ||||
| fun getLastHistoryByMangaId() = """ | ||||
|     SELECT ${History.TABLE}.* | ||||
|     FROM ${History.TABLE} | ||||
|     JOIN ${Chapter.TABLE} | ||||
|     ON ${History.TABLE}.${History.COL_CHAPTER_ID} = ${Chapter.TABLE}.${Chapter.COL_ID} | ||||
|      LEFT JOIN ( | ||||
|             SELECT MAX(${History.TABLE}.${History.COL_LAST_READ}) AS max | ||||
|             FROM ${History.TABLE} | ||||
|             GROUP BY ${History.COL_LAST_READ} | ||||
|         ) AS M | ||||
|     WHERE ${Chapter.TABLE}.${Chapter.COL_MANGA_ID} = ? AND M.max = ${History.TABLE}.${History.COL_LAST_READ} | ||||
| """ | ||||
|  | ||||
| /** | ||||
|  * Query to get the categories for a manga. | ||||
|  */ | ||||
|   | ||||
| @@ -0,0 +1,33 @@ | ||||
| package eu.kanade.tachiyomi.data.database.resolvers | ||||
|  | ||||
| import android.content.ContentValues | ||||
| import com.pushtorefresh.storio.sqlite.StorIOSQLite | ||||
| import com.pushtorefresh.storio.sqlite.operations.put.PutResolver | ||||
| import com.pushtorefresh.storio.sqlite.operations.put.PutResult | ||||
| import com.pushtorefresh.storio.sqlite.queries.UpdateQuery | ||||
| import eu.kanade.tachiyomi.data.database.inTransactionReturn | ||||
| import eu.kanade.tachiyomi.data.database.models.Manga | ||||
| import eu.kanade.tachiyomi.data.database.tables.MangaTable | ||||
|  | ||||
| class MangaLastUpdatedPutResolver : PutResolver<Manga>() { | ||||
|  | ||||
|     override fun performPut(db: StorIOSQLite, manga: Manga) = db.inTransactionReturn { | ||||
|         val updateQuery = mapToUpdateQuery(manga) | ||||
|         val contentValues = mapToContentValues(manga) | ||||
|  | ||||
|         val numberOfRowsUpdated = db.lowLevel().update(updateQuery, contentValues) | ||||
|         PutResult.newUpdateResult(numberOfRowsUpdated, updateQuery.table()) | ||||
|     } | ||||
|  | ||||
|     fun mapToUpdateQuery(manga: Manga) = UpdateQuery.builder() | ||||
|             .table(MangaTable.TABLE) | ||||
|             .where("${MangaTable.COL_ID} = ?") | ||||
|             .whereArgs(manga.id) | ||||
|             .build() | ||||
|  | ||||
|     fun mapToContentValues(manga: Manga) = ContentValues(1).apply { | ||||
|         put(MangaTable.COL_LAST_UPDATE, manga.last_update) | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| @@ -251,7 +251,13 @@ class LibraryUpdateService : Service() { | ||||
|                             .map { manga } | ||||
|                 } | ||||
|                 // Add manga with new chapters to the list. | ||||
|                 .doOnNext { newUpdates.add(it) } | ||||
|                 .doOnNext { manga -> | ||||
|                     // Set last updated time | ||||
|                     manga.last_update = Date().time | ||||
|                     db.updateLastUpdated(manga).executeAsBlocking() | ||||
|                     // Add to the list | ||||
|                     newUpdates.add(manga) | ||||
|                 } | ||||
|                 // Notify result of the overall update. | ||||
|                 .doOnCompleted { | ||||
|                     if (newUpdates.isEmpty()) { | ||||
|   | ||||
| @@ -83,6 +83,8 @@ class PreferenceKeys(context: Context) { | ||||
|  | ||||
|     val filterUnread = context.getString(R.string.pref_filter_unread_key) | ||||
|  | ||||
|     val librarySortingMode = context.getString(R.string.pref_library_sorting_mode_key) | ||||
|  | ||||
|     val automaticUpdates = context.getString(R.string.pref_enable_automatic_updates_key) | ||||
|  | ||||
|     val startScreen = context.getString(R.string.pref_start_screen_key) | ||||
|   | ||||
| @@ -126,6 +126,8 @@ class PreferencesHelper(context: Context) { | ||||
|  | ||||
|     fun filterUnread() = rxPrefs.getBoolean(keys.filterUnread, false) | ||||
|  | ||||
|     fun librarySortingMode() = rxPrefs.getInteger(keys.librarySortingMode, 0) | ||||
|  | ||||
|     fun automaticUpdates() = prefs.getBoolean(keys.automaticUpdates, false) | ||||
|  | ||||
|     fun hiddenCatalogues() = rxPrefs.getStringSet("hidden_catalogues", emptySet()) | ||||
|   | ||||
| @@ -21,7 +21,7 @@ import rx.schedulers.Schedulers | ||||
| import rx.subjects.PublishSubject | ||||
| import timber.log.Timber | ||||
| import uy.kohesive.injekt.injectLazy | ||||
| import java.util.NoSuchElementException | ||||
| import java.util.* | ||||
|  | ||||
| /** | ||||
|  * Presenter of [CatalogueFragment]. | ||||
|   | ||||
| @@ -11,6 +11,7 @@ import android.support.v7.widget.SearchView | ||||
| import android.view.* | ||||
| import com.afollestad.materialdialogs.MaterialDialog | ||||
| import com.f2prateek.rx.preferences.Preference | ||||
| import eu.kanade.tachiyomi.Constants | ||||
| import eu.kanade.tachiyomi.R | ||||
| import eu.kanade.tachiyomi.data.database.models.Category | ||||
| import eu.kanade.tachiyomi.data.database.models.Manga | ||||
| @@ -83,6 +84,11 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback | ||||
|      */ | ||||
|     var isFilterUnread = false | ||||
|  | ||||
|     /** | ||||
|      * Sorting mode for library | ||||
|      */ | ||||
|     var sortingMode = 0 | ||||
|  | ||||
|     /** | ||||
|      * Number of manga per row in grid mode. | ||||
|      */ | ||||
| @@ -123,8 +129,9 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback | ||||
|     override fun onCreate(savedState: Bundle?) { | ||||
|         super.onCreate(savedState) | ||||
|         setHasOptionsMenu(true) | ||||
|         isFilterDownloaded = preferences.filterDownloaded().get() as Boolean | ||||
|         isFilterUnread = preferences.filterUnread().get() as Boolean | ||||
|         isFilterDownloaded = preferences.filterDownloaded().getOrDefault() | ||||
|         isFilterUnread = preferences.filterUnread().getOrDefault() | ||||
|         sortingMode = preferences.librarySortingMode().getOrDefault() | ||||
|     } | ||||
|  | ||||
|     override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedState: Bundle?): View? { | ||||
| @@ -179,12 +186,37 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback | ||||
|         super.onSaveInstanceState(outState) | ||||
|     } | ||||
|  | ||||
|     override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { | ||||
|         inflater.inflate(R.menu.library, menu) | ||||
|  | ||||
|     /** | ||||
|      * Prepare the Fragment host's standard options menu to be displayed.  This is | ||||
|      * called right before the menu is shown, every time it is shown.  You can | ||||
|      * use this method to efficiently enable/disable items or otherwise | ||||
|      * dynamically modify the contents. | ||||
|      * | ||||
|      * @param menu The options menu as last shown or first initialized by | ||||
|      */ | ||||
|     override fun onPrepareOptionsMenu(menu: Menu) { | ||||
|         // Initialize search menu | ||||
|         val filterDownloadedItem = menu.findItem(R.id.action_filter_downloaded) | ||||
|         val filterUnreadItem = menu.findItem(R.id.action_filter_unread) | ||||
|         val sortModeAlpha = menu.findItem(R.id.action_sort_alpha) | ||||
|         val sortModeLastRead = menu.findItem(R.id.action_sort_last_read) | ||||
|         val sortModeLastUpdated = menu.findItem(R.id.action_sort_last_updated) | ||||
|  | ||||
|         // Set correct checkbox filter | ||||
|         filterDownloadedItem.isChecked = isFilterDownloaded | ||||
|         filterUnreadItem.isChecked = isFilterUnread | ||||
|  | ||||
|         // Set correct radio button sort | ||||
|         when (sortingMode) { | ||||
|             Constants.SORT_LIBRARY_ALPHA -> sortModeAlpha.isChecked = true | ||||
|             Constants.SORT_LIBRARY_LAST_READ -> sortModeLastRead.isChecked = true | ||||
|             Constants.SORT_LIBRARY_LAST_UPDATED -> sortModeLastUpdated.isChecked = true | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { | ||||
|         inflater.inflate(R.menu.library, menu) | ||||
|  | ||||
|         val searchItem = menu.findItem(R.id.action_search) | ||||
|         val searchView = searchItem.actionView as SearchView | ||||
|  | ||||
| @@ -194,9 +226,6 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback | ||||
|             searchView.clearFocus() | ||||
|         } | ||||
|  | ||||
|         filterDownloadedItem.isChecked = isFilterDownloaded | ||||
|         filterUnreadItem.isChecked = isFilterUnread | ||||
|  | ||||
|         searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { | ||||
|             override fun onQueryTextSubmit(query: String): Boolean { | ||||
|                 onSearchTextChange(query) | ||||
| @@ -219,7 +248,7 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback | ||||
|                 // Update settings. | ||||
|                 preferences.filterUnread().set(isFilterUnread) | ||||
|                 // Apply filter. | ||||
|                 onFilterCheckboxChanged() | ||||
|                 onFilterOrSortChanged() | ||||
|             } | ||||
|             R.id.action_filter_downloaded -> { | ||||
|                 // Change downloaded filter status. | ||||
| @@ -227,7 +256,7 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback | ||||
|                 // Update settings. | ||||
|                 preferences.filterDownloaded().set(isFilterDownloaded) | ||||
|                 // Apply filter. | ||||
|                 onFilterCheckboxChanged() | ||||
|                 onFilterOrSortChanged() | ||||
|             } | ||||
|             R.id.action_filter_empty -> { | ||||
|                 // Remove filter status. | ||||
| @@ -237,7 +266,22 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback | ||||
|                 preferences.filterUnread().set(isFilterUnread) | ||||
|                 preferences.filterDownloaded().set(isFilterDownloaded) | ||||
|                 // Apply filter | ||||
|                 onFilterCheckboxChanged() | ||||
|                 onFilterOrSortChanged() | ||||
|             } | ||||
|             R.id.action_sort_alpha -> { | ||||
|                 sortingMode = Constants.SORT_LIBRARY_ALPHA | ||||
|                 preferences.librarySortingMode().set(sortingMode) | ||||
|                 onFilterOrSortChanged() | ||||
|             } | ||||
|             R.id.action_sort_last_read -> { | ||||
|                 sortingMode = Constants.SORT_LIBRARY_LAST_READ | ||||
|                 preferences.librarySortingMode().set(sortingMode) | ||||
|                 onFilterOrSortChanged() | ||||
|             } | ||||
|             R.id.action_sort_last_updated -> { | ||||
|                 sortingMode = Constants.SORT_LIBRARY_LAST_UPDATED | ||||
|                 preferences.librarySortingMode().set(sortingMode) | ||||
|                 onFilterOrSortChanged() | ||||
|             } | ||||
|             R.id.action_library_display_mode -> swapDisplayMode() | ||||
|             R.id.action_update_library -> { | ||||
| @@ -256,7 +300,7 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback | ||||
|     /** | ||||
|      * Applies filter change | ||||
|      */ | ||||
|     private fun onFilterCheckboxChanged() { | ||||
|     private fun onFilterOrSortChanged() { | ||||
|         presenter.resubscribeLibrary() | ||||
|         activity.supportInvalidateOptionsMenu() | ||||
|     } | ||||
|   | ||||
| @@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.ui.library | ||||
|  | ||||
| import android.os.Bundle | ||||
| import android.util.Pair | ||||
| import eu.kanade.tachiyomi.Constants | ||||
| import eu.kanade.tachiyomi.data.cache.CoverCache | ||||
| import eu.kanade.tachiyomi.data.database.DatabaseHelper | ||||
| import eu.kanade.tachiyomi.data.database.models.Category | ||||
| @@ -133,10 +134,12 @@ class LibraryPresenter : BasePresenter<LibraryFragment>() { | ||||
|      */ | ||||
|     fun getLibraryMangasObservable(): Observable<Map<Int, List<Manga>>> { | ||||
|         return db.getLibraryMangas().asRxObservable() | ||||
|                 .flatMap { mangas -> | ||||
|                     Observable.from(mangas) | ||||
|                 .flatMap { | ||||
|                     Observable.from(it) | ||||
|                             // Filter library by options | ||||
|                             .filter { filterManga(it) } | ||||
|                             .toSortedList { manga1, manga2 -> sortManga(manga1, manga2) } | ||||
|                             .flatMap { Observable.from(it) } | ||||
|                             .groupBy { it.category } | ||||
|                             .flatMap { group -> group.toList().map { Pair(group.key, it) } } | ||||
|                             .toMap({ it.first }, { it.second }) | ||||
| @@ -159,6 +162,33 @@ class LibraryPresenter : BasePresenter<LibraryFragment>() { | ||||
|         start(GET_LIBRARY) | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Compares the two manga determined by sorting mode. | ||||
|      * Returns zero if this object is equal to the specified other object, | ||||
|      * a negative number if it's less than other, or a positive number if it's greater than other. | ||||
|      * | ||||
|      * @param manga1 first manga to compare | ||||
|      * @param manga2 second manga to compare | ||||
|      */ | ||||
|     fun sortManga(manga1: Manga, manga2: Manga): Int { | ||||
|         when (preferences.librarySortingMode().getOrDefault()) { | ||||
|             Constants.SORT_LIBRARY_ALPHA -> return manga1.title.compareTo(manga2.title) | ||||
|             Constants.SORT_LIBRARY_LAST_READ -> { | ||||
|                 var a = 0L | ||||
|                 var b = 0L | ||||
|                 manga1.id?.let { manga1Id -> | ||||
|                     manga2.id?.let { manga2Id -> | ||||
|                         db.getLastHistoryByMangaId(manga1Id).executeAsBlocking()?.let { a = it.last_read } | ||||
|                         db.getLastHistoryByMangaId(manga2Id).executeAsBlocking()?.let { b = it.last_read } | ||||
|                     } | ||||
|                 } | ||||
|                 return b.compareTo(a) | ||||
|             } | ||||
|             Constants.SORT_LIBRARY_LAST_UPDATED -> return manga2.last_update.compareTo(manga1.last_update) | ||||
|             else -> return manga1.title.compareTo(manga2.title) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Filters an entry of the library. | ||||
|      * | ||||
|   | ||||
		Reference in New Issue
	
	Block a user