ソースを参照

Information Page Improvements (click to search, copy to clipboard, etc) (#1139)

* adds long click to copy details per inorichi/tachiyomi#1127

* Added the latest update date for inorichi/tachiyomi#1098 and possible fix for inorichi/tachiyomi#1141

* cleanup some mistakes I left

* adds modifications to full name display for inorichi/tachiyomi#1141 and click to search on various information pieces for inorichi/tachiyomi#860

* This modifies how the full title shows up in the info pages and also properly ellipsizes the titles in the catalogue/library list views

* Changes full title layout in horizontal mode

* Adds the tags in using AndroidTagGroup library

* reverting the sdk version in the gradle build

* code cleanup

* added back status update
Josh 7 年 前
コミット
34d21c1de3

+ 1 - 0
app/build.gradle

@@ -202,6 +202,7 @@ dependencies {
     implementation 'me.zhanghai.android.systemuihelper:library:1.0.0'
     implementation 'com.nightlynexus.viewstatepageradapter:viewstatepageradapter:1.0.4'
     implementation 'com.github.mthli:Slice:v1.2'
+    implementation 'me.gujun.android.taggroup:library:1.4@aar'
 
     // Conductor
     implementation "com.bluelinelabs:conductor:2.1.4"

+ 5 - 4
app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueController.kt

@@ -185,10 +185,11 @@ class CatalogueController : NucleusController<CataloguePresenter>(),
         // Create query listener which opens the global search view.
         searchView.queryTextChangeEvents()
                 .filter { it.isSubmitted }
-                .subscribeUntilDestroy {
-                    val query = it.queryText().toString()
-                    router.pushController(CatalogueSearchController(query).withFadeTransaction())
-                }
+                .subscribeUntilDestroy { performGlobalSearch(it.queryText().toString()) }
+    }
+
+    fun performGlobalSearch(query: String){
+        router.pushController(CatalogueSearchController(query).withFadeTransaction())
     }
 
     /**

+ 6 - 4
app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt

@@ -21,10 +21,8 @@ import eu.kanade.tachiyomi.data.database.models.Manga
 import eu.kanade.tachiyomi.data.track.TrackManager
 import eu.kanade.tachiyomi.source.Source
 import eu.kanade.tachiyomi.source.SourceManager
-import eu.kanade.tachiyomi.ui.base.controller.RouterPagerAdapter
-import eu.kanade.tachiyomi.ui.base.controller.RxController
-import eu.kanade.tachiyomi.ui.base.controller.TabbedController
-import eu.kanade.tachiyomi.ui.base.controller.requestPermissionsSafe
+import eu.kanade.tachiyomi.ui.base.controller.*
+import eu.kanade.tachiyomi.ui.catalogue.global_search.CatalogueSearchController
 import eu.kanade.tachiyomi.ui.manga.chapter.ChaptersController
 import eu.kanade.tachiyomi.ui.manga.info.MangaInfoController
 import eu.kanade.tachiyomi.ui.manga.track.TrackController
@@ -34,6 +32,7 @@ import kotlinx.android.synthetic.main.manga_controller.*
 import rx.Subscription
 import uy.kohesive.injekt.Injekt
 import uy.kohesive.injekt.api.get
+import java.util.*
 
 class MangaController : RxController, TabbedController {
 
@@ -63,6 +62,8 @@ class MangaController : RxController, TabbedController {
 
     val fromCatalogue = args.getBoolean(FROM_CATALOGUE_EXTRA, false)
 
+    val lastUpdateRelay: BehaviorRelay<Date> = BehaviorRelay.create()
+
     val chapterCountRelay: BehaviorRelay<Float> = BehaviorRelay.create()
 
     val mangaFavoriteRelay: PublishRelay<Boolean> = PublishRelay.create()
@@ -188,4 +189,5 @@ class MangaController : RxController, TabbedController {
                 .apply { isAccessible = true }
     }
 
+
 }

+ 3 - 1
app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersController.kt

@@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.ui.manga.chapter
 
 import android.animation.Animator
 import android.animation.AnimatorListenerAdapter
+import android.annotation.SuppressLint
 import android.app.Activity
 import android.content.Intent
 import android.support.design.widget.Snackbar
@@ -61,7 +62,7 @@ class ChaptersController : NucleusController<ChaptersPresenter>(),
     override fun createPresenter(): ChaptersPresenter {
         val ctrl = parentController as MangaController
         return ChaptersPresenter(ctrl.manga!!, ctrl.source!!,
-                ctrl.chapterCountRelay, ctrl.mangaFavoriteRelay)
+                ctrl.chapterCountRelay, ctrl.lastUpdateRelay, ctrl.mangaFavoriteRelay)
     }
 
     override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View {
@@ -292,6 +293,7 @@ class ChaptersController : NucleusController<ChaptersPresenter>(),
         return true
     }
 
+    @SuppressLint("StringFormatInvalid")
     override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
         val count = adapter?.selectedItemCount ?: 0
         if (count == 0) {

+ 7 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersPresenter.kt

@@ -20,6 +20,7 @@ import rx.schedulers.Schedulers
 import timber.log.Timber
 import uy.kohesive.injekt.Injekt
 import uy.kohesive.injekt.api.get
+import java.util.*
 
 /**
  * Presenter of [ChaptersController].
@@ -28,6 +29,7 @@ class ChaptersPresenter(
         val manga: Manga,
         val source: Source,
         private val chapterCountRelay: BehaviorRelay<Float>,
+        private val lastUpdateRelay: BehaviorRelay<Date>,
         private val mangaFavoriteRelay: PublishRelay<Boolean>,
         val preferences: PreferencesHelper = Injekt.get(),
         private val db: DatabaseHelper = Injekt.get(),
@@ -91,6 +93,11 @@ class ChaptersPresenter(
                     // Emit the number of chapters to the info tab.
                     chapterCountRelay.call(chapters.maxBy { it.chapter_number }?.chapter_number
                             ?: 0f)
+
+                    // Emit the upload date of the most recent chapter
+                    lastUpdateRelay.call(Date(chapters.maxBy { it.date_upload }?.date_upload
+                            ?: 0))
+
                 }
                 .subscribe { chaptersRelay.call(it) })
     }

+ 118 - 10
app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoController.kt

@@ -2,8 +2,12 @@ package eu.kanade.tachiyomi.ui.manga.info
 
 import android.app.Dialog
 import android.app.PendingIntent
+import android.content.ClipData
+import android.content.ClipboardManager
+import android.content.Context
 import android.content.Intent
 import android.graphics.Bitmap
+import android.graphics.Color
 import android.graphics.drawable.Drawable
 import android.net.Uri
 import android.os.Build
@@ -13,6 +17,7 @@ import android.support.v4.content.pm.ShortcutInfoCompat
 import android.support.v4.content.pm.ShortcutManagerCompat
 import android.support.v4.graphics.drawable.IconCompat
 import android.view.*
+import android.widget.Toast
 import com.afollestad.materialdialogs.MaterialDialog
 import com.bumptech.glide.load.engine.DiskCacheStrategy
 import com.bumptech.glide.load.resource.bitmap.RoundedCorners
@@ -20,6 +25,7 @@ import com.bumptech.glide.request.target.SimpleTarget
 import com.bumptech.glide.request.transition.Transition
 import com.jakewharton.rxbinding.support.v4.widget.refreshes
 import com.jakewharton.rxbinding.view.clicks
+import com.jakewharton.rxbinding.view.longClicks
 import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.data.database.models.Category
 import eu.kanade.tachiyomi.data.database.models.Manga
@@ -31,17 +37,22 @@ import eu.kanade.tachiyomi.source.model.SManga
 import eu.kanade.tachiyomi.source.online.HttpSource
 import eu.kanade.tachiyomi.ui.base.controller.DialogController
 import eu.kanade.tachiyomi.ui.base.controller.NucleusController
+import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
+import eu.kanade.tachiyomi.ui.catalogue.global_search.CatalogueSearchController
 import eu.kanade.tachiyomi.ui.library.ChangeMangaCategoriesDialog
 import eu.kanade.tachiyomi.ui.main.MainActivity
 import eu.kanade.tachiyomi.ui.manga.MangaController
 import eu.kanade.tachiyomi.util.getResourceColor
 import eu.kanade.tachiyomi.util.snack
 import eu.kanade.tachiyomi.util.toast
+import eu.kanade.tachiyomi.util.truncateCenter
 import jp.wasabeef.glide.transformations.CropSquareTransformation
 import jp.wasabeef.glide.transformations.MaskTransformation
 import kotlinx.android.synthetic.main.manga_info_controller.*
 import uy.kohesive.injekt.injectLazy
+import java.text.DateFormat
 import java.text.DecimalFormat
+import java.util.*
 
 /**
  * Fragment that shows manga information.
@@ -64,7 +75,7 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
     override fun createPresenter(): MangaInfoPresenter {
         val ctrl = parentController as MangaController
         return MangaInfoPresenter(ctrl.manga!!, ctrl.source!!,
-                ctrl.chapterCountRelay, ctrl.mangaFavoriteRelay)
+                ctrl.chapterCountRelay, ctrl.lastUpdateRelay, ctrl.mangaFavoriteRelay)
     }
 
     override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View {
@@ -79,6 +90,41 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
 
         // Set SwipeRefresh to refresh manga data.
         swipe_refresh.refreshes().subscribeUntilDestroy { fetchMangaFromSource() }
+
+        manga_full_title.longClicks().subscribeUntilDestroy{
+            copyToClipboard(view.context.getString(R.string.title), manga_full_title.text.toString())
+        }
+
+        manga_full_title.clicks().subscribeUntilDestroy {
+            performGlobalSearch(manga_full_title.text.toString())
+        }
+
+        manga_artist.longClicks().subscribeUntilDestroy {
+            copyToClipboard(manga_artist_label.text.toString(), manga_artist.text.toString())
+        }
+
+        manga_artist.clicks().subscribeUntilDestroy {
+            performGlobalSearch(manga_artist.text.toString())
+        }
+
+        manga_author.longClicks().subscribeUntilDestroy {
+            copyToClipboard(manga_author.text.toString(), manga_author.text.toString())
+        }
+
+        manga_author.clicks().subscribeUntilDestroy {
+            performGlobalSearch(manga_author.text.toString())
+        }
+
+        manga_summary.longClicks().subscribeUntilDestroy {
+            copyToClipboard(view.context.getString(R.string.description), manga_summary.text.toString())
+        }
+
+        manga_genres_tags.setOnTagClickListener { tag -> performGlobalSearch(tag) }
+
+        manga_cover.longClicks().subscribeUntilDestroy {
+            copyToClipboard(view.context.getString(R.string.title), presenter.manga.title)
+        }
+
     }
 
     override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
@@ -107,6 +153,7 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
         if (manga.initialized) {
             // Update view.
             setMangaInfo(manga, source)
+
         } else {
             // Initialize manga.
             fetchMangaFromSource()
@@ -122,19 +169,45 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
     private fun setMangaInfo(manga: Manga, source: Source?) {
         val view = view ?: return
 
+        //update full title TextView.
+        manga_full_title.text = if (manga.title.isBlank()) {
+            view.context.getString(R.string.unknown)
+        } else {
+            manga.title
+        }
+
         // Update artist TextView.
-        manga_artist.text = manga.artist
+        manga_artist.text = if (manga.artist.isNullOrBlank()) {
+            view.context.getString(R.string.unknown)
+        } else {
+            manga.artist
+        }
 
         // Update author TextView.
-        manga_author.text = manga.author
+        manga_author.text = if (manga.author.isNullOrBlank()) {
+            view.context.getString(R.string.unknown)
+        } else {
+            manga.author
+        }
 
         // If manga source is known update source TextView.
-        if (source != null) {
-            manga_source.text = source.toString()
+        manga_source.text = if(source == null) {
+            view.context.getString(R.string.unknown)
+        } else {
+            source.toString()
         }
 
-        // Update genres TextView.
-        manga_genres.text = manga.genre
+        // Update genres list
+        if(manga.genre.isNullOrBlank().not()){
+            manga_genres_tags.setTags(manga.genre?.split(", "))
+        }
+
+        // Update description TextView.
+        manga_summary.text = if (manga.description.isNullOrBlank()) {
+            view.context.getString(R.string.unknown)
+        } else {
+            manga.description
+        }
 
         // Update status TextView.
         manga_status.setText(when (manga.status) {
@@ -144,9 +217,6 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
             else -> R.string.unknown
         })
 
-        // Update description TextView.
-        manga_summary.text = manga.description
-
         // Set the favorite drawable to the correct one.
         setFavoriteDrawable(manga.favorite)
 
@@ -168,6 +238,11 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
         }
     }
 
+    override fun onDestroyView(view: View) {
+        manga_genres_tags.setOnTagClickListener(null)
+        super.onDestroyView(view)
+    }
+
     /**
      * Update chapter count TextView.
      *
@@ -177,6 +252,10 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
         manga_chapters?.text = DecimalFormat("#.#").format(count)
     }
 
+    fun setLastUpdateDate(date: Date){
+        manga_last_update?.text = DateFormat.getDateInstance(DateFormat.SHORT).format(date)
+    }
+
     /**
      * Toggles the favorite status and asks for confirmation to delete downloaded chapters.
      */
@@ -380,6 +459,35 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
                 })
     }
 
