forked from recloudstream/cloudstream
		
	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…
	
	Add table
		Add a link
		
	
		Reference in a new issue