mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
added primitive search history & added appCategory = video
fixed #874 and #878
This commit is contained in:
parent
71da2233d9
commit
279605e6ae
6 changed files with 246 additions and 16 deletions
|
@ -27,7 +27,8 @@
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme"
|
android:theme="@style/AppTheme"
|
||||||
android:fullBackupContent="@xml/backup_descriptor"
|
android:fullBackupContent="@xml/backup_descriptor"
|
||||||
tools:targetApi="m">
|
android:appCategory="video"
|
||||||
|
tools:targetApi="o">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
|
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
|
||||||
android:value="com.lagradost.cloudstream3.utils.CastOptionsProvider"/>
|
android:value="com.lagradost.cloudstream3.utils.CastOptionsProvider"/>
|
||||||
|
|
|
@ -23,6 +23,7 @@ import com.lagradost.cloudstream3.*
|
||||||
import com.lagradost.cloudstream3.APIHolder.filterProviderByPreferredMedia
|
import com.lagradost.cloudstream3.APIHolder.filterProviderByPreferredMedia
|
||||||
import com.lagradost.cloudstream3.APIHolder.getApiFromName
|
import com.lagradost.cloudstream3.APIHolder.getApiFromName
|
||||||
import com.lagradost.cloudstream3.APIHolder.getApiSettings
|
import com.lagradost.cloudstream3.APIHolder.getApiSettings
|
||||||
|
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
|
||||||
import com.lagradost.cloudstream3.mvvm.Resource
|
import com.lagradost.cloudstream3.mvvm.Resource
|
||||||
import com.lagradost.cloudstream3.mvvm.logError
|
import com.lagradost.cloudstream3.mvvm.logError
|
||||||
import com.lagradost.cloudstream3.mvvm.observe
|
import com.lagradost.cloudstream3.mvvm.observe
|
||||||
|
@ -164,7 +165,8 @@ class SearchFragment : Fragment() {
|
||||||
val cancelBtt = dialog.findViewById<MaterialButton>(R.id.cancel_btt)
|
val cancelBtt = dialog.findViewById<MaterialButton>(R.id.cancel_btt)
|
||||||
val applyBtt = dialog.findViewById<MaterialButton>(R.id.apply_btt)
|
val applyBtt = dialog.findViewById<MaterialButton>(R.id.apply_btt)
|
||||||
|
|
||||||
val pairList = HomeFragment.getPairList(anime, cartoons, tvs, docs, movies,asian)
|
val pairList =
|
||||||
|
HomeFragment.getPairList(anime, cartoons, tvs, docs, movies, asian)
|
||||||
|
|
||||||
cancelBtt?.setOnClickListener {
|
cancelBtt?.setOnClickListener {
|
||||||
dialog.dismissSafe()
|
dialog.dismissSafe()
|
||||||
|
@ -277,10 +279,21 @@ class SearchFragment : Fragment() {
|
||||||
search_select_asian,
|
search_select_asian,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val settingsManager = context?.let { PreferenceManager.getDefaultSharedPreferences(it) }
|
||||||
|
val isAdvancedSearch = settingsManager?.getBoolean("advanced_search", true) ?: true
|
||||||
|
|
||||||
selectedSearchTypes = context?.getKey<List<String>>(SEARCH_PREF_TAGS)
|
selectedSearchTypes = context?.getKey<List<String>>(SEARCH_PREF_TAGS)
|
||||||
?.mapNotNull { listName -> TvType.values().firstOrNull { it.name == listName } }
|
?.mapNotNull { listName -> TvType.values().firstOrNull { it.name == listName } }
|
||||||
?.toMutableList()
|
?.toMutableList()
|
||||||
?: mutableListOf(TvType.Movie, TvType.TvSeries)
|
?: mutableListOf(TvType.Movie, TvType.TvSeries)
|
||||||
|
|
||||||
|
fun updateSelectedList(list: MutableList<TvType>) {
|
||||||
|
selectedSearchTypes = list
|
||||||
|
for ((button, validTypes) in pairList) {
|
||||||
|
button?.isSelected = selectedSearchTypes.any { validTypes.contains(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
context?.filterProviderByPreferredMedia()?.let { validAPIs ->
|
context?.filterProviderByPreferredMedia()?.let { validAPIs ->
|
||||||
for ((button, validTypes) in pairList) {
|
for ((button, validTypes) in pairList) {
|
||||||
val isValid =
|
val isValid =
|
||||||
|
@ -339,10 +352,25 @@ class SearchFragment : Fragment() {
|
||||||
|
|
||||||
override fun onQueryTextChange(newText: String): Boolean {
|
override fun onQueryTextChange(newText: String): Boolean {
|
||||||
//searchViewModel.quickSearch(newText)
|
//searchViewModel.quickSearch(newText)
|
||||||
|
val showHistory = newText.isBlank()
|
||||||
|
searchViewModel.clearSearch()
|
||||||
|
searchViewModel.updateHistory()
|
||||||
|
|
||||||
|
search_history_recycler?.isVisible = showHistory
|
||||||
|
|
||||||
|
search_master_recycler?.isVisible = !showHistory && isAdvancedSearch
|
||||||
|
search_autofit_results?.isVisible = !showHistory && !isAdvancedSearch
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
observe(searchViewModel.currentHistory) { list ->
|
||||||
|
(search_history_recycler.adapter as? SearchHistoryAdaptor?)?.updateList(list)
|
||||||
|
}
|
||||||
|
|
||||||
|
searchViewModel.updateHistory()
|
||||||
|
|
||||||
observe(searchViewModel.searchResponse) {
|
observe(searchViewModel.searchResponse) {
|
||||||
when (it) {
|
when (it) {
|
||||||
is Resource.Success -> {
|
is Resource.Success -> {
|
||||||
|
@ -406,15 +434,31 @@ class SearchFragment : Fragment() {
|
||||||
activity?.loadHomepageList(item)
|
activity?.loadHomepageList(item)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
val historyAdapter = SearchHistoryAdaptor(mutableListOf()) { click ->
|
||||||
|
val searchItem = click.item
|
||||||
|
when (click.clickAction) {
|
||||||
|
SEARCH_HISTORY_OPEN -> {
|
||||||
|
searchViewModel.clearSearch()
|
||||||
|
if (searchItem.type.isNotEmpty())
|
||||||
|
updateSelectedList(searchItem.type.toMutableList())
|
||||||
|
main_search?.setQuery(searchItem.searchText, true)
|
||||||
|
}
|
||||||
|
SEARCH_HISTORY_REMOVE -> {
|
||||||
|
removeKey(SEARCH_HISTORY_KEY, searchItem.key)
|
||||||
|
searchViewModel.updateHistory()
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
// wth are you doing???
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
search_history_recycler?.adapter = historyAdapter
|
||||||
|
search_history_recycler?.layoutManager = GridLayoutManager(context, 1)
|
||||||
|
|
||||||
search_master_recycler?.adapter = masterAdapter
|
search_master_recycler?.adapter = masterAdapter
|
||||||
search_master_recycler?.layoutManager = GridLayoutManager(context, 1)
|
search_master_recycler?.layoutManager = GridLayoutManager(context, 1)
|
||||||
|
|
||||||
val settingsManager = context?.let { PreferenceManager.getDefaultSharedPreferences(it) }
|
|
||||||
val isAdvancedSearch = settingsManager?.getBoolean("advanced_search", true) ?: true
|
|
||||||
|
|
||||||
search_master_recycler?.isVisible = isAdvancedSearch
|
|
||||||
search_autofit_results?.isVisible = !isAdvancedSearch
|
|
||||||
|
|
||||||
// SubtitlesFragment.push(activity)
|
// SubtitlesFragment.push(activity)
|
||||||
//searchViewModel.search("iron man")
|
//searchViewModel.search("iron man")
|
||||||
//(activity as AppCompatActivity).loadResult("https://shiro.is/overlord-dubbed", "overlord-dubbed", "Shiro")
|
//(activity as AppCompatActivity).loadResult("https://shiro.is/overlord-dubbed", "overlord-dubbed", "Shiro")
|
||||||
|
|
|
@ -0,0 +1,103 @@
|
||||||
|
package com.lagradost.cloudstream3.ui.search
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
|
import com.lagradost.cloudstream3.R
|
||||||
|
import com.lagradost.cloudstream3.TvType
|
||||||
|
import kotlinx.android.synthetic.main.search_history_item.view.*
|
||||||
|
|
||||||
|
data class SearchHistoryItem(
|
||||||
|
@JsonProperty("searchedAt") val searchedAt: Long,
|
||||||
|
@JsonProperty("searchText") val searchText: String,
|
||||||
|
@JsonProperty("type") val type: List<TvType>,
|
||||||
|
@JsonProperty("key") val key: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class SearchHistoryCallback(
|
||||||
|
val item: SearchHistoryItem,
|
||||||
|
val clickAction: Int,
|
||||||
|
)
|
||||||
|
|
||||||
|
const val SEARCH_HISTORY_OPEN = 0
|
||||||
|
const val SEARCH_HISTORY_REMOVE = 1
|
||||||
|
|
||||||
|
class SearchHistoryAdaptor(
|
||||||
|
private val cardList: MutableList<SearchHistoryItem>,
|
||||||
|
private val clickCallback: (SearchHistoryCallback) -> Unit,
|
||||||
|
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
|
return CardViewHolder(
|
||||||
|
LayoutInflater.from(parent.context)
|
||||||
|
.inflate(R.layout.search_history_item, parent, false),
|
||||||
|
clickCallback,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||||
|
when (holder) {
|
||||||
|
is CardViewHolder -> {
|
||||||
|
holder.bind(cardList[position])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int {
|
||||||
|
return cardList.size
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateList(newList: List<SearchHistoryItem>) {
|
||||||
|
val diffResult = DiffUtil.calculateDiff(
|
||||||
|
SearchHistoryDiffCallback(this.cardList, newList)
|
||||||
|
)
|
||||||
|
|
||||||
|
cardList.clear()
|
||||||
|
cardList.addAll(newList)
|
||||||
|
|
||||||
|
diffResult.dispatchUpdatesTo(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
class CardViewHolder
|
||||||
|
constructor(
|
||||||
|
itemView: View,
|
||||||
|
private val clickCallback: (SearchHistoryCallback) -> Unit,
|
||||||
|
) :
|
||||||
|
RecyclerView.ViewHolder(itemView) {
|
||||||
|
private val removeButton: ImageView = itemView.home_history_remove
|
||||||
|
private val openButton: View = itemView.home_history_tab
|
||||||
|
private val title: TextView = itemView.home_history_title
|
||||||
|
|
||||||
|
fun bind(card: SearchHistoryItem) {
|
||||||
|
title.text = card.searchText
|
||||||
|
|
||||||
|
removeButton.setOnClickListener {
|
||||||
|
clickCallback.invoke(SearchHistoryCallback(card, SEARCH_HISTORY_REMOVE))
|
||||||
|
}
|
||||||
|
openButton.setOnClickListener {
|
||||||
|
clickCallback.invoke(SearchHistoryCallback(card, SEARCH_HISTORY_OPEN))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SearchHistoryDiffCallback(
|
||||||
|
private val oldList: List<SearchHistoryItem>,
|
||||||
|
private val newList: List<SearchHistoryItem>
|
||||||
|
) :
|
||||||
|
DiffUtil.Callback() {
|
||||||
|
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) =
|
||||||
|
oldList[oldItemPosition].searchText == newList[newItemPosition].searchText
|
||||||
|
|
||||||
|
override fun getOldListSize() = oldList.size
|
||||||
|
|
||||||
|
override fun getNewListSize() = newList.size
|
||||||
|
|
||||||
|
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) =
|
||||||
|
oldList[oldItemPosition] == newList[newItemPosition]
|
||||||
|
}
|
|
@ -6,11 +6,15 @@ import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.lagradost.cloudstream3.*
|
import com.lagradost.cloudstream3.*
|
||||||
import com.lagradost.cloudstream3.APIHolder.apis
|
import com.lagradost.cloudstream3.APIHolder.apis
|
||||||
|
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
|
||||||
|
import com.lagradost.cloudstream3.AcraApplication.Companion.getKeys
|
||||||
|
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
|
||||||
import com.lagradost.cloudstream3.mvvm.Resource
|
import com.lagradost.cloudstream3.mvvm.Resource
|
||||||
import com.lagradost.cloudstream3.mvvm.safeApiCall
|
import com.lagradost.cloudstream3.mvvm.safeApiCall
|
||||||
import com.lagradost.cloudstream3.syncproviders.OAuth2API.Companion.SyncApis
|
import com.lagradost.cloudstream3.syncproviders.OAuth2API.Companion.SyncApis
|
||||||
import com.lagradost.cloudstream3.syncproviders.SyncAPI
|
import com.lagradost.cloudstream3.syncproviders.SyncAPI
|
||||||
import com.lagradost.cloudstream3.ui.APIRepository
|
import com.lagradost.cloudstream3.ui.APIRepository
|
||||||
|
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
@ -21,22 +25,28 @@ data class OnGoingSearch(
|
||||||
val data: Resource<List<SearchResponse>>
|
val data: Resource<List<SearchResponse>>
|
||||||
)
|
)
|
||||||
|
|
||||||
class SearchViewModel : ViewModel() {
|
const val SEARCH_HISTORY_KEY = "search_history"
|
||||||
private val _searchResponse: MutableLiveData<Resource<ArrayList<SearchResponse>>> =
|
|
||||||
MutableLiveData()
|
|
||||||
val searchResponse: LiveData<Resource<ArrayList<SearchResponse>>> get() = _searchResponse
|
|
||||||
|
|
||||||
private val _currentSearch: MutableLiveData<ArrayList<OnGoingSearch>> = MutableLiveData()
|
class SearchViewModel : ViewModel() {
|
||||||
val currentSearch: LiveData<ArrayList<OnGoingSearch>> get() = _currentSearch
|
private val _searchResponse: MutableLiveData<Resource<List<SearchResponse>>> =
|
||||||
|
MutableLiveData()
|
||||||
|
val searchResponse: LiveData<Resource<List<SearchResponse>>> get() = _searchResponse
|
||||||
|
|
||||||
|
private val _currentSearch: MutableLiveData<List<OnGoingSearch>> = MutableLiveData()
|
||||||
|
val currentSearch: LiveData<List<OnGoingSearch>> get() = _currentSearch
|
||||||
|
|
||||||
|
private val _currentHistory: MutableLiveData<List<SearchHistoryItem>> = MutableLiveData()
|
||||||
|
val currentHistory: LiveData<List<SearchHistoryItem>> get() = _currentHistory
|
||||||
|
|
||||||
private val repos = apis.map { APIRepository(it) }
|
private val repos = apis.map { APIRepository(it) }
|
||||||
private val syncApis = SyncApis
|
private val syncApis = SyncApis
|
||||||
|
|
||||||
private fun clearSearch() {
|
fun clearSearch() {
|
||||||
_searchResponse.postValue(Resource.Success(ArrayList()))
|
_searchResponse.postValue(Resource.Success(ArrayList()))
|
||||||
|
_currentSearch.postValue(emptyList())
|
||||||
}
|
}
|
||||||
|
|
||||||
var onGoingSearch: Job? = null
|
private var onGoingSearch: Job? = null
|
||||||
fun searchAndCancel(
|
fun searchAndCancel(
|
||||||
query: String,
|
query: String,
|
||||||
isMainApis: Boolean = true,
|
isMainApis: Boolean = true,
|
||||||
|
@ -68,6 +78,15 @@ class SearchViewModel : ViewModel() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun updateHistory() = viewModelScope.launch {
|
||||||
|
ioSafe {
|
||||||
|
val items = getKeys(SEARCH_HISTORY_KEY)?.mapNotNull {
|
||||||
|
getKey<SearchHistoryItem>(it)
|
||||||
|
}?.sortedByDescending { it.searchedAt } ?: emptyList()
|
||||||
|
_currentHistory.postValue(items)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun search(
|
private fun search(
|
||||||
query: String,
|
query: String,
|
||||||
isMainApis: Boolean = true,
|
isMainApis: Boolean = true,
|
||||||
|
@ -80,6 +99,18 @@ class SearchViewModel : ViewModel() {
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val key = query.hashCode().toString()
|
||||||
|
setKey(
|
||||||
|
SEARCH_HISTORY_KEY,
|
||||||
|
key,
|
||||||
|
SearchHistoryItem(
|
||||||
|
searchedAt = System.currentTimeMillis(),
|
||||||
|
searchText = query,
|
||||||
|
type = emptyList(), // TODO implement tv type
|
||||||
|
key = key,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
_searchResponse.postValue(Resource.Loading())
|
_searchResponse.postValue(Resource.Loading())
|
||||||
|
|
||||||
val currentList = ArrayList<OnGoingSearch>()
|
val currentList = ArrayList<OnGoingSearch>()
|
||||||
|
|
|
@ -81,6 +81,7 @@
|
||||||
app:tint="?attr/textColor"
|
app:tint="?attr/textColor"
|
||||||
android:contentDescription="@string/change_providers_img_des" />
|
android:contentDescription="@string/change_providers_img_des" />
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
<HorizontalScrollView
|
<HorizontalScrollView
|
||||||
android:paddingStart="10dp"
|
android:paddingStart="10dp"
|
||||||
android:paddingEnd="10dp"
|
android:paddingEnd="10dp"
|
||||||
|
@ -144,6 +145,7 @@
|
||||||
|
|
||||||
|
|
||||||
<com.lagradost.cloudstream3.ui.AutofitRecyclerView
|
<com.lagradost.cloudstream3.ui.AutofitRecyclerView
|
||||||
|
android:visibility="gone"
|
||||||
android:nextFocusLeft="@id/nav_rail_view"
|
android:nextFocusLeft="@id/nav_rail_view"
|
||||||
android:descendantFocusability="afterDescendants"
|
android:descendantFocusability="afterDescendants"
|
||||||
|
|
||||||
|
@ -160,6 +162,7 @@
|
||||||
android:orientation="vertical" />
|
android:orientation="vertical" />
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:visibility="gone"
|
||||||
android:nextFocusLeft="@id/nav_rail_view"
|
android:nextFocusLeft="@id/nav_rail_view"
|
||||||
android:descendantFocusability="afterDescendants"
|
android:descendantFocusability="afterDescendants"
|
||||||
|
|
||||||
|
@ -168,4 +171,15 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:listitem="@layout/homepage_parent" />
|
tools:listitem="@layout/homepage_parent" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:visibility="visible"
|
||||||
|
android:nextFocusLeft="@id/nav_rail_view"
|
||||||
|
android:descendantFocusability="afterDescendants"
|
||||||
|
|
||||||
|
android:background="?attr/primaryBlackBackground"
|
||||||
|
android:id="@+id/search_history_recycler"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:listitem="@layout/search_history_item" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
37
app/src/main/res/layout/search_history_item.xml
Normal file
37
app/src/main/res/layout/search_history_item.xml
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:id="@+id/home_history_tab"
|
||||||
|
android:nextFocusRight="@id/home_history_remove">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/home_history_title"
|
||||||
|
android:textColor="?attr/textColor"
|
||||||
|
android:textSize="18sp"
|
||||||
|
tools:text="Hello World"
|
||||||
|
android:padding="10dp"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:padding="10dp"
|
||||||
|
android:id="@+id/home_history_remove"
|
||||||
|
android:nextFocusLeft="@id/home_history_tab"
|
||||||
|
|
||||||
|
android:src="@drawable/ic_baseline_close_24"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical|end"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:tint="?attr/white"
|
||||||
|
tools:ignore="ContentDescription"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
Loading…
Reference in a new issue