+    /**
+     * Copies a string to clipboard
+     *
+     * @param label Label to show to the user describing the content
+     * @param content the actual text to copy to the board
+     */
+    private fun copyToClipboard(label: String, content: String){
+        if(content.isBlank()) return
+
+        val activity = activity ?: return
+        val view = view ?: return
+
+        val clipboard = activity.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
+        clipboard.primaryClip = ClipData.newPlainText(label, content)
+
+        activity.toast( view.context.getString(R.string.copied_to_clipboard, content.truncateCenter(20)),
+                Toast.LENGTH_SHORT)
+    }
+
+    /**
+     * Perform a global search using the provided query.
+     *
+     * @param query the search query to pass to the search controller
+     */
+    fun performGlobalSearch(query: String){
+        val router = parentController?.router ?: return
+        router.pushController(CatalogueSearchController(query).withFadeTransaction())
+    }
+
     /**
      * Create shortcut using ShortcutManager.
      *

+ 9 - 3
app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoPresenter.kt

@@ -18,6 +18,7 @@ import rx.android.schedulers.AndroidSchedulers
 import rx.schedulers.Schedulers
 import uy.kohesive.injekt.Injekt
 import uy.kohesive.injekt.api.get
+import java.util.*
 
 /**
  * Presenter of MangaInfoFragment.
@@ -28,6 +29,7 @@ class MangaInfoPresenter(
         val manga: Manga,
         val source: Source,
         private val chapterCountRelay: BehaviorRelay<Float>,
+        private val lastUpdateRelay: BehaviorRelay<Date>,
         private val mangaFavoriteRelay: PublishRelay<Boolean>,
         private val db: DatabaseHelper = Injekt.get(),
         private val downloadManager: DownloadManager = Injekt.get(),
@@ -37,7 +39,7 @@ class MangaInfoPresenter(
     /**
      * Subscription to send the manga to the view.
      */
