Browse Source

Added the ability to view the library as a list (#394)

* Added in the ability to view the library as a list

* reverted LibraryAdapter and renamed libraryToggleViewEvent to LibraryToggleViewEvent for consistency

* removed LibraryToggleViewEvent and directly subscribed to option change

* fixed the toggleView subscription

* Made the library list item layout more compliant with material design

* Changed unread text style and removed background
Josh 8 years ago
parent
commit
f21a030cf8

+ 1 - 0
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'

+ 2 - 0
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)
+
 }

+ 2 - 0
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)

+ 12 - 4
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)
+
     }
 
     /**

+ 55 - 0
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.
      */

+ 42 - 0
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<LibraryPresenter>(), 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<LibraryPresenter>(), 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<LibraryPresenter>(), 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<LibraryPresenter>(), 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<LibraryPresenter>(), 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.
      *

+ 49 - 0
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)
+    }
+
+}

+ 6 - 27
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)
 
 }

+ 53 - 0
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)
+    }
+
+}

+ 26 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt

@@ -71,6 +71,12 @@ class LibraryPresenter : BasePresenter<LibraryFragment>() {
      */
     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<LibraryFragment>() {
             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<LibraryFragment>() {
         return false
     }
 
+    /**
+     * Changes the active display mode.
+     */
+    fun swapDisplayMode() {
+        var currentMode: Boolean = displayAsList
+        preferences.libraryAsList().set(!displayAsList)
+    }
+
 }

+ 4 - 6
app/src/main/res/layout/fragment_library.xml

@@ -3,11 +3,9 @@
               android:layout_height="match_parent"
               android:orientation="vertical">
 
-    <android.support.v4.view.ViewPager
-        android:id="@+id/view_pager"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent">
-
-    </android.support.v4.view.ViewPager>
+        <android.support.v4.view.ViewPager
+            android:id="@+id/view_pager"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"/>
 
 </LinearLayout>

+ 19 - 6
app/src/main/res/layout/fragment_library_category.xml

@@ -9,14 +9,27 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent">
 
-        <eu.kanade.tachiyomi.widget.AutofitRecyclerView
-            android:id="@+id/recycler"
-            style="@style/Theme.Widget.GridView"
+        <ViewSwitcher
+            android:id="@+id/library_switcher"
             android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:columnWidth="140dp"
-            tools:listitem="@layout/item_catalogue_grid"/>
+            android:layout_height="0dp"
+            android:layout_weight="1">
 
+            <eu.kanade.tachiyomi.widget.AutofitRecyclerView
+                android:id="@+id/recycler"
+                style="@style/Theme.Widget.GridView"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:columnWidth="140dp"
+                tools:listitem="@layout/item_catalogue_grid"/>
+
+            <android.support.v7.widget.RecyclerView
+                android:id="@+id/library_list"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                tools:listitem="@layout/item_library_list"/>
+
+        </ViewSwitcher>
     </android.support.v4.widget.SwipeRefreshLayout>
 
 </FrameLayout>

+ 35 - 0
app/src/main/res/layout/item_library_list.xml

@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="?android:listPreferredItemHeightSmall"
+    android:background="?attr/selectable_list_drawable">
+
+    <de.hdodenhof.circleimageview.CircleImageView
+        android:id="@+id/thumbnail"
+        android:layout_width="32dp"
+        android:layout_height="32dp"
+        android:src="@drawable/icon"
+        android:layout_gravity="center_vertical|left"
+        android:paddingLeft="6dp"/>
+
+    <TextView
+            android:id="@+id/title"
+            style="@style/TextAppearance.Regular.Body1"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:paddingLeft="38dp"
+            android:text="Sample name"/>
+
+    <TextView
+            android:id="@+id/unread_text"
+            style="@style/TextAppearance.Regular.Caption"
+            android:textColor="@color/material_grey_500"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical|right"
+            android:paddingRight="9dp"
+            android:visibility="gone"/>
+
+</FrameLayout>

+ 5 - 0
app/src/main/res/menu/library.xml

@@ -29,6 +29,11 @@
         app:showAsAction="collapseActionView|ifRoom"
         app:actionViewClass="android.support.v7.widget.SearchView" />
 
+    <item
+        android:id="@+id/action_library_display_mode"
+        android:title="Display Mode"
+        app:showAsAction="always"/>
+
     <item
         android:id="@+id/action_update_library"
         android:title="@string/action_update_library"

+ 1 - 0
app/src/main/res/values/keys.xml

@@ -9,6 +9,7 @@
     <string name="pref_category_about_key">pref_category_about_key</string>
     <string name="pref_category_sources_key">pref_category_sources_key</string>
 
+    <string name="pref_display_library_as_list">pref_display_library_as_list</string>
     <string name="pref_library_columns_dialog_key">pref_library_columns_dialog_key</string>
     <string name="pref_library_columns_portrait_key">pref_library_columns_portrait_key</string>
     <string name="pref_library_columns_landscape_key">pref_library_columns_landscape_key</string>

+ 1 - 1
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"/>
-
+        
         <eu.kanade.tachiyomi.widget.preference.IntListPreference
                 android:title="@string/pref_start_screen"
                 android:key="@string/pref_start_screen_key"