Tracking sheet and search adjustments (#5427)

* Tracking sheet and search visual adjustments

* Remove track item divider

* Add start margin to "add tracking" button

* Fix track search dialog crash when no item chosen

* Show "remove" action only when track item is previously set

* Remove placeholder for total chapters

* Cleanups

* Add track search error/empty result message

* Make track search dialog fullscreen

* Use AutofitRecyclerView for track search dialog

* Fix text input overlapping

* Run track search from IME action instead

* Remove deprecated method

* Reformat

* Set track search error message on the placeholder

* Use payload to notify track search item change

* Fix track search action icon tint color
This commit is contained in:
Ivan Iskandar 2021-06-28 22:33:26 +07:00 committed by GitHub
parent 7e3ea9074c
commit cb71d44024
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 662 additions and 434 deletions

View File

@ -1118,8 +1118,7 @@ class MangaController :
fun onTrackingSearchResultsError(error: Throwable) {
Timber.e(error)
activity?.toast(error.message)
getTrackingSearchDialog()?.onSearchResultsError()
getTrackingSearchDialog()?.onSearchResultsError(error.message)
}
private fun getTrackingSearchDialog(): TrackSearchDialog? {

View File

@ -4,6 +4,8 @@ import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import eu.kanade.tachiyomi.databinding.TrackItemBinding
import eu.kanade.tachiyomi.util.view.applyElevationOverlay
import uy.kohesive.injekt.api.get
class TrackAdapter(listener: OnClickListener) : RecyclerView.Adapter<TrackHolder>() {
@ -29,6 +31,7 @@ class TrackAdapter(listener: OnClickListener) : RecyclerView.Adapter<TrackHolder
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TrackHolder {
binding = TrackItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
binding.card.applyElevationOverlay()
return TrackHolder(binding, this)
}

View File

@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.ui.manga.track
import android.annotation.SuppressLint
import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.databinding.TrackItemBinding
import uy.kohesive.injekt.injectLazy
@ -37,38 +38,64 @@ class TrackHolder(private val binding: TrackItemBinding, adapter: TrackAdapter)
fun bind(item: TrackItem) {
val track = item.track
binding.trackLogo.setImageResource(item.service.getLogo())
binding.logoContainer.setBackgroundColor(item.service.getLogoColor())
binding.logoContainer.setCardBackgroundColor(item.service.getLogoColor())
binding.trackSet.isVisible = track == null
binding.trackTitle.isVisible = track != null
binding.topDivider.isVisible = track != null
binding.middleRow.isVisible = track != null
binding.bottomDivider.isVisible = track != null
binding.bottomRow.isVisible = track != null
binding.card.isVisible = track != null
if (track != null) {
val ctx = binding.trackTitle.context
binding.trackTitle.text = track.title
binding.trackChapters.text = "${track.last_chapter_read}/" +
if (track.total_chapters > 0) track.total_chapters else "-"
binding.trackChapters.text = track.last_chapter_read.toString()
if (track.total_chapters > 0) {
binding.trackChapters.text = "${binding.trackChapters.text} / ${track.total_chapters}"
}
binding.trackStatus.text = item.service.getStatus(track.status)
if (item.service.getScoreList().isEmpty()) {
binding.trackScore.isVisible = false
binding.vertDivider2.isVisible = false
val supportsScoring = item.service.getScoreList().isNotEmpty()
if (supportsScoring) {
if (track.score != 0F) {
item.service.getScoreList()
binding.trackScore.text = item.service.displayScore(track)
binding.trackScore.alpha = SET_STATUS_TEXT_ALPHA
} else {
binding.trackScore.text = if (track.score == 0f) "-" else item.service.displayScore(track)
binding.trackScore.text = ctx.getString(R.string.score)
binding.trackScore.alpha = UNSET_STATUS_TEXT_ALPHA
}
}
binding.trackScore.isVisible = supportsScoring
binding.vertDivider2.isVisible = supportsScoring
val supportsReadingDates = item.service.supportsReadingDates
if (supportsReadingDates) {
if (track.started_reading_date != 0L) {
binding.trackStartDate.text = dateFormat.format(track.started_reading_date)
binding.trackStartDate.alpha = SET_STATUS_TEXT_ALPHA
} else {
binding.trackStartDate.text = ctx.getString(R.string.track_started_reading_date)
binding.trackStartDate.alpha = UNSET_STATUS_TEXT_ALPHA
}
if (track.finished_reading_date != 0L) {
binding.trackFinishDate.text = dateFormat.format(track.finished_reading_date)
binding.trackFinishDate.alpha = SET_STATUS_TEXT_ALPHA
} else {
binding.trackFinishDate.text = ctx.getString(R.string.track_finished_reading_date)
binding.trackFinishDate.alpha = UNSET_STATUS_TEXT_ALPHA
}
}
binding.bottomDivider.isVisible = supportsReadingDates
binding.bottomRow.isVisible = supportsReadingDates
}
}
if (item.service.supportsReadingDates) {
binding.trackStartDate.text =
if (track.started_reading_date != 0L) dateFormat.format(track.started_reading_date) else "-"
binding.trackFinishDate.text =
if (track.finished_reading_date != 0L) dateFormat.format(track.finished_reading_date) else "-"
} else {
binding.bottomDivider.isVisible = false
binding.bottomRow.isVisible = false
}
}
companion object {
private const val SET_STATUS_TEXT_ALPHA = 1F
private const val UNSET_STATUS_TEXT_ALPHA = 0.5F
}
}

View File

@ -1,76 +1,57 @@
package eu.kanade.tachiyomi.ui.manga.track
import android.content.Context
import android.view.View
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.ArrayAdapter
import androidx.core.view.isVisible
import coil.clear
import coil.load
import eu.kanade.tachiyomi.R
import androidx.recyclerview.widget.RecyclerView
import eu.kanade.tachiyomi.data.track.model.TrackSearch
import eu.kanade.tachiyomi.databinding.TrackSearchItemBinding
import eu.kanade.tachiyomi.util.view.inflate
import eu.kanade.tachiyomi.util.view.applyElevationOverlay
class TrackSearchAdapter(context: Context) :
ArrayAdapter<TrackSearch>(context, R.layout.track_search_item, mutableListOf<TrackSearch>()) {
override fun getView(position: Int, view: View?, parent: ViewGroup): View {
var v = view
// Get the data item for this position
val track = getItem(position)!!
// Check if an existing view is being reused, otherwise inflate the view
val holder: TrackSearchHolder // view lookup cache stored in tag
if (v == null) {
v = parent.inflate(R.layout.track_search_item)
holder = TrackSearchHolder(v)
v.tag = holder
} else {
holder = v.tag as TrackSearchHolder
class TrackSearchAdapter(
private val currentTrackUrl: String?,
private val onSelectionChanged: (TrackSearch?) -> Unit
) : RecyclerView.Adapter<TrackSearchHolder>() {
var selectedItemPosition = -1
set(value) {
if (field != value) {
val previousPosition = field
field = value
// Just notify the now-unselected item
notifyItemChanged(previousPosition, UncheckPayload)
onSelectionChanged(items.getOrNull(value))
}
holder.onSetValues(track)
return v
}
fun setItems(syncs: List<TrackSearch>) {
setNotifyOnChange(false)
clear()
addAll(syncs)
var items = emptyList<TrackSearch>()
set(value) {
if (field != value) {
field = value
selectedItemPosition = value.indexOfFirst { it.tracking_url == currentTrackUrl }
notifyDataSetChanged()
}
class TrackSearchHolder(private val view: View) {
private val binding = TrackSearchItemBinding.bind(view)
fun onSetValues(track: TrackSearch) {
binding.trackSearchTitle.text = track.title
binding.trackSearchSummary.text = track.summary
binding.trackSearchCover.clear()
if (track.cover_url.isNotEmpty()) {
binding.trackSearchCover.load(track.cover_url)
}
val hasStatus = track.publishing_status.isNotBlank()
binding.trackSearchStatus.isVisible = hasStatus
binding.trackSearchStatusResult.isVisible = hasStatus
if (hasStatus) {
binding.trackSearchStatusResult.text = track.publishing_status.capitalize()
override fun getItemCount(): Int = items.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TrackSearchHolder {
val binding = TrackSearchItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
binding.container.applyElevationOverlay()
return TrackSearchHolder(binding, this)
}
val hasType = track.publishing_type.isNotBlank()
binding.trackSearchType.isVisible = hasType
binding.trackSearchTypeResult.isVisible = hasType
if (hasType) {
binding.trackSearchTypeResult.text = track.publishing_type.capitalize()
override fun onBindViewHolder(holder: TrackSearchHolder, position: Int) {
holder.bind(items[position], position)
}
val hasStartDate = track.start_date.isNotBlank()
binding.trackSearchStart.isVisible = hasStartDate
binding.trackSearchStartResult.isVisible = hasStartDate
if (hasStartDate) {
binding.trackSearchStartResult.text = track.start_date
override fun onBindViewHolder(holder: TrackSearchHolder, position: Int, payloads: MutableList<Any>) {
if (payloads.getOrNull(0) == UncheckPayload) {
holder.setUnchecked()
} else {
super.onBindViewHolder(holder, position, payloads)
}
}
companion object {
private object UncheckPayload
}
}

View File

@ -2,29 +2,31 @@ package eu.kanade.tachiyomi.ui.manga.track
import android.app.Dialog
import android.os.Bundle
import android.view.KeyEvent
import android.view.LayoutInflater
import android.view.View
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import androidx.appcompat.app.AppCompatDialog
import androidx.core.content.getSystemService
import androidx.core.os.bundleOf
import androidx.core.view.WindowCompat
import androidx.core.view.isVisible
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.customview.customView
import dev.chrisbanes.insetter.applyInsetter
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.data.track.model.TrackSearch
import eu.kanade.tachiyomi.databinding.TrackSearchDialogBinding
import eu.kanade.tachiyomi.ui.base.controller.DialogController
import eu.kanade.tachiyomi.ui.manga.MangaController
import kotlinx.coroutines.flow.debounce
import eu.kanade.tachiyomi.util.view.setNavigationBarTransparentCompat
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import reactivecircus.flowbinding.android.widget.itemClicks
import reactivecircus.flowbinding.android.widget.textChanges
import reactivecircus.flowbinding.android.widget.editorActionEvents
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.util.concurrent.TimeUnit
class TrackSearchDialog : DialogController {
@ -32,59 +34,130 @@ class TrackSearchDialog : DialogController {
private var adapter: TrackSearchAdapter? = null
private var selectedItem: Track? = null
private val service: TrackService
private val currentTrackUrl: String?
private val trackController
get() = targetController as MangaController
constructor(target: MangaController, service: TrackService) : super(
bundleOf(KEY_SERVICE to service.id)
) {
private lateinit var currentlySearched: String
constructor(
target: MangaController,
_service: TrackService,
_currentTrackUrl: String?
) : super(bundleOf(KEY_SERVICE to _service.id, KEY_CURRENT_URL to _currentTrackUrl)) {
targetController = target
this.service = service
service = _service
currentTrackUrl = _currentTrackUrl
}
@Suppress("unused")
constructor(bundle: Bundle) : super(bundle) {
service = Injekt.get<TrackManager>().getService(bundle.getInt(KEY_SERVICE))!!
currentTrackUrl = bundle.getString(KEY_CURRENT_URL)
}
@Suppress("DEPRECATION")
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
binding = TrackSearchDialogBinding.inflate(LayoutInflater.from(activity!!))
val dialog = MaterialDialog(activity!!)
.customView(view = binding!!.root)
.positiveButton(android.R.string.ok) { onPositiveButtonClick() }
.negativeButton(android.R.string.cancel)
.neutralButton(R.string.action_remove) { onRemoveButtonClick() }
onViewCreated(dialog.view, savedViewState)
return dialog
// Toolbar stuff
binding!!.toolbar.setNavigationOnClickListener { dialog?.dismiss() }
binding!!.toolbar.setOnMenuItemClickListener {
when (it.itemId) {
R.id.done -> {
val adapter = adapter ?: return@setOnMenuItemClickListener true
val item = adapter.items.getOrNull(adapter.selectedItemPosition)
if (item != null) {
trackController.presenter.registerTracking(item, service)
dialog?.dismiss()
}
}
R.id.remove -> {
trackController.presenter.unregisterTracking(service)
dialog?.dismiss()
}
}
true
}
binding!!.toolbar.menu.findItem(R.id.remove).isVisible = currentTrackUrl != null
fun onViewCreated(view: View, savedState: Bundle?) {
// Create adapter
val adapter = TrackSearchAdapter(view.context)
this.adapter = adapter
binding!!.trackSearchList.adapter = adapter
// Set listeners
selectedItem = null
binding!!.trackSearchList.itemClicks()
.onEach { position ->
selectedItem = adapter.getItem(position)
adapter = TrackSearchAdapter(currentTrackUrl) { which ->
binding!!.toolbar.menu.findItem(R.id.done).isEnabled = which != null
}
.launchIn(trackController.viewScope)
binding!!.trackSearchRecyclerview.adapter = adapter
// Do an initial search based on the manga's title
if (savedState == null) {
val title = trackController.presenter.manga.title
binding!!.trackSearch.append(title)
search(title)
if (savedViewState == null) {
currentlySearched = trackController.presenter.manga.title
binding!!.titleInput.editText?.append(currentlySearched)
}
search(currentlySearched)
// Input listener
binding?.titleInput?.editText
?.editorActionEvents {
when (it.actionId) {
EditorInfo.IME_ACTION_SEARCH -> {
true
}
else -> {
it.keyEvent?.action == KeyEvent.ACTION_DOWN && it.keyEvent?.keyCode == KeyEvent.KEYCODE_ENTER
}
}
}
?.filter { it.view.text.isNotBlank() }
?.onEach {
val query = it.view.text.toString()
if (query != currentlySearched) {
currentlySearched = query
search(it.view.text.toString())
it.view.context.getSystemService<InputMethodManager>()?.hideSoftInputFromWindow(it.view.windowToken, 0)
it.view.clearFocus()
}
}
?.launchIn(trackController.viewScope)
// Edge to edge
binding!!.appbar.applyInsetter {
type(navigationBars = true, statusBars = true) {
padding(left = true, top = true, right = true)
}
}
binding!!.titleInput.applyInsetter {
type(navigationBars = true) {
margin(horizontal = true)
}
}
binding!!.progress.applyInsetter {
type(navigationBars = true) {
margin()
}
}
binding!!.message.applyInsetter {
type(navigationBars = true) {
margin()
}
}
binding!!.trackSearchRecyclerview.applyInsetter {
type(navigationBars = true) {
padding(vertical = true)
margin(horizontal = true)
}
}
return AppCompatDialog(activity!!, R.style.ThemeOverlay_Tachiyomi_Dialog_Fullscreen).apply {
setContentView(binding!!.root)
}
}
override fun onAttach(view: View) {
super.onAttach(view)
dialog?.window?.let { window ->
window.setNavigationBarTransparentCompat(window.context)
WindowCompat.setDecorFitsSystemWindows(window, false)
}
}
@ -94,46 +167,39 @@ class TrackSearchDialog : DialogController {
adapter = null
}
override fun onAttach(view: View) {
super.onAttach(view)
binding!!.trackSearch.textChanges()
.debounce(TimeUnit.SECONDS.toMillis(1))
.filter { it.isNotBlank() }
.onEach { search(it.toString()) }
.launchIn(trackController.viewScope)
}
private fun search(query: String) {
val binding = binding ?: return
binding.progress.isVisible = true
binding.trackSearchList.isVisible = false
binding.trackSearchRecyclerview.isVisible = false
binding.message.isVisible = false
trackController.presenter.trackingSearch(query, service)
}
fun onSearchResults(results: List<TrackSearch>) {
selectedItem = null
val binding = binding ?: return
binding.progress.isVisible = false
binding.trackSearchList.isVisible = true
adapter?.setItems(results)
val emptyResult = results.isEmpty()
adapter?.items = results
binding.trackSearchRecyclerview.isVisible = !emptyResult
binding.trackSearchRecyclerview.scrollToPosition(0)
binding.message.isVisible = emptyResult
if (emptyResult) {
binding.message.text = binding.message.context.getString(R.string.no_results_found)
}
}
fun onSearchResultsError() {
fun onSearchResultsError(message: String?) {
val binding = binding ?: return
binding.progress.isVisible = false
binding.trackSearchList.isVisible = false
adapter?.setItems(emptyList())
}
private fun onPositiveButtonClick() {
trackController.presenter.registerTracking(selectedItem, service)
}
private fun onRemoveButtonClick() {
trackController.presenter.unregisterTracking(service)
binding.trackSearchRecyclerview.isVisible = false
binding.message.isVisible = true
binding.message.text = message ?: binding.message.context.getString(R.string.unknown_error)
adapter?.items = emptyList()
}
private companion object {
const val KEY_SERVICE = "service_id"
const val KEY_CURRENT_URL = "current_url"
}
}

View File

@ -0,0 +1,61 @@
package eu.kanade.tachiyomi.ui.manga.track
import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import coil.clear
import coil.load
import eu.kanade.tachiyomi.data.track.model.TrackSearch
import eu.kanade.tachiyomi.databinding.TrackSearchItemBinding
import eu.kanade.tachiyomi.util.view.setMaxLinesAndEllipsize
import java.util.Locale
class TrackSearchHolder(
private val binding: TrackSearchItemBinding,
private val adapter: TrackSearchAdapter
) : RecyclerView.ViewHolder(binding.root) {
fun bind(track: TrackSearch, position: Int) {
binding.container.isChecked = position == adapter.selectedItemPosition
binding.container.setOnClickListener {
adapter.selectedItemPosition = position
binding.container.isChecked = true
}
binding.trackSearchTitle.text = track.title
binding.trackSearchCover.clear()
if (track.cover_url.isNotEmpty()) {
binding.trackSearchCover.load(track.cover_url)
}
val hasStatus = track.publishing_status.isNotBlank()
binding.trackSearchStatus.isVisible = hasStatus
binding.trackSearchStatusResult.isVisible = hasStatus
if (hasStatus) {
binding.trackSearchStatusResult.text = track.publishing_status.lowercase().replaceFirstChar {
it.titlecase(Locale.getDefault())
}
}
val hasType = track.publishing_type.isNotBlank()
binding.trackSearchType.isVisible = hasType
binding.trackSearchTypeResult.isVisible = hasType
if (hasType) {
binding.trackSearchTypeResult.text = track.publishing_type.lowercase().replaceFirstChar {
it.titlecase(Locale.getDefault())
}
}
val hasStartDate = track.start_date.isNotBlank()
binding.trackSearchStart.isVisible = hasStartDate
binding.trackSearchStartResult.isVisible = hasStartDate
if (hasStartDate) {
binding.trackSearchStartResult.text = track.start_date
}
binding.trackSearchSummary.setMaxLinesAndEllipsize()
binding.trackSearchSummary.text = track.summary
}
fun setUnchecked() {
binding.container.isChecked = false
}
}

View File

@ -96,7 +96,8 @@ class TrackSheet(
}
}
} else {
TrackSearchDialog(controller, item.service).showDialog(controller.router, TAG_SEARCH_CONTROLLER)
TrackSearchDialog(controller, item.service, item.track?.tracking_url)
.showDialog(controller.router, TAG_SEARCH_CONTROLLER)
}
}

View File

@ -38,6 +38,7 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.util.lang.truncateCenter
import timber.log.Timber
import uy.kohesive.injekt.api.get
import java.io.File
import kotlin.math.roundToInt

View File

@ -4,10 +4,12 @@ package eu.kanade.tachiyomi.util.view
import android.annotation.SuppressLint
import android.graphics.Point
import android.text.TextUtils
import android.view.Gravity
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.TextView
import androidx.annotation.MenuRes
import androidx.annotation.StringRes
import androidx.appcompat.view.menu.MenuBuilder
@ -16,12 +18,17 @@ import androidx.appcompat.widget.TooltipCompat
import androidx.core.content.ContextCompat
import androidx.core.view.forEach
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.card.MaterialCardView
import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipGroup
import com.google.android.material.elevation.ElevationOverlayProvider
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
import com.google.android.material.snackbar.Snackbar
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.util.system.getResourceColor
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
/**
* Returns coordinates of view.
@ -174,3 +181,21 @@ inline fun ChipGroup.setChips(
addView(chip)
}
}
/**
* Applies elevation overlay to a MaterialCardView
*/
inline fun MaterialCardView.applyElevationOverlay() {
if (Injekt.get<PreferencesHelper>().isDarkMode()) {
val provider = ElevationOverlayProvider(context)
setCardBackgroundColor(provider.compositeOverlay(cardBackgroundColor.defaultColor, cardElevation))
}
}
/**
* Sets TextView max lines dynamically. Can only be called when the view is already laid out.
*/
inline fun TextView.setMaxLinesAndEllipsize(_ellipsize: TextUtils.TruncateAt = TextUtils.TruncateAt.END) = post {
maxLines = (measuredHeight - paddingTop - paddingBottom) / lineHeight
ellipsize = _ellipsize
}

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="100"
android:fromAlpha="0.0"
android:interpolator="@android:interpolator/linear"
android:toAlpha="1.0" />

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="100"
android:fromAlpha="1.0"
android:interpolator="@android:interpolator/linear"
android:toAlpha="0.0" />

View File

@ -5,6 +5,5 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:paddingVertical="8dp"
tools:listitem="@layout/track_item" />

View File

@ -1,47 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/track"
style="@style/Widget.Tachiyomi.CardView.Item"
android:padding="0dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false"
android:orientation="vertical"
android:paddingHorizontal="16dp"
android:paddingVertical="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<LinearLayout
<com.google.android.material.card.MaterialCardView
android:id="@+id/logo_container"
android:layout_width="48dp"
android:layout_height="match_parent"
android:layout_height="48dp"
android:clickable="true"
android:focusable="true"
android:gravity="center"
android:orientation="horizontal"
android:padding="4dp"
tools:background="#2E51A2">
android:foreground="?attr/selectableItemBackground"
app:cardBackgroundColor="#2E51A2"
app:cardElevation="0dp"
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.MaterialCardView.Tracker">
<ImageView
android:id="@+id/track_logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:importantForAccessibility="no"
android:padding="4dp"
tools:src="@drawable/ic_tracker_mal" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
android:orientation="horizontal">
<Button
android:id="@+id/track_set"
@ -49,29 +48,39 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="16dp"
android:text="@string/add_tracking"
android:visibility="gone" />
<TextView
android:id="@+id/track_title"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/list_item_selector"
android:layout_height="48dp"
android:ellipsize="end"
android:foreground="?attr/selectableItemBackgroundBorderless"
android:gravity="center_vertical"
android:maxLines="1"
android:padding="16dp"
android:paddingHorizontal="16dp"
android:textAppearance="?attr/textAppearanceSubtitle1"
tools:text="Title" />
</LinearLayout>
</LinearLayout>
<View
android:id="@+id/top_divider"
<com.google.android.material.card.MaterialCardView
android:id="@+id/card"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?android:divider" />
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.MaterialCardView.Tracker">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingHorizontal="12dp"
android:paddingVertical="8dp">
<LinearLayout
android:id="@+id/middle_row"
@ -80,58 +89,53 @@
<TextView
android:id="@+id/track_status"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@drawable/list_item_selector"
android:layout_weight="1"
android:ellipsize="end"
android:foreground="?attr/selectableItemBackgroundBorderless"
android:gravity="center"
android:maxLines="1"
android:padding="16dp"
android:padding="12dp"
android:textAppearance="?attr/textAppearanceBody2"
tools:text="Reading" />
<View
android:id="@+id/vert_divider_1"
android:layout_width="1dp"
android:layout_height="match_parent"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:background="?android:divider"
app:layout_constraintTop_toTopOf="parent" />
android:background="?android:divider" />
<TextView
android:id="@+id/track_chapters"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@drawable/list_item_selector"
android:layout_weight="1"
android:ellipsize="end"
android:foreground="?attr/selectableItemBackgroundBorderless"
android:gravity="center"
android:maxLines="1"
android:padding="16dp"
android:padding="12dp"
android:textAppearance="?attr/textAppearanceBody2"
tools:text="12/24" />
<View
android:id="@+id/vert_divider_2"
android:layout_width="1dp"
android:layout_height="match_parent"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:background="?android:divider"/>
<TextView
android:id="@+id/track_score"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@drawable/list_item_selector"
android:layout_weight="1"
android:ellipsize="end"
android:foreground="?attr/selectableItemBackgroundBorderless"
android:gravity="center"
android:maxLines="1"
android:padding="16dp"
android:padding="12dp"
android:textAppearance="?attr/textAppearanceBody2"
tools:text="10" />
</LinearLayout>
@ -140,6 +144,7 @@
android:id="@+id/bottom_divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?android:divider" />
<LinearLayout
@ -149,44 +154,41 @@
<TextView
android:id="@+id/track_start_date"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@drawable/list_item_selector"
android:layout_weight="1"
android:ellipsize="end"
android:foreground="?attr/selectableItemBackgroundBorderless"
android:gravity="center"
android:maxLines="1"
android:padding="16dp"
android:padding="12dp"
android:textAppearance="?attr/textAppearanceBody2"
tools:text="4/16/2020" />
<View
android:id="@+id/vert_divider_3"
android:layout_width="1dp"
android:layout_height="match_parent"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:background="?android:divider"
app:layout_constraintBottom_toBottomOf="parent" />
android:background="?android:divider" />
<TextView
android:id="@+id/track_finish_date"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@drawable/list_item_selector"
android:layout_weight="1"
android:ellipsize="end"
android:foreground="?attr/selectableItemBackgroundBorderless"
android:gravity="center"
android:maxLines="1"
android:padding="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/vert_divider_3"
app:layout_constraintTop_toBottomOf="@+id/bottom_divider"
android:padding="12dp"
android:textAppearance="?attr/textAppearanceBody2"
tools:text="4/16/2020" />
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
</com.google.android.material.card.MaterialCardView>
</LinearLayout>

View File

@ -1,26 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:theme="?attr/actionBarTheme"
app:contentInsetStartWithNavigation="0dp"
app:menu="@menu/track_search"
app:navigationIcon="@drawable/ic_close_24dp"
app:title="@string/add_tracking" />
</com.google.android.material.appbar.AppBarLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
android:orientation="vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/title_input"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.Dense"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
app:boxBackgroundMode="filled"
app:endIconMode="clear_text"
app:hintEnabled="false">
android:layout_marginHorizontal="12dp"
android:layout_marginTop="8dp"
android:hint="@string/title">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/track_search"
android:id="@+id/title_input_edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/title"
android:inputType="text" />
android:imeOptions="actionSearch"
android:inputType="text"
android:maxLines="1" />
</com.google.android.material.textfield.TextInputLayout>
@ -34,29 +58,33 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="32dp"
android:layout_marginBottom="32dp"
android:indeterminate="true"
android:visibility="invisible"
tools:visibility="visible" />
android:visibility="gone" />
<ListView
android:id="@+id/track_search_list"
style="@style/Widget.Tachiyomi.CardView"
<TextView
android:id="@+id/message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textAppearance="?attr/textAppearanceBody2"
android:visibility="gone"
tools:text="@string/no_results_found" />
<eu.kanade.tachiyomi.widget.AutofitRecyclerView
android:id="@+id/track_search_recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:choiceMode="singleChoice"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:clipToPadding="false"
android:divider="@null"
android:dividerHeight="10dp"
android:footerDividersEnabled="true"
android:headerDividersEnabled="true"
android:listSelector="@drawable/list_item_selector"
android:scrollbars="none"
android:visibility="invisible"
android:columnWidth="330dp"
android:paddingHorizontal="8dp"
android:paddingBottom="8dp"
android:visibility="gone"
tools:listitem="@layout/track_search_item"
tools:visibility="visible" />
</FrameLayout>
</LinearLayout>
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -1,65 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
style="@style/Widget.Tachiyomi.CardView.Item"
android:layout_margin="0dp"
android:padding="0dp">
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:checkable="true"
android:clickable="true"
android:focusable="true"
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.MaterialCardView.Tracker">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="216dp"
android:background="@drawable/list_item_selector"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/track_search_cover"
android:layout_width="135dp"
android:layout_height="match_parent"
android:layout_width="130dp"
android:layout_height="180dp"
android:contentDescription="@string/description_cover"
android:scaleType="centerCrop"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0"
tools:src="@mipmap/ic_launcher" />
<TextView
android:id="@+id/track_search_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:maxLines="3"
android:textAppearance="@style/TextAppearance.Regular.Body1.Bold"
android:textSize="16sp"
android:layout_marginEnd="36dp"
android:ellipsize="end"
android:maxLines="2"
android:textAppearance="?attr/textAppearanceHeadline6"
android:textSize="17sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/track_search_cover"
app:layout_constraintTop_toTopOf="parent"
tools:text="One Piece" />
tools:text="@string/app_name" />
<TextView
android:id="@+id/track_search_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:maxLines="1"
android:text="@string/track_type"
android:textAppearance="@style/TextAppearance.Regular.Body1.Bold"
android:textSize="12sp"
app:layout_constraintStart_toEndOf="@id/track_search_cover"
android:textAppearance="?attr/textAppearanceSubtitle2"
app:layout_constraintStart_toStartOf="@+id/track_search_title"
app:layout_constraintTop_toBottomOf="@id/track_search_title" />
<TextView
android:id="@+id/track_search_type_result"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginStart="4dp"
android:layout_marginEnd="12dp"
android:maxLines="1"
android:textAppearance="@style/TextAppearance.Regular.Body1.Secondary"
android:textSize="12sp"
android:textAppearance="?attr/textAppearanceBody2"
android:textColor="?android:attr/textColorSecondary"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/track_search_type"
app:layout_constraintTop_toBottomOf="@id/track_search_title"
tools:text="Manga" />
@ -68,22 +72,23 @@
android:id="@+id/track_search_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:maxLines="1"
android:text="@string/track_start_date"
android:textAppearance="@style/TextAppearance.Regular.Body1.Bold"
android:textSize="12sp"
app:layout_constraintStart_toEndOf="@id/track_search_cover"
android:textAppearance="?attr/textAppearanceSubtitle2"
app:layout_constraintStart_toStartOf="@+id/track_search_type"
app:layout_constraintTop_toBottomOf="@id/track_search_type" />
<TextView
android:id="@+id/track_search_start_result"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginStart="4dp"
android:layout_marginEnd="12dp"
android:ellipsize="end"
android:maxLines="1"
android:textAppearance="@style/TextAppearance.Regular.Body1.Secondary"
android:textSize="12sp"
android:textAppearance="?attr/textAppearanceBody2"
android:textColor="?android:attr/textColorSecondary"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/track_search_start"
app:layout_constraintTop_toBottomOf="@id/track_search_type"
tools:text="2018-10-01" />
@ -92,22 +97,24 @@
android:id="@+id/track_search_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:ellipsize="end"
android:maxLines="1"
android:text="@string/track_status"
android:textAppearance="@style/TextAppearance.Regular.Body1.Bold"
android:textSize="12sp"
app:layout_constraintStart_toEndOf="@id/track_search_cover"
android:textAppearance="?attr/textAppearanceSubtitle2"
app:layout_constraintStart_toStartOf="@+id/track_search_start"
app:layout_constraintTop_toBottomOf="@id/track_search_start" />
<TextView
android:id="@+id/track_search_status_result"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginStart="8dp"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginEnd="12dp"
android:ellipsize="end"
android:maxLines="1"
android:textAppearance="@style/TextAppearance.Regular.Body1.Secondary"
android:textSize="12sp"
android:textAppearance="?attr/textAppearanceBody2"
android:textColor="?android:attr/textColorSecondary"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/track_search_status"
app:layout_constraintTop_toBottomOf="@id/track_search_start"
tools:text="Ongoing" />
@ -116,22 +123,17 @@
android:id="@+id/track_search_summary"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
android:layout_marginEnd="12dp"
android:layout_marginBottom="8dp"
android:ellipsize="end"
android:maxLines="7"
android:textAppearance="@style/TextAppearance.Regular.Body1.Secondary"
android:textSize="12sp"
android:textAppearance="?attr/textAppearanceCaption"
android:textColor="?android:attr/textColorSecondary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@id/track_search_cover"
app:layout_constraintStart_toStartOf="@+id/track_search_status"
app:layout_constraintTop_toBottomOf="@+id/track_search_status"
app:layout_constraintVertical_bias="0.333"
tools:text="This is the summary of the manga that fits This is the summary of the manga that fits This is the summary of the manga that fits This is the summary of the manga that fits This is the summary of the manga that fits This is the summary of the manga that fits This is the summary of the manga that fits " />
tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas condimentum et turpis ut sollicitudin. Donec tellus dolor, rhoncus a mattis eget, tempor quis augue. Fusce eleifend dignissim turpis a molestie. Praesent tincidunt, risus sed egestas fringilla, urna orci ultrices libero, id iaculis sem lorem placerat lacus." />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</com.google.android.material.card.MaterialCardView>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/remove"
android:icon="@drawable/ic_delete_24dp"
android:title="@string/action_remove"
android:visible="false"
app:iconTint="?attr/colorOnToolbar"
app:showAsAction="ifRoom" />
<item
android:id="@+id/done"
android:enabled="false"
android:icon="@drawable/ic_check_24dp"
android:title="@android:string/ok"
app:iconTint="?attr/colorOnToolbar"
app:showAsAction="ifRoom" />
</menu>

View File

@ -605,8 +605,8 @@
<string name="status">Status</string>
<string name="track_status">Status</string>
<string name="track_start_date">Started</string>
<string name="track_started_reading_date">Started reading date</string>
<string name="track_finished_reading_date">Finished reading date</string>
<string name="track_started_reading_date">Start date</string>
<string name="track_finished_reading_date">Finish date</string>
<string name="track_type">Type</string>
<string name="track_author">Author</string>
<string name="error_invalid_date_supplied">Invalid date supplied</string>

View File

@ -148,24 +148,6 @@
<!--Widgets-->
<!--=======-->
<style name="Widget.Tachiyomi.CardView" parent="CardView">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:padding">16dp</item>
<item name="android:layout_marginTop">8dp</item>
<item name="android:layout_marginBottom">8dp</item>
<item name="android:layout_marginStart">8dp</item>
<item name="android:layout_marginEnd">8dp</item>
<item name="cardBackgroundColor">?attr/colorSurface</item>
<item name="cardCornerRadius">@dimen/card_radius</item>
<item name="cardElevation">2dp</item>
</style>
<style name="Widget.Tachiyomi.CardView.Item">
<item name="android:layout_marginTop">@dimen/space_between_cards</item>
<item name="android:layout_marginBottom">@dimen/space_between_cards</item>
</style>
<style name="Widget.Tachiyomi.GridView" parent="android:Widget">
<item name="android:smoothScrollbar">true</item>
<item name="android:numColumns">auto_fit</item>
@ -307,4 +289,23 @@
<item name="md_button_selector">@drawable/md_btn_selector_dark</item>
</style>
<!--================-->
<!--Shape Appearance-->
<!--================-->
<style name="ShapeAppearanceOverlay.MaterialCardView.Tracker" parent="">
<item name="cornerFamily">rounded</item>
<item name="cornerSize">8dp</item>
</style>
<style name="ThemeOverlay.Tachiyomi.Dialog.Fullscreen" parent="ThemeOverlay.MaterialComponents">
<item name="android:windowIsFloating">false</item>
<item name="android:windowBackground">?android:attr/colorBackground</item>
<item name="android:windowAnimationStyle">@style/Animation.Tachiyomi.Dialog</item>
</style>
<style name="Animation.Tachiyomi.Dialog" parent="Animation.AppCompat.Dialog">
<item name="android:windowEnterAnimation">@anim/fade_in_short</item>
<item name="android:windowExitAnimation">@anim/fade_out_short</item>
</style>
</resources>