-    private var viewMangaSubcription: Subscription? = null
+    private var viewMangaSubscription: Subscription? = null
 
     /**
      * Subscription to update the manga from the source.
@@ -56,14 +58,18 @@ class MangaInfoPresenter(
         mangaFavoriteRelay.observeOn(AndroidSchedulers.mainThread())
                 .subscribe { setFavorite(it) }
                 .apply { add(this) }
+
+        //update last update date
+        lastUpdateRelay.observeOn(AndroidSchedulers.mainThread())
+                .subscribeLatestCache(MangaInfoController::setLastUpdateDate)
     }
 
     /**
      * Sends the active manga to the view.
      */
     fun sendMangaToView() {
-        viewMangaSubcription?.let { remove(it) }
-        viewMangaSubcription = Observable.just(manga)
+        viewMangaSubscription?.let { remove(it) }
+        viewMangaSubscription = Observable.just(manga)
                 .subscribeLatestCache({ view, manga -> view.onNextManga(manga, source) })
     }
 

+ 15 - 0
app/src/main/java/eu/kanade/tachiyomi/util/StringExtensions.kt

@@ -1,5 +1,7 @@
 package eu.kanade.tachiyomi.util
 
+import java.lang.Math.floor
+
 /**
  * Replaces the given string to have at most [count] characters using [replacement] at its end.
  * If [replacement] is longer than [count] an exception will be thrown when `length > count`.
@@ -11,3 +13,16 @@ fun String.chop(count: Int, replacement: String = "..."): String {
         this
 
 }
+
+/**
+ * Replaces the given string to have at most [count] characters using [replacement] near the center.
+ * If [replacement] is longer than [count] an exception will be thrown when `length > count`.
+ */
+fun String.truncateCenter(count: Int, replacement: String = "..."): String{
+    if(length <= count)
+        return this
+
+    val pieceLength:Int = floor((count - replacement.length).div(2.0)).toInt()
+
+    return "${ take(pieceLength) }$replacement${ takeLast(pieceLength) }"
+}

