diff --git a/app/build.gradle b/app/build.gradle index 6c06d311f0..23a51cbc4d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -164,6 +164,7 @@ dependencies { compile 'net.xpece.android:support-preference:0.8.1' compile 'me.zhanghai.android.systemuihelper:library:1.0.0' compile 'org.adw.library:discrete-seekbar:1.0.1' + compile 'de.hdodenhof:circleimageview:2.1.0' // Tests testCompile 'junit:junit:4.12' diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt index 28b6fdc09e..667a1eb90f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt @@ -92,4 +92,6 @@ class PreferenceKeys(context: Context) { fun syncPassword(syncId: Int) = "pref_mangasync_password_$syncId" + val libraryAsList = context.getString(R.string.pref_display_library_as_list) + } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt index 93a0c8bfd3..6ebbdbceac 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt @@ -126,6 +126,8 @@ class PreferencesHelper(context: Context) { fun libraryUpdateRestriction() = prefs.getStringSet(keys.libraryUpdateRestriction, emptySet()) + fun libraryAsList() = rxPrefs.getBoolean(keys.libraryAsList, false) + fun filterDownloaded() = rxPrefs.getBoolean(keys.filterDownloaded, false) fun filterUnread() = rxPrefs.getBoolean(keys.filterUnread, false) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryAdapter.kt index c16e86af31..5320e5be98 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryAdapter.kt @@ -84,11 +84,19 @@ class LibraryCategoryAdapter(val fragment: LibraryCategoryFragment) : * @return a new view holder for a manga. */ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LibraryHolder { - val view = parent.inflate(R.layout.item_catalogue_grid).apply { - card.layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, coverHeight) - gradient.layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, coverHeight / 2, Gravity.BOTTOM) + //depending on preferences, display a list or display a grid + if(parent.id == R.id.library_list) { + val view = parent.inflate(R.layout.item_library_list) + return LibraryListHolder(view, this, fragment) + + } else { + val view = parent.inflate(R.layout.item_catalogue_grid).apply { + card.layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, coverHeight) + gradient.layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, coverHeight / 2, Gravity.BOTTOM) + } + return LibraryGridHolder(view, this, fragment) } - return LibraryHolder(view, this, fragment) + } /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryFragment.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryFragment.kt index 7cf99f8073..1085df828e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryFragment.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryFragment.kt @@ -7,17 +7,22 @@ import android.support.v7.widget.RecyclerView import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.view.animation.AnimationUtils import com.f2prateek.rx.preferences.Preference import eu.davidea.flexibleadapter.FlexibleAdapter import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.library.LibraryUpdateService +import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder import eu.kanade.tachiyomi.ui.base.fragment.BaseFragment import eu.kanade.tachiyomi.ui.manga.MangaActivity import eu.kanade.tachiyomi.util.toast +import kotlinx.android.synthetic.main.fragment_catalogue.* +import kotlinx.android.synthetic.main.fragment_library.* import kotlinx.android.synthetic.main.fragment_library_category.* import rx.Subscription +import uy.kohesive.injekt.injectLazy /** * Fragment containing the library manga for a certain category. @@ -25,6 +30,11 @@ import rx.Subscription */ class LibraryCategoryFragment : BaseFragment(), FlexibleViewHolder.OnListItemClickListener { + /** + * Preferences. + */ + val preferences: PreferencesHelper by injectLazy() + /** * Adapter to hold the manga in this category. */ @@ -46,11 +56,21 @@ class LibraryCategoryFragment : BaseFragment(), FlexibleViewHolder.OnListItemCli */ private var numColumnsSubscription: Subscription? = null + /** + * subscription to view toggle + */ + private var toggleViewSubscription: Subscription? = null + /** * Subscription of the library search. */ private var searchSubscription: Subscription? = null + /** + * display mode + */ + private var displayAsList: Boolean = false; + companion object { /** * Key to save and restore [position] from a [Bundle]. @@ -66,19 +86,29 @@ class LibraryCategoryFragment : BaseFragment(), FlexibleViewHolder.OnListItemCli fun newInstance(position: Int): LibraryCategoryFragment { val fragment = LibraryCategoryFragment() fragment.position = position + return fragment } } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedState: Bundle?): View? { return inflater.inflate(R.layout.fragment_library_category, container, false) + + } override fun onViewCreated(view: View, savedState: Bundle?) { adapter = LibraryCategoryAdapter(this) + + //set up grid recycler.setHasFixedSize(true) recycler.adapter = adapter + //set up list + library_list.setHasFixedSize(true) + library_list.adapter = adapter + library_list.layoutManager = LinearLayoutManager(activity) + if (libraryFragment.actionMode != null) { setSelectionMode(FlexibleAdapter.MODE_MULTI) } @@ -94,6 +124,17 @@ class LibraryCategoryFragment : BaseFragment(), FlexibleViewHolder.OnListItemCli adapter.updateDataSet() } + toggleViewSubscription = preferences.libraryAsList().asObservable().subscribe {onViewModeChange(it)} + + if(libraryPresenter.displayAsList != displayAsList) { + library_switcher.showNext() + displayAsList = libraryPresenter.displayAsList + } + + + library_switcher.inAnimation = AnimationUtils.loadAnimation(activity, android.R.anim.fade_in) + library_switcher.outAnimation = AnimationUtils.loadAnimation(activity, android.R.anim.fade_out) + if (savedState != null) { position = savedState.getInt(POSITION_KEY) adapter.onRestoreInstanceState(savedState) @@ -129,13 +170,17 @@ class LibraryCategoryFragment : BaseFragment(), FlexibleViewHolder.OnListItemCli override fun onDestroyView() { numColumnsSubscription?.unsubscribe() searchSubscription?.unsubscribe() + toggleViewSubscription?.unsubscribe() super.onDestroyView() } override fun onResume() { super.onResume() + + libraryMangaSubscription = libraryPresenter.libraryMangaSubject .subscribe { onNextLibraryManga(it) } + } override fun onPause() { @@ -211,6 +256,7 @@ class LibraryCategoryFragment : BaseFragment(), FlexibleViewHolder.OnListItemCli startActivity(intent) } + /** * Toggles the selection for a manga. * @@ -262,6 +308,15 @@ class LibraryCategoryFragment : BaseFragment(), FlexibleViewHolder.OnListItemCli } } + fun onViewModeChange(isList: Boolean) { + //do nothing if the display does not need to change + if(isList == displayAsList) return + + //else change view and display mode + library_switcher.showNext() + displayAsList = isList + } + /** * Property to get the library fragment. */ diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryFragment.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryFragment.kt index 5f14d4eecf..9e455a0296 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryFragment.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryFragment.kt @@ -14,6 +14,7 @@ import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.models.Category import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.library.LibraryUpdateService +import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.ui.base.fragment.BaseRxFragment import eu.kanade.tachiyomi.ui.category.CategoryActivity @@ -22,6 +23,7 @@ import eu.kanade.tachiyomi.util.toast import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.fragment_library.* import nucleus.factory.RequiresPresenter +import uy.kohesive.injekt.injectLazy import java.io.IOException /** @@ -37,6 +39,11 @@ class LibraryFragment : BaseRxFragment(), ActionMode.Callback lateinit var adapter: LibraryAdapter private set + /** + * Preferences. + */ + val preferences: PreferencesHelper by injectLazy() + /** * TabLayout of the categories. */ @@ -53,6 +60,11 @@ class LibraryFragment : BaseRxFragment(), ActionMode.Callback */ private var query: String? = null + /** + * Display mode of the library (list or grid mode). + */ + private var displayMode: MenuItem? = null + /** * Action mode for manga selection. */ @@ -178,6 +190,18 @@ class LibraryFragment : BaseRxFragment(), ActionMode.Callback return true } }) + + //set the icon for the display mode button + displayMode = menu.findItem(R.id.action_library_display_mode).apply { + val icon = if (preferences.libraryAsList().getOrDefault()) + R.drawable.ic_view_module_white_24dp + else + R.drawable.ic_view_list_white_24dp + + setIcon(icon) + } + + } override fun onOptionsItemSelected(item: MenuItem): Boolean { @@ -208,6 +232,7 @@ class LibraryFragment : BaseRxFragment(), ActionMode.Callback // Apply filter onFilterCheckboxChanged() } + R.id.action_library_display_mode -> swapDisplayMode() R.id.action_update_library -> { LibraryUpdateService.start(activity, true) } @@ -231,6 +256,23 @@ class LibraryFragment : BaseRxFragment(), ActionMode.Callback activity.supportInvalidateOptionsMenu() } + /** + * swap display mode + */ + private fun swapDisplayMode() { + + presenter.swapDisplayMode() + val isListMode = presenter.displayAsList + val icon = if (isListMode) + R.drawable.ic_view_module_white_24dp + else + R.drawable.ic_view_list_white_24dp + + displayMode?.setIcon(icon) + + } + + /** * Updates the query. * diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryGridHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryGridHolder.kt new file mode 100644 index 0000000000..91424f3bd6 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryGridHolder.kt @@ -0,0 +1,49 @@ +package eu.kanade.tachiyomi.ui.library + +import android.view.View +import com.bumptech.glide.Glide +import com.bumptech.glide.load.engine.DiskCacheStrategy +import eu.kanade.tachiyomi.data.database.models.Manga +import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder +import kotlinx.android.synthetic.main.item_catalogue_grid.view.* + +/** + * Class used to hold the displayed data of a manga in the library, like the cover or the title. + * All the elements from the layout file "item_catalogue_grid" are available in this class. + * + * @param view the inflated view for this holder. + * @param adapter the adapter handling this holder. + * @param listener a listener to react to single tap and long tap events. + * @constructor creates a new library holder. + */ +class LibraryGridHolder(private val view: View, + private val adapter: LibraryCategoryAdapter, + listener: FlexibleViewHolder.OnListItemClickListener) +: LibraryHolder(view, adapter, listener) { + + /** + * Method called from [LibraryCategoryAdapter.onBindViewHolder]. It updates the data for this + * holder with the given manga. + * + * @param manga the manga to bind. + */ + override fun onSetValues(manga: Manga) { + // Update the title of the manga. + view.title.text = manga.title + + // Update the unread count and its visibility. + with(view.unread_text) { + visibility = if (manga.unread > 0) View.VISIBLE else View.GONE + text = manga.unread.toString() + } + + // Update the cover. + Glide.clear(view.thumbnail) + Glide.with(view.context) + .load(manga) + .diskCacheStrategy(DiskCacheStrategy.RESULT) + .centerCrop() + .into(view.thumbnail) + } + +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryHolder.kt index 9cf28ae46a..07f5add56c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryHolder.kt @@ -8,18 +8,14 @@ import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder import kotlinx.android.synthetic.main.item_catalogue_grid.view.* /** - * Class used to hold the displayed data of a manga in the library, like the cover or the title. - * All the elements from the layout file "item_catalogue_grid" are available in this class. - * + * Generic class used to hold the displayed data of a manga in the library. * @param view the inflated view for this holder. * @param adapter the adapter handling this holder. - * @param listener a listener to react to single tap and long tap events. - * @constructor creates a new library holder. + * @param listener a listener to react to the single tap and long tap events. */ -class LibraryHolder(private val view: View, - private val adapter: LibraryCategoryAdapter, - listener: FlexibleViewHolder.OnListItemClickListener) -: FlexibleViewHolder(view, adapter, listener) { + +abstract class LibraryHolder(private val view: View, adapter: LibraryCategoryAdapter, listener: FlexibleViewHolder.OnListItemClickListener) + : FlexibleViewHolder(view, adapter, listener) { /** * Method called from [LibraryCategoryAdapter.onBindViewHolder]. It updates the data for this @@ -27,23 +23,6 @@ class LibraryHolder(private val view: View, * * @param manga the manga to bind. */ - fun onSetValues(manga: Manga) { - // Update the title of the manga. - view.title.text = manga.title - - // Update the unread count and its visibility. - with(view.unread_text) { - visibility = if (manga.unread > 0) View.VISIBLE else View.GONE - text = manga.unread.toString() - } - - // Update the cover. - Glide.clear(view.thumbnail) - Glide.with(view.context) - .load(manga) - .diskCacheStrategy(DiskCacheStrategy.RESULT) - .centerCrop() - .into(view.thumbnail) - } + abstract fun onSetValues(manga: Manga) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryListHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryListHolder.kt new file mode 100644 index 0000000000..78d9accee6 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryListHolder.kt @@ -0,0 +1,53 @@ +package eu.kanade.tachiyomi.ui.library + +import android.view.View +import com.bumptech.glide.Glide +import com.bumptech.glide.load.engine.DiskCacheStrategy +import eu.kanade.tachiyomi.data.database.models.Manga +import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder +import kotlinx.android.synthetic.main.item_library_list.view.* + +/** + * Class used to hold the displayed data of a manga in the library, like the cover or the title. + * All the elements from the layout file "item_library_list" are available in this class. + * + * @param view the inflated view for this holder. + * @param adapter the adapter handling this holder. + * @param listener a listener to react to single tap and long tap events. + * @constructor creates a new library holder. + */ + +class LibraryListHolder(private val view: View, + private val adapter: LibraryCategoryAdapter, + listener: FlexibleViewHolder.OnListItemClickListener) +: LibraryHolder(view, adapter, listener) { + + /** + * Method called from [LibraryCategoryAdapter.onBindViewHolder]. It updates the data for this + * holder with the given manga. + * + * @param manga the manga to bind. + */ + override fun onSetValues(manga: Manga) { + // Update the title of the manga. + view.title.text = manga.title + + // Update the unread count and its visibility. + with(view.unread_text) { + visibility = if (manga.unread > 0) View.VISIBLE else View.GONE + text = manga.unread.toString() + } + + + + // Update the cover. + Glide.clear(view.thumbnail) + Glide.with(view.context) + .load(manga) + .diskCacheStrategy(DiskCacheStrategy.RESULT) + .centerCrop() + .dontAnimate() + .into(view.thumbnail) + } + +} \ No newline at end of file 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 83c8682c81..26650cd04c 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 @@ -71,6 +71,12 @@ class LibraryPresenter : BasePresenter() { */ val downloadManager: DownloadManager by injectLazy() + /** + * display the library as a list? + */ + var displayAsList: Boolean = false + private set + companion object { /** * Id of the restartable that listens for library updates. @@ -89,6 +95,18 @@ class LibraryPresenter : BasePresenter() { start(GET_LIBRARY) } + + add(preferences.libraryAsList().asObservable().subscribe{setDisplayMode(it)}) + + } + + /** + * Sets the display mode + * + * @param asList display as list or not + */ + fun setDisplayMode(asList: Boolean) { + displayAsList = asList } /** @@ -285,4 +303,12 @@ class LibraryPresenter : BasePresenter() { return false } + /** + * Changes the active display mode. + */ + fun swapDisplayMode() { + var currentMode: Boolean = displayAsList + preferences.libraryAsList().set(!displayAsList) + } + } diff --git a/app/src/main/res/layout/fragment_library.xml b/app/src/main/res/layout/fragment_library.xml index f603e3d3ed..2edae65d22 100644 --- a/app/src/main/res/layout/fragment_library.xml +++ b/app/src/main/res/layout/fragment_library.xml @@ -3,11 +3,9 @@ android:layout_height="match_parent" android:orientation="vertical"> - - - + diff --git a/app/src/main/res/layout/fragment_library_category.xml b/app/src/main/res/layout/fragment_library_category.xml index 46801572ad..77fd57c33c 100644 --- a/app/src/main/res/layout/fragment_library_category.xml +++ b/app/src/main/res/layout/fragment_library_category.xml @@ -9,14 +9,27 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - + android:layout_height="0dp" + android:layout_weight="1"> + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_library_list.xml b/app/src/main/res/layout/item_library_list.xml new file mode 100644 index 0000000000..7142650f4a --- /dev/null +++ b/app/src/main/res/layout/item_library_list.xml @@ -0,0 +1,35 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/library.xml b/app/src/main/res/menu/library.xml index bb669a95df..0af3d8b000 100644 --- a/app/src/main/res/menu/library.xml +++ b/app/src/main/res/menu/library.xml @@ -29,6 +29,11 @@ app:showAsAction="collapseActionView|ifRoom" app:actionViewClass="android.support.v7.widget.SearchView" /> + + pref_category_about_key pref_category_sources_key + pref_display_library_as_list pref_library_columns_dialog_key pref_library_columns_portrait_key pref_library_columns_landscape_key diff --git a/app/src/main/res/xml/pref_general.xml b/app/src/main/res/xml/pref_general.xml index 77e5fda3ef..350b551a8f 100644 --- a/app/src/main/res/xml/pref_general.xml +++ b/app/src/main/res/xml/pref_general.xml @@ -13,7 +13,7 @@ android:key="@string/pref_theme_key" android:summary="%s" android:title="@string/pref_theme"/> - +