mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-12-23 14:31:50 +01:00
Open source from global search
Along with other changes from upstream The difference as you'd expect: Not allowing the opening of a source on migration and from extension intent Also when moving up sources in global search after returning results. The incoming results for subsequent sources will go under the already returned results, instead of being alphabetical and shifting the top results (pinned sources still show on top though) Co-Authored-By: arkon <4098258+arkon@users.noreply.github.com>
This commit is contained in:
parent
48a4b3091f
commit
c9388f6633
@ -284,7 +284,7 @@ class PreferencesHelper(val context: Context) {
|
||||
|
||||
fun hiddenSources() = flowPrefs.getStringSet("hidden_catalogues", mutableSetOf())
|
||||
|
||||
fun pinnedCatalogues() = rxPrefs.getStringSet("pinned_catalogues", emptySet())
|
||||
fun pinnedCatalogues() = flowPrefs.getStringSet("pinned_catalogues", mutableSetOf())
|
||||
|
||||
fun downloadNew() = flowPrefs.getBoolean(Keys.downloadNew, false)
|
||||
|
||||
|
@ -177,7 +177,7 @@ class PreMigrationController(bundle: Bundle? = null) :
|
||||
val enabledSources = if (item.itemId == R.id.action_match_enabled) {
|
||||
prefs.hiddenSources().get().mapNotNull { it.toLongOrNull() }
|
||||
} else {
|
||||
prefs.pinnedCatalogues().get()?.mapNotNull { it.toLongOrNull() } ?: emptyList()
|
||||
prefs.pinnedCatalogues().get().mapNotNull { it.toLongOrNull() } ?: emptyList()
|
||||
}
|
||||
val items = adapter?.currentItems?.toList() ?: return true
|
||||
items.forEach {
|
||||
|
@ -24,7 +24,6 @@ import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||
import eu.kanade.tachiyomi.databinding.BrowseControllerBinding
|
||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||
import eu.kanade.tachiyomi.source.LocalSource
|
||||
@ -390,7 +389,7 @@ class BrowseController :
|
||||
}
|
||||
|
||||
private fun pinCatalogue(source: Source, isPinned: Boolean) {
|
||||
val current = preferences.pinnedCatalogues().getOrDefault()
|
||||
val current = preferences.pinnedCatalogues().get()
|
||||
if (isPinned) {
|
||||
preferences.pinnedCatalogues().set(current - source.id.toString())
|
||||
} else {
|
||||
|
@ -1,7 +1,6 @@
|
||||
package eu.kanade.tachiyomi.ui.source
|
||||
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||
import eu.kanade.tachiyomi.source.LocalSource
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
@ -57,7 +56,7 @@ class SourcePresenter(
|
||||
private fun loadSources() {
|
||||
scope.launch {
|
||||
val pinnedSources = mutableListOf<SourceItem>()
|
||||
val pinnedCatalogues = preferences.pinnedCatalogues().getOrDefault()
|
||||
val pinnedCatalogues = preferences.pinnedCatalogues().get()
|
||||
|
||||
val map = TreeMap<String, MutableList<CatalogueSource>> { d1, d2 ->
|
||||
// Catalogues without a lang defined will be placed at the end
|
||||
@ -105,7 +104,7 @@ class SourcePresenter(
|
||||
|
||||
private fun getLastUsedSource(value: Long): SourceItem? {
|
||||
return (sourceManager.get(value) as? CatalogueSource)?.let { source ->
|
||||
val pinnedCatalogues = preferences.pinnedCatalogues().getOrDefault()
|
||||
val pinnedCatalogues = preferences.pinnedCatalogues().get()
|
||||
val isPinned = source.id.toString() in pinnedCatalogues
|
||||
if (isPinned) null
|
||||
else SourceItem(source, null, isPinned)
|
||||
|
@ -12,7 +12,6 @@ import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.jakewharton.rxbinding.support.v7.widget.queryTextChangeEvents
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.kanade.tachiyomi.R
|
||||
@ -29,6 +28,7 @@ import eu.kanade.tachiyomi.ui.main.FloatingSearchInterface
|
||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaDetailsController
|
||||
import eu.kanade.tachiyomi.ui.source.BrowseController
|
||||
import eu.kanade.tachiyomi.ui.source.global_search.GlobalSearchController
|
||||
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
||||
import eu.kanade.tachiyomi.util.addOrRemoveToFavorites
|
||||
import eu.kanade.tachiyomi.util.system.connectivityManager
|
||||
@ -37,6 +37,7 @@ import eu.kanade.tachiyomi.util.system.openInBrowser
|
||||
import eu.kanade.tachiyomi.util.view.gone
|
||||
import eu.kanade.tachiyomi.util.view.inflate
|
||||
import eu.kanade.tachiyomi.util.view.scrollViewWith
|
||||
import eu.kanade.tachiyomi.util.view.setOnQueryTextChangeListener
|
||||
import eu.kanade.tachiyomi.util.view.snack
|
||||
import eu.kanade.tachiyomi.util.view.updateLayoutParams
|
||||
import eu.kanade.tachiyomi.util.view.visible
|
||||
@ -44,12 +45,8 @@ import eu.kanade.tachiyomi.util.view.visibleIf
|
||||
import eu.kanade.tachiyomi.util.view.withFadeTransaction
|
||||
import eu.kanade.tachiyomi.widget.AutofitRecyclerView
|
||||
import eu.kanade.tachiyomi.widget.EmptyView
|
||||
import rx.Observable
|
||||
import rx.Subscription
|
||||
import rx.android.schedulers.AndroidSchedulers
|
||||
import timber.log.Timber
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/**
|
||||
* Controller to manage the catalogues available in the app.
|
||||
@ -105,11 +102,6 @@ open class BrowseSourceController(bundle: Bundle) :
|
||||
*/
|
||||
private var recycler: RecyclerView? = null
|
||||
|
||||
/**
|
||||
* Subscription for the search view.
|
||||
*/
|
||||
private var searchViewSubscription: Subscription? = null
|
||||
|
||||
/**
|
||||
* Endless loading item.
|
||||
*/
|
||||
@ -124,7 +116,7 @@ open class BrowseSourceController(bundle: Bundle) :
|
||||
}
|
||||
|
||||
override fun createPresenter(): BrowseSourcePresenter {
|
||||
return BrowseSourcePresenter(args.getLong(SOURCE_ID_KEY))
|
||||
return BrowseSourcePresenter(args.getLong(SOURCE_ID_KEY), args.getString(SEARCH_QUERY_KEY))
|
||||
}
|
||||
|
||||
override fun createBinding(inflater: LayoutInflater) = BrowseSourceControllerBinding.inflate(inflater)
|
||||
@ -142,8 +134,6 @@ open class BrowseSourceController(bundle: Bundle) :
|
||||
}
|
||||
|
||||
override fun onDestroyView(view: View) {
|
||||
searchViewSubscription?.unsubscribe()
|
||||
searchViewSubscription = null
|
||||
adapter = null
|
||||
snack = null
|
||||
recycler = null
|
||||
@ -222,31 +212,40 @@ open class BrowseSourceController(bundle: Bundle) :
|
||||
val searchView = searchItem.actionView as SearchView
|
||||
|
||||
val query = presenter.query
|
||||
if (!query.isBlank()) {
|
||||
if (query.isNotBlank()) {
|
||||
searchItem.expandActionView()
|
||||
searchView.setQuery(query, true)
|
||||
searchView.clearFocus()
|
||||
}
|
||||
|
||||
val searchEventsObservable = searchView.queryTextChangeEvents()
|
||||
.skip(1)
|
||||
.filter { router.backstack.lastOrNull()?.controller() == this@BrowseSourceController }
|
||||
.share()
|
||||
val writingObservable = searchEventsObservable
|
||||
.filter { !it.isSubmitted }
|
||||
.debounce(1250, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
|
||||
val submitObservable = searchEventsObservable
|
||||
.filter { it.isSubmitted }
|
||||
// val searchEventsObservable = searchView.queryTextChangeEvents()
|
||||
// .skip(1)
|
||||
// .filter { router.backstack.lastOrNull()?.controller() == this@BrowseSourceController }
|
||||
// .share()
|
||||
// val writingObservable = searchEventsObservable
|
||||
// .filter { !it.isSubmitted }
|
||||
// .debounce(1250, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
|
||||
// val submitObservable = searchEventsObservable
|
||||
// .filter { it.isSubmitted }
|
||||
//
|
||||
// searchViewSubscription?.unsubscribe()
|
||||
// searchViewSubscription = Observable.merge(writingObservable, submitObservable)
|
||||
// .map { it.queryText().toString() }
|
||||
// .subscribeUntilDestroy { searchWithQuery(it) }
|
||||
|
||||
searchViewSubscription?.unsubscribe()
|
||||
searchViewSubscription = Observable.merge(writingObservable, submitObservable)
|
||||
.map { it.queryText().toString() }
|
||||
.subscribeUntilDestroy { searchWithQuery(it) }
|
||||
setOnQueryTextChangeListener(searchView, onlyOnSubmit = true, hideKbOnSubmit = false) {
|
||||
searchWithQuery(it ?: "")
|
||||
true
|
||||
}
|
||||
|
||||
searchItem.fixExpand(
|
||||
onExpand = { invalidateMenuOnExpand() },
|
||||
onCollapse = {
|
||||
searchWithQuery("")
|
||||
if (router.backstackSize >= 2 && router.backstack[router.backstackSize - 2].controller() is GlobalSearchController) {
|
||||
router.popController(this)
|
||||
} else {
|
||||
searchWithQuery("")
|
||||
}
|
||||
true
|
||||
}
|
||||
)
|
||||
|
@ -48,6 +48,7 @@ import uy.kohesive.injekt.api.get
|
||||
*/
|
||||
open class BrowseSourcePresenter(
|
||||
sourceId: Long,
|
||||
searchQuery: String? = null,
|
||||
sourceManager: SourceManager = Injekt.get(),
|
||||
val db: DatabaseHelper = Injekt.get(),
|
||||
private val prefs: PreferencesHelper = Injekt.get(),
|
||||
@ -117,6 +118,10 @@ open class BrowseSourcePresenter(
|
||||
|
||||
private var scope = CoroutineScope(Job() + Dispatchers.IO)
|
||||
|
||||
init {
|
||||
query = searchQuery ?: ""
|
||||
}
|
||||
|
||||
override fun onCreate(savedState: Bundle?) {
|
||||
super.onCreate(savedState)
|
||||
|
||||
|
@ -4,6 +4,7 @@ import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.util.SparseArray
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||
|
||||
/**
|
||||
* Adapter that holds the search cards.
|
||||
@ -13,6 +14,8 @@ import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
class GlobalSearchAdapter(val controller: GlobalSearchController) :
|
||||
FlexibleAdapter<GlobalSearchItem>(null, controller, true) {
|
||||
|
||||
val titleClickListener: OnTitleClickListener = controller
|
||||
|
||||
/**
|
||||
* Bundle where the view state of the holders is saved.
|
||||
*/
|
||||
@ -67,6 +70,10 @@ class GlobalSearchAdapter(val controller: GlobalSearchController) :
|
||||
}
|
||||
}
|
||||
|
||||
interface OnTitleClickListener {
|
||||
fun onTitleClick(source: CatalogueSource)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val HOLDER_BUNDLE_KEY = "holder_bundle"
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ import eu.kanade.tachiyomi.ui.base.controller.NucleusController
|
||||
import eu.kanade.tachiyomi.ui.main.FloatingSearchInterface
|
||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaDetailsController
|
||||
import eu.kanade.tachiyomi.ui.source.browse.BrowseSourceController
|
||||
import eu.kanade.tachiyomi.util.addOrRemoveToFavorites
|
||||
import eu.kanade.tachiyomi.util.view.activityBinding
|
||||
import eu.kanade.tachiyomi.util.view.scrollViewWith
|
||||
@ -33,9 +34,10 @@ import uy.kohesive.injekt.injectLazy
|
||||
*/
|
||||
open class GlobalSearchController(
|
||||
protected val initialQuery: String? = null,
|
||||
protected val extensionFilter: String? = null
|
||||
val extensionFilter: String? = null
|
||||
) : NucleusController<SourceGlobalSearchControllerBinding, GlobalSearchPresenter>(),
|
||||
FloatingSearchInterface,
|
||||
GlobalSearchAdapter.OnTitleClickListener,
|
||||
GlobalSearchCardAdapter.OnMangaClickListener {
|
||||
|
||||
/**
|
||||
@ -82,6 +84,11 @@ open class GlobalSearchController(
|
||||
return GlobalSearchPresenter(initialQuery, extensionFilter)
|
||||
}
|
||||
|
||||
override fun onTitleClick(source: CatalogueSource) {
|
||||
preferences.lastUsedCatalogueSource().set(source.id)
|
||||
router.pushController(BrowseSourceController(source, presenter.query).withFadeTransaction())
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when manga in global search is clicked, opens manga.
|
||||
*
|
||||
@ -180,6 +187,9 @@ open class GlobalSearchController(
|
||||
customTitle = view.context?.getString(R.string.loading)
|
||||
setTitle()
|
||||
}
|
||||
binding.recycler.post {
|
||||
binding.recycler.scrollToPosition(0)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView(view: View) {
|
||||
|
@ -1,9 +1,13 @@
|
||||
package eu.kanade.tachiyomi.ui.source.global_search
|
||||
|
||||
import android.view.View
|
||||
import androidx.core.view.isVisible
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.databinding.SourceGlobalSearchControllerCardBinding
|
||||
import eu.kanade.tachiyomi.source.LocalSource
|
||||
import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder
|
||||
import eu.kanade.tachiyomi.ui.migration.SearchController
|
||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||
import eu.kanade.tachiyomi.util.view.gone
|
||||
import eu.kanade.tachiyomi.util.view.visible
|
||||
|
||||
@ -30,6 +34,15 @@ class GlobalSearchHolder(view: View, val adapter: GlobalSearchAdapter) :
|
||||
binding.recycler.layoutManager =
|
||||
androidx.recyclerview.widget.LinearLayoutManager(view.context, androidx.recyclerview.widget.LinearLayoutManager.HORIZONTAL, false)
|
||||
binding.recycler.adapter = mangaAdapter
|
||||
|
||||
binding.titleMoreIcon.isVisible = adapter.controller !is SearchController && adapter.controller.extensionFilter == null
|
||||
if (binding.titleMoreIcon.isVisible) {
|
||||
binding.titleWrapper.setOnClickListener {
|
||||
adapter.getItem(bindingAdapterPosition)?.let {
|
||||
adapter.titleClickListener.onTitleClick(it.source)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -46,6 +59,8 @@ class GlobalSearchHolder(view: View, val adapter: GlobalSearchAdapter) :
|
||||
|
||||
// Set Title with country code if available.
|
||||
binding.title.text = titlePrefix + source.name + langSuffix
|
||||
binding.subtitle.isVisible = source !is LocalSource
|
||||
binding.subtitle.text = LocaleHelper.getDisplayName(source.lang)
|
||||
|
||||
when {
|
||||
results == null -> {
|
||||
|
@ -6,7 +6,6 @@ import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||
import eu.kanade.tachiyomi.extension.ExtensionManager
|
||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||
import eu.kanade.tachiyomi.source.Source
|
||||
@ -25,6 +24,7 @@ import timber.log.Timber
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.util.Date
|
||||
|
||||
/**
|
||||
* Presenter of [GlobalSearchController]
|
||||
@ -32,7 +32,7 @@ import uy.kohesive.injekt.injectLazy
|
||||
*
|
||||
* @param sourceManager manages the different sources.
|
||||
* @param db manages the database calls.
|
||||
* @param preferencesHelper manages the preference calls.
|
||||
* @param preferences manages the preference calls.
|
||||
*/
|
||||
open class GlobalSearchPresenter(
|
||||
private val initialQuery: String? = "",
|
||||
@ -40,7 +40,7 @@ open class GlobalSearchPresenter(
|
||||
private val sourcesToUse: List<CatalogueSource>? = null,
|
||||
val sourceManager: SourceManager = Injekt.get(),
|
||||
val db: DatabaseHelper = Injekt.get(),
|
||||
private val preferencesHelper: PreferencesHelper = Injekt.get(),
|
||||
private val preferences: PreferencesHelper = Injekt.get(),
|
||||
private val coverCache: CoverCache = Injekt.get()
|
||||
) : BasePresenter<GlobalSearchController>() {
|
||||
|
||||
@ -60,6 +60,8 @@ open class GlobalSearchPresenter(
|
||||
*/
|
||||
private var fetchSourcesSubscription: Subscription? = null
|
||||
|
||||
private var loadTime = hashMapOf<Long, Long>()
|
||||
|
||||
/**
|
||||
* Subject which fetches image of given manga.
|
||||
*/
|
||||
@ -104,16 +106,16 @@ open class GlobalSearchPresenter(
|
||||
* @return list containing enabled sources.
|
||||
*/
|
||||
protected open fun getEnabledSources(): List<CatalogueSource> {
|
||||
val languages = preferencesHelper.enabledLanguages().get()
|
||||
val hiddenCatalogues = preferencesHelper.hiddenSources().get()
|
||||
val pinnedCatalogues = preferencesHelper.pinnedCatalogues().getOrDefault()
|
||||
val languages = preferences.enabledLanguages().get()
|
||||
val hiddenCatalogues = preferences.hiddenSources().get()
|
||||
val pinnedCatalogues = preferences.pinnedCatalogues().get()
|
||||
|
||||
val list = sourceManager.getCatalogueSources()
|
||||
.filter { it.lang in languages }
|
||||
.filterNot { it.id.toString() in hiddenCatalogues }
|
||||
.sortedBy { "(${it.lang}) ${it.name}" }
|
||||
|
||||
return if (preferencesHelper.onlySearchPinned().get()) {
|
||||
return if (preferences.onlySearchPinned().get()) {
|
||||
list.filter { it.id.toString() in pinnedCatalogues }
|
||||
} else {
|
||||
list.sortedBy { it.id.toString() !in pinnedCatalogues }
|
||||
@ -176,6 +178,8 @@ open class GlobalSearchPresenter(
|
||||
val initialItems = sources.map { createCatalogueSearchItem(it, null) }
|
||||
var items = initialItems
|
||||
|
||||
val pinnedSourceIds = preferences.pinnedCatalogues().get()
|
||||
|
||||
fetchSourcesSubscription?.unsubscribe()
|
||||
fetchSourcesSubscription = Observable.from(sources).flatMap(
|
||||
{ source ->
|
||||
@ -197,6 +201,9 @@ open class GlobalSearchPresenter(
|
||||
} // Convert to local manga.
|
||||
.doOnNext { fetchImage(it, source) } // Load manga covers.
|
||||
.map {
|
||||
if (it.isNotEmpty() && !loadTime.containsKey(source.id)) {
|
||||
loadTime[source.id] = Date().time
|
||||
}
|
||||
createCatalogueSearchItem(
|
||||
source,
|
||||
it.map { GlobalSearchMangaItem(it) }
|
||||
@ -204,10 +211,22 @@ open class GlobalSearchPresenter(
|
||||
}
|
||||
},
|
||||
5
|
||||
).observeOn(AndroidSchedulers.mainThread())
|
||||
)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
// Update matching source with the obtained results
|
||||
.map { result ->
|
||||
items.map { item -> if (item.source == result.source) result else item }
|
||||
items
|
||||
.map { item -> if (item.source == result.source) result else item }
|
||||
.sortedWith(
|
||||
compareBy(
|
||||
// Bubble up sources that actually have results
|
||||
{ it.results.isNullOrEmpty() },
|
||||
// Same as initial sort, i.e. pinned first then alphabetically
|
||||
{ it.source.id.toString() !in pinnedSourceIds },
|
||||
{ loadTime[it.source.id] ?: 0L },
|
||||
{ "${it.source.name.toLowerCase()} (${it.source.lang})" }
|
||||
)
|
||||
)
|
||||
}
|
||||
// Update current state
|
||||
.doOnNext { items = it }
|
||||
|
@ -42,6 +42,7 @@ import kotlin.random.Random
|
||||
fun Controller.setOnQueryTextChangeListener(
|
||||
searchView: SearchView,
|
||||
onlyOnSubmit: Boolean = false,
|
||||
hideKbOnSubmit: Boolean = true,
|
||||
f: (text: String?) -> Boolean
|
||||
) {
|
||||
searchView.setOnQueryTextListener(
|
||||
@ -57,10 +58,12 @@ fun Controller.setOnQueryTextChangeListener(
|
||||
|
||||
override fun onQueryTextSubmit(query: String?): Boolean {
|
||||
if (router.backstack.lastOrNull()?.controller() == this@setOnQueryTextChangeListener) {
|
||||
val imm =
|
||||
activity?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
|
||||
?: return f(query)
|
||||
imm.hideSoftInputFromWindow(searchView.windowToken, 0)
|
||||
if (hideKbOnSubmit) {
|
||||
val imm =
|
||||
activity?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
|
||||
?: return f(query)
|
||||
imm.hideSoftInputFromWindow(searchView.windowToken, 0)
|
||||
}
|
||||
return f(query)
|
||||
}
|
||||
return true
|
||||
|
@ -6,18 +6,56 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
style="@style/TextAppearance.Regular.SubHeading"
|
||||
android:layout_width="wrap_content"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/title_wrapper"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:paddingBottom="0dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Title" />
|
||||
android:background="?attr/selectableItemBackground">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
style="@style/TextAppearance.Regular.SubHeading"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/subtitle"
|
||||
app:layout_constraintEnd_toStartOf="@id/title_more_icon"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
tools:text="Title" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/subtitle"
|
||||
style="@style/TextAppearance.Regular.Body1.Secondary"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:maxLines="1"
|
||||
android:textSize="12sp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/title_more_icon"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/title"
|
||||
tools:text="English"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/title_more_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/more"
|
||||
android:padding="@dimen/material_component_text_fields_padding_above_and_below_label"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/ic_arrow_forward_24dp"
|
||||
app:tint="?android:attr/textColorPrimary" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/no_results"
|
||||
@ -34,7 +72,7 @@
|
||||
android:visibility="gone"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
tools:visibility="visible"
|
||||
app:layout_constraintTop_toBottomOf="@id/title"
|
||||
app:layout_constraintTop_toBottomOf="@id/title_wrapper"
|
||||
android:text="@string/no_results_found"/>
|
||||
|
||||
<FrameLayout
|
||||
@ -44,7 +82,8 @@
|
||||
android:minHeight="208dp"
|
||||
android:paddingTop="2dp"
|
||||
android:paddingBottom="8dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/title"
|
||||
android:layout_marginTop="4dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/title_wrapper"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constrainedHeight="true"
|
||||
|
Loading…
Reference in New Issue
Block a user