+ 52 - 20
app/src/main/res/layout-land/manga_info_controller.xml

@@ -59,6 +59,18 @@
                 android:layout_width="match_parent"
                 android:layout_height="match_parent">
 
+                <TextView
+                    android:text="@string/manga_info_full_title_label"
+                    android:id="@+id/manga_full_title"
+                    style="@style/TextAppearance.Medium.Title"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:ellipsize="end"
+                    android:maxLines="2"
+                    android:textIsSelectable="false"
+                    app:layout_constraintTop_toTopOf="parent"
+                    app:layout_constraintLeft_toLeftOf="parent"/>
+
                 <TextView
                     android:id="@+id/manga_author_label"
                     style="@style/TextAppearance.Medium.Body2"
@@ -66,7 +78,7 @@
                     android:layout_height="wrap_content"
                     android:text="@string/manga_info_author_label"
                     android:textIsSelectable="false"
-                    app:layout_constraintTop_toTopOf="parent"
+                    app:layout_constraintTop_toBottomOf="@+id/manga_full_title"
                     app:layout_constraintLeft_toLeftOf="parent"/>
 
                 <TextView
@@ -132,17 +144,17 @@
                     android:layout_marginStart="8dp"/>
 
                 <TextView
-                    android:id="@+id/manga_status_label"
+                    android:id="@+id/manga_last_update_label"
                     style="@style/TextAppearance.Medium.Body2"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
-                    android:text="@string/manga_info_status_label"
+                    android:text="@string/manga_info_latest_data_label"
                     android:textIsSelectable="false"
                     app:layout_constraintTop_toBottomOf="@+id/manga_chapters_label"
                     app:layout_constraintLeft_toLeftOf="parent"/>
 
                 <TextView
-                    android:id="@+id/manga_status"
+                    android:id="@+id/manga_last_update"
                     style="@style/TextAppearance.Regular.Body1.Secondary"
                     android:layout_width="0dp"
                     android:layout_height="wrap_content"
@@ -150,23 +162,23 @@
                     android:ellipsize="end"
                     android:maxLines="1"
                     android:textIsSelectable="false"
-                    app:layout_constraintBaseline_toBaselineOf="@+id/manga_status_label"
-                    app:layout_constraintLeft_toRightOf="@+id/manga_status_label"
+                    app:layout_constraintBaseline_toBaselineOf="@+id/manga_last_update_label"
+                    app:layout_constraintLeft_toRightOf="@+id/manga_last_update_label"
                     app:layout_constraintRight_toRightOf="parent"
                     android:layout_marginStart="8dp"/>
 
                 <TextView
-                    android:id="@+id/manga_source_label"
+                    android:id="@+id/manga_status_label"
                     style="@style/TextAppearance.Medium.Body2"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
-                    android:text="@string/manga_info_source_label"
+                    android:text="@string/manga_info_status_label"
                     android:textIsSelectable="false"
-                    app:layout_constraintTop_toBottomOf="@+id/manga_status_label"
+                    app:layout_constraintTop_toBottomOf="@+id/manga_last_update_label"
                     app:layout_constraintLeft_toLeftOf="parent"/>
 
                 <TextView
-                    android:id="@+id/manga_source"
+                    android:id="@+id/manga_status"
                     style="@style/TextAppearance.Regular.Body1.Secondary"
                     android:layout_width="0dp"
                     android:layout_height="wrap_content"
@@ -174,30 +186,34 @@
                     android:ellipsize="end"
                     android:maxLines="1"
                     android:textIsSelectable="false"
-                    app:layout_constraintBaseline_toBaselineOf="@+id/manga_source_label"
-                    app:layout_constraintLeft_toRightOf="@+id/manga_source_label"
+                    app:layout_constraintBaseline_toBaselineOf="@+id/manga_status_label"
+                    app:layout_constraintLeft_toRightOf="@+id/manga_status_label"
                     app:layout_constraintRight_toRightOf="parent"
                     android:layout_marginStart="8dp"/>
 
                 <TextView
-                    android:id="@+id/manga_genres_label"
+                    android:id="@+id/manga_source_label"
                     style="@style/TextAppearance.Medium.Body2"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
-                    android:text="@string/manga_info_genres_label"
+                    android:text="@string/manga_info_source_label"
                     android:textIsSelectable="false"
-                    app:layout_constraintTop_toBottomOf="@+id/manga_source_label"
+                    app:layout_constraintTop_toBottomOf="@+id/manga_status_label"
                     app:layout_constraintLeft_toLeftOf="parent"/>
 
                 <TextView
-                    android:id="@+id/manga_genres"
+                    android:id="@+id/manga_source"
                     style="@style/TextAppearance.Regular.Body1.Secondary"
                     android:layout_width="0dp"
                     android:layout_height="wrap_content"
+                    android:layout_marginLeft="8dp"
+                    android:ellipsize="end"
+                    android:maxLines="1"
                     android:textIsSelectable="false"
-                    app:layout_constraintTop_toBottomOf="@+id/manga_genres_label"
-                    app:layout_constraintLeft_toLeftOf="parent"
-                    app:layout_constraintRight_toRightOf="parent"/>
+                    app:layout_constraintBaseline_toBaselineOf="@+id/manga_source_label"
+                    app:layout_constraintLeft_toRightOf="@+id/manga_source_label"
+                    app:layout_constraintRight_toRightOf="parent"
+                    android:layout_marginStart="8dp"/>
 
                 <TextView
                     android:id="@+id/manga_summary_label"
@@ -206,7 +222,7 @@
                     android:layout_height="wrap_content"
                     android:text="@string/description"
                     android:textIsSelectable="false"
-                    app:layout_constraintTop_toBottomOf="@+id/manga_genres"
+                    app:layout_constraintTop_toBottomOf="@+id/manga_source"
                     app:layout_constraintLeft_toLeftOf="parent"/>
 
                 <TextView
@@ -220,6 +236,22 @@
                     app:layout_constraintRight_toRightOf="parent"
                     android:layout_marginRight="64dp"/>
 
+                <me.gujun.android.taggroup.TagGroup
+                    android:id="@+id/manga_genres_tags"
+                    style="@style/TagGroup"
+                    android:layout_marginTop="8dp"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    app:layout_constraintTop_toBottomOf="@+id/manga_summary"
+                    app:layout_constraintLeft_toLeftOf="parent"
+                    app:layout_constraintRight_toRightOf="parent"
+                    app:atg_borderStrokeWidth="1dp"
+                    app:atg_backgroundColor="@android:color/transparent"
+                    app:atg_borderColor="@color/md_blue_A400"
+                    app:atg_textColor="@color/md_blue_A400"
+                    android:layout_marginRight="64dp"/>
+
+
             </android.support.constraint.ConstraintLayout>
 
         </android.support.v4.widget.NestedScrollView>

+ 26 - 23
app/src/main/res/layout/catalogue_list_item.xml

@@ -21,10 +21,11 @@
        android:paddingTop="8dp"
        android:paddingBottom="8dp"
        android:layout_marginLeft="8dp"/>
+
     <TextView
         android:id="@+id/title"
         style="@style/TextAppearance.Regular.SubHeading"
-        android:layout_width="wrap_content"
+        android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:ellipsize="end"
         android:maxLines="1"
@@ -41,6 +42,28 @@
         app:layout_constraintVertical_bias="0.523"
         app:layout_constraintHorizontal_bias="0.007"/>
 
+    <TextView
+        android:id="@+id/local_text"
+        style="@style/TextAppearance.Regular.Caption.Light"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:background="@color/md_teal_500"
+        android:paddingBottom="1dp"
+        android:paddingLeft="3dp"
+        android:paddingRight="3dp"
+        android:paddingTop="1dp"
+        android:layout_centerVertical="true"
+        android:maxLines="1"
+        android:text="@string/local_source_badge"
+        android:visibility="gone"
+        tools:visibility="visible"
+        android:layout_marginEnd="8dp"
+        app:layout_constraintRight_toLeftOf="@+id/unread_text"
+        app:layout_constraintTop_toTopOf="parent"
+        android:layout_marginTop="8dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        android:layout_marginBottom="8dp"/>
+
         <TextView
             android:id="@+id/unread_text"
             style="@style/TextAppearance.Regular.Caption.Light"
@@ -58,6 +81,7 @@
             tools:text="130"
             tools:visibility="visible"
             app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintRight_toLeftOf="@+id/download_text"
             android:layout_marginTop="8dp"
             app:layout_constraintBottom_toBottomOf="parent"
             android:layout_marginBottom="8dp"
@@ -81,32 +105,11 @@
             tools:text="122"
             tools:visibility="visible"
             android:layout_marginEnd="8dp"
-            app:layout_constraintRight_toLeftOf="@+id/unread_text"
+            app:layout_constraintRight_toLeftOf="parent"
             app:layout_constraintTop_toTopOf="parent"
             android:layout_marginTop="8dp"
             app:layout_constraintBottom_toBottomOf="parent"
             android:layout_marginBottom="8dp"/>
-    <TextView
-        android:id="@+id/local_text"
-        style="@style/TextAppearance.Regular.Caption.Light"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:background="@color/md_teal_500"
-        android:paddingBottom="1dp"
-        android:paddingLeft="3dp"
-        android:paddingRight="3dp"
-        android:paddingTop="1dp"
-        android:layout_centerVertical="true"
-        android:maxLines="1"
-        android:text="@string/local_source_badge"
-        android:visibility="gone"
-        tools:visibility="visible"
-        android:layout_marginEnd="8dp"
-        app:layout_constraintRight_toLeftOf="@+id/download_text"
-        app:layout_constraintTop_toTopOf="parent"
-        android:layout_marginTop="8dp"
-        app:layout_constraintBottom_toBottomOf="parent"
-        android:layout_marginBottom="8dp"/>
 
     </android.support.constraint.ConstraintLayout>
 

+ 4 - 4
app/src/main/res/layout/chapters_item.xml

@@ -41,10 +41,10 @@
         android:layout_height="wrap_content"
         tools:text="22/02/2016"
         android:ellipsize="marquee"
-        android:maxLines="1"
         app:layout_constraintLeft_toLeftOf="parent"
         app:layout_constraintBottom_toBottomOf="parent"
-        android:layout_marginLeft="16dp"/>
+        android:layout_marginLeft="16dp"
+        android:singleLine="true" />
 
     <TextView
         android:id="@+id/chapter_pages"
@@ -53,10 +53,10 @@
         android:layout_height="wrap_content"
         tools:text="Pages: 45"
         android:ellipsize="marquee"
-        android:maxLines="1"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintRight_toRightOf="parent"
-        app:layout_constraintLeft_toLeftOf="parent"/>
+        app:layout_constraintLeft_toLeftOf="parent"
+        android:singleLine="true" />
 
     <ImageView
         android:id="@+id/chapter_menu"

+ 59 - 22
app/src/main/res/layout/manga_info_controller.xml

@@ -83,6 +83,16 @@
                 android:layout_width="match_parent"
                 android:layout_height="match_parent">
 
+                <TextView
+                    android:text="@string/manga_info_full_title_label"
+                    android:id="@+id/manga_full_title"
+                    style="@style/TextAppearance.Medium.Title"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:maxLines="2"
+                    android:textIsSelectable="false"
+                    app:layout_constraintTop_toTopOf="parent"
+                    app:layout_constraintLeft_toLeftOf="parent"/>
                 <TextView
                     android:id="@+id/manga_author_label"
                     style="@style/TextAppearance.Medium.Body2"
@@ -90,9 +100,11 @@
                     android:layout_height="wrap_content"
                     android:text="@string/manga_info_author_label"
                     android:textIsSelectable="false"
-                    app:layout_constraintTop_toTopOf="parent"
+
+                    app:layout_constraintTop_toBottomOf="@+id/manga_full_title"
                     app:layout_constraintLeft_toLeftOf="parent"/>
 
+
                 <TextView
                     android:id="@+id/manga_author"
                     style="@style/TextAppearance.Regular.Body1.Secondary"
@@ -153,17 +165,17 @@
                     app:layout_constraintRight_toRightOf="parent"/>
 
                 <TextView
-                    android:id="@+id/manga_status_label"
+                    android:id="@+id/manga_last_update_label"
                     style="@style/TextAppearance.Medium.Body2"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
-                    android:text="@string/manga_info_status_label"
+                    android:text="@string/manga_info_latest_data_label"
                     android:textIsSelectable="false"
                     app:layout_constraintTop_toBottomOf="@+id/manga_chapters_label"
                     app:layout_constraintLeft_toLeftOf="parent"/>
 
                 <TextView
-                    android:id="@+id/manga_status"
+                    android:id="@+id/manga_last_update"
                     style="@style/TextAppearance.Regular.Body1.Secondary"
                     android:layout_width="0dp"
                     android:layout_height="wrap_content"
@@ -171,22 +183,22 @@
                     android:ellipsize="end"
                     android:maxLines="1"
                     android:textIsSelectable="false"
-                    app:layout_constraintBaseline_toBaselineOf="@+id/manga_status_label"
-                    app:layout_constraintLeft_toRightOf="@+id/manga_status_label"
+                    app:layout_constraintBaseline_toBaselineOf="@+id/manga_last_update_label"
+                    app:layout_constraintLeft_toRightOf="@+id/manga_last_update_label"
                     app:layout_constraintRight_toRightOf="parent"/>
 
                 <TextView
-                    android:id="@+id/manga_source_label"
+                    android:id="@+id/manga_status_label"
                     style="@style/TextAppearance.Medium.Body2"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
-                    android:text="@string/manga_info_source_label"
+                    android:text="@string/manga_info_status_label"
                     android:textIsSelectable="false"
-                    app:layout_constraintTop_toBottomOf="@+id/manga_status_label"
+                    app:layout_constraintTop_toBottomOf="@+id/manga_last_update_label"
                     app:layout_constraintLeft_toLeftOf="parent"/>
 
                 <TextView
-                    android:id="@+id/manga_source"
+                    android:id="@+id/manga_status"
                     style="@style/TextAppearance.Regular.Body1.Secondary"
                     android:layout_width="0dp"
                     android:layout_height="wrap_content"
@@ -194,28 +206,31 @@
                     android:ellipsize="end"
                     android:maxLines="1"
                     android:textIsSelectable="false"
-                    app:layout_constraintBaseline_toBaselineOf="@+id/manga_source_label"
-                    app:layout_constraintLeft_toRightOf="@+id/manga_source_label"
+                    app:layout_constraintBaseline_toBaselineOf="@+id/manga_status_label"
+                    app:layout_constraintLeft_toRightOf="@+id/manga_status_label"
                     app:layout_constraintRight_toRightOf="parent"/>
 
                 <TextView
-                    android:id="@+id/manga_genres_label"
+                    android:id="@+id/manga_source_label"
                     style="@style/TextAppearance.Medium.Body2"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
-                    android:text="@string/manga_info_genres_label"
+                    android:text="@string/manga_info_source_label"
                     android:textIsSelectable="false"
-                    app:layout_constraintTop_toBottomOf="@+id/manga_source_label"
+                    app:layout_constraintTop_toBottomOf="@+id/manga_status_label"
                     app:layout_constraintLeft_toLeftOf="parent"/>
 
                 <TextView
-                    android:id="@+id/manga_genres"
+                    android:id="@+id/manga_source"
                     style="@style/TextAppearance.Regular.Body1.Secondary"
                     android:layout_width="0dp"
                     android:layout_height="wrap_content"
+                    android:layout_marginLeft="8dp"
+                    android:ellipsize="end"
+                    android:maxLines="1"
                     android:textIsSelectable="false"
-                    app:layout_constraintTop_toBottomOf="@+id/manga_genres_label"
-                    app:layout_constraintLeft_toLeftOf="parent"
+                    app:layout_constraintBaseline_toBaselineOf="@+id/manga_source_label"
+                    app:layout_constraintLeft_toRightOf="@+id/manga_source_label"
                     app:layout_constraintRight_toRightOf="parent"/>
 
             </android.support.constraint.ConstraintLayout>
@@ -225,17 +240,16 @@
         <android.support.v4.widget.NestedScrollView
             android:id="@+id/description_scrollview"
             android:layout_width="0dp"
-            android:layout_height="0dp"
+            android:layout_height="wrap_content"
             android:layout_marginBottom="16dp"
             android:layout_marginEnd="8dp"
             android:layout_marginLeft="16dp"
             android:layout_marginRight="16dp"
             android:layout_marginStart="8dp"
-            android:layout_marginTop="16dp"
-            app:layout_constraintBottom_toBottomOf="parent"
+            android:layout_marginTop="8dp"
             app:layout_constraintLeft_toLeftOf="parent"
             app:layout_constraintRight_toRightOf="parent"
-            app:layout_constraintTop_toTopOf="@+id/guideline">
+            app:layout_constraintTop_toBottomOf="@+id/guideline">
 
             <LinearLayout
                 android:layout_width="match_parent"
@@ -261,6 +275,29 @@
 
         </android.support.v4.widget.NestedScrollView>
 
+        <me.gujun.android.taggroup.TagGroup
+            android:id="@+id/manga_genres_tags"
+            style="@style/TagGroup"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_marginBottom="16dp"
+            android:layout_marginEnd="8dp"
+            android:layout_marginLeft="16dp"
+            android:layout_marginRight="16dp"
+            android:layout_marginStart="8dp"
+            android:layout_marginTop="16dp"
+            android:orientation="vertical"
+            app:layout_constraintLeft_toLeftOf="parent"
+            app:layout_constraintRight_toRightOf="parent"
+            app:layout_constraintTop_toBottomOf="@+id/description_scrollview"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:atg_borderStrokeWidth="1dp"
+            app:atg_backgroundColor="@android:color/transparent"
+            app:atg_borderColor="@color/md_blue_A400"
+            app:atg_textColor="@color/md_blue_A400"
+            />
+
+
     </android.support.constraint.ConstraintLayout>
 
 </android.support.v4.widget.SwipeRefreshLayout>

+ 4 - 0
app/src/main/res/values/strings.xml

@@ -300,12 +300,14 @@
     <string name="unknown">Unknown</string>
     <string name="licensed">Licensed</string>
     <string name="remove_from_library">Remove from library</string>
+    <string name="manga_info_full_title_label">Title</string>
     <string name="manga_added_library">Added to library</string>
     <string name="manga_removed_library">Removed from library</string>
     <string name="manga_info_author_label">Author</string>
     <string name="manga_info_artist_label">Artist</string>
     <string name="manga_info_chapters_label">Chapters</string>
     <string name="manga_info_last_chapter_label">Last chapter</string>
+    <string name="manga_info_latest_data_label">Updated</string>
     <string name="manga_info_status_label">Status</string>
     <string name="manga_info_source_label">Source</string>
     <string name="manga_info_genres_label">Genres</string>
@@ -319,6 +321,7 @@
     <string name="icon_shape">Icon shape</string>
     <string name="icon_creation_fail">Failed to create shortcut!</string>
     <string name="delete_downloads_for_manga">Delete downloaded chapters?</string>
+    <string name="copied_to_clipboard">%1$s copied to clipboard</string>
 
     <!-- Manga chapters fragment -->
     <string name="manga_chapters_tab">Chapters</string>
@@ -457,4 +460,5 @@
     <string name="channel_common">Common</string>
     <string name="channel_library">Library</string>
     <string name="channel_downloader">Downloader</string>
+
 </resources>