new search behavior

This commit is contained in:
Bnyro 2022-07-31 22:17:36 +02:00
parent f33b9b7afb
commit 022281e107
9 changed files with 224 additions and 324 deletions

View File

@ -17,9 +17,11 @@ import android.view.WindowInsetsController
import android.view.WindowManager import android.view.WindowManager
import android.widget.LinearLayout import android.widget.LinearLayout
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.SearchView
import androidx.constraintlayout.motion.widget.MotionLayout import androidx.constraintlayout.motion.widget.MotionLayout
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.fragment.app.replace
import androidx.navigation.NavController import androidx.navigation.NavController
import androidx.navigation.findNavController import androidx.navigation.findNavController
import androidx.navigation.ui.setupWithNavController import androidx.navigation.ui.setupWithNavController
@ -28,6 +30,7 @@ import com.github.libretube.Globals
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.databinding.ActivityMainBinding import com.github.libretube.databinding.ActivityMainBinding
import com.github.libretube.fragments.PlayerFragment import com.github.libretube.fragments.PlayerFragment
import com.github.libretube.fragments.SearchFragment
import com.github.libretube.preferences.PreferenceHelper import com.github.libretube.preferences.PreferenceHelper
import com.github.libretube.preferences.PreferenceKeys import com.github.libretube.preferences.PreferenceKeys
import com.github.libretube.services.ClosingService import com.github.libretube.services.ClosingService
@ -46,6 +49,7 @@ class MainActivity : AppCompatActivity() {
lateinit var navController: NavController lateinit var navController: NavController
private var startFragmentId = R.id.homeFragment private var startFragmentId = R.id.homeFragment
var autoRotationEnabled = false var autoRotationEnabled = false
private var searchFragment: SearchFragment? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
// set the app theme (e.g. Material You) // set the app theme (e.g. Material You)
@ -154,7 +158,26 @@ class MainActivity : AppCompatActivity() {
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
// Inflate the menu; this adds items to the action bar if it is present. // Inflate the menu; this adds items to the action bar if it is present.
menuInflater.inflate(R.menu.action_bar, menu) menuInflater.inflate(R.menu.action_bar, menu)
return true val searchItem = menu.findItem(R.id.action_search)
// stuff for the search in the topBar
val searchView = searchItem.actionView as SearchView
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
val bundle = Bundle()
bundle.putString("query", query)
navController.navigate(R.id.searchResultFragment, bundle)
return true
}
override fun onQueryTextChange(newText: String?): Boolean {
val bundle = Bundle()
bundle.putString("query", newText)
navController.navigate(R.id.searchFragment, bundle)
return true
}
})
return super.onCreateOptionsMenu(menu)
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
@ -162,10 +185,6 @@ class MainActivity : AppCompatActivity() {
// automatically handle clicks on the Home/Up button, so long // automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml. // as you specify a parent activity in AndroidManifest.xml.
return when (item.itemId) { return when (item.itemId) {
R.id.action_search -> {
navController.navigate(R.id.searchFragment)
true
}
R.id.action_settings -> { R.id.action_settings -> {
val settingsIntent = Intent(this, SettingsActivity::class.java) val settingsIntent = Intent(this, SettingsActivity::class.java)
startActivity(settingsIntent) startActivity(settingsIntent)
@ -185,6 +204,14 @@ class MainActivity : AppCompatActivity() {
} }
} }
private fun addToHistory(query: String) {
val searchHistoryEnabled =
PreferenceHelper.getBoolean(PreferenceKeys.SEARCH_HISTORY_TOGGLE, true)
if (searchHistoryEnabled && query != "") {
PreferenceHelper.saveToSearchHistory(query)
}
}
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
val intentData: Uri? = intent?.data val intentData: Uri? = intent?.data

View File

@ -2,16 +2,12 @@ package com.github.libretube.adapters
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.EditText
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.databinding.SearchhistoryRowBinding import com.github.libretube.databinding.SearchhistoryRowBinding
import com.github.libretube.fragments.SearchFragment
import com.github.libretube.preferences.PreferenceHelper import com.github.libretube.preferences.PreferenceHelper
class SearchHistoryAdapter( class SearchHistoryAdapter(
private var historyList: List<String>, private var historyList: List<String>
private val editText: EditText,
private val searchFragment: SearchFragment
) : ) :
RecyclerView.Adapter<SearchHistoryViewHolder>() { RecyclerView.Adapter<SearchHistoryViewHolder>() {
@ -37,8 +33,6 @@ class SearchHistoryAdapter(
} }
root.setOnClickListener { root.setOnClickListener {
editText.setText(historyQuery)
searchFragment.fetchSearch(historyQuery)
} }
} }
} }

View File

@ -2,15 +2,11 @@ package com.github.libretube.adapters
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.EditText
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.databinding.SearchsuggestionRowBinding import com.github.libretube.databinding.SearchsuggestionRowBinding
import com.github.libretube.fragments.SearchFragment
class SearchSuggestionsAdapter( class SearchSuggestionsAdapter(
private var suggestionsList: List<String>, private var suggestionsList: List<String>
private var editText: EditText,
private val searchFragment: SearchFragment
) : ) :
RecyclerView.Adapter<SearchSuggestionsViewHolder>() { RecyclerView.Adapter<SearchSuggestionsViewHolder>() {
@ -31,8 +27,6 @@ class SearchSuggestionsAdapter(
holder.binding.apply { holder.binding.apply {
suggestionText.text = suggestion suggestionText.text = suggestion
root.setOnClickListener { root.setOnClickListener {
editText.setText(suggestion)
searchFragment.fetchSearch(editText.text.toString())
} }
} }
} }

View File

@ -1,51 +1,30 @@
package com.github.libretube.fragments package com.github.libretube.fragments
import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.util.Log import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import android.widget.TextView.GONE
import android.widget.TextView.OnEditorActionListener
import android.widget.TextView.VISIBLE
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.adapters.SearchAdapter
import com.github.libretube.adapters.SearchHistoryAdapter
import com.github.libretube.adapters.SearchSuggestionsAdapter import com.github.libretube.adapters.SearchSuggestionsAdapter
import com.github.libretube.databinding.FragmentSearchBinding import com.github.libretube.databinding.FragmentSearchBinding
import com.github.libretube.preferences.PreferenceHelper
import com.github.libretube.preferences.PreferenceKeys
import com.github.libretube.util.RetrofitInstance import com.github.libretube.util.RetrofitInstance
import com.github.libretube.util.hideKeyboard
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import retrofit2.HttpException import retrofit2.HttpException
import java.io.IOException import java.io.IOException
class SearchFragment : Fragment() { class SearchFragment() : Fragment() {
private val TAG = "SearchFragment" private val TAG = "SearchFragment"
private lateinit var binding: FragmentSearchBinding private lateinit var binding: FragmentSearchBinding
private var apiSearchFilter = "all" private var query: String? = null
private var nextPage: String? = null
private var searchAdapter: SearchAdapter? = null
private var isLoading: Boolean = true
private var isFetchingSearch: Boolean = false
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
arguments?.let { query = arguments?.getString("query")
}
} }
override fun onCreateView( override fun onCreateView(
@ -60,103 +39,13 @@ class SearchFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
binding.clearSearchImageView.setOnClickListener { // fetch the search
binding.autoCompleteTextView.text.clear() fetchSuggestions(query!!)
binding.historyRecycler.adapter = null
showHistory()
}
binding.filterMenuImageView.setOnClickListener {
val filterOptions = arrayOf(
getString(R.string.all),
getString(R.string.videos),
getString(R.string.channels),
getString(R.string.playlists),
getString(R.string.music_songs),
getString(R.string.music_videos),
getString(R.string.music_albums),
getString(R.string.music_playlists)
)
MaterialAlertDialogBuilder(view.context)
.setTitle(getString(R.string.choose_filter))
.setItems(filterOptions) { _, id ->
apiSearchFilter = when (id) {
0 -> "all"
1 -> "videos"
2 -> "channels"
3 -> "playlists"
4 -> "music_songs"
5 -> "music_videos"
6 -> "music_albums"
7 -> "music_playlists"
else -> "all"
}
fetchSearch(binding.autoCompleteTextView.text.toString())
}
.setNegativeButton(getString(R.string.cancel), null)
.show()
}
// show search history
binding.historyRecycler.layoutManager = LinearLayoutManager(view.context)
showHistory()
binding.searchRecycler.layoutManager = GridLayoutManager(view.context, 1)
binding.autoCompleteTextView.requestFocus()
val imm =
requireContext().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.showSoftInput(binding.autoCompleteTextView, InputMethodManager.SHOW_IMPLICIT)
binding.autoCompleteTextView.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(
s: CharSequence?,
start: Int,
count: Int,
after: Int
) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
if (s.toString() != "") {
binding.searchRecycler.adapter = null
binding.searchRecycler.viewTreeObserver
.addOnScrollChangedListener {
if (!binding.searchRecycler.canScrollVertically(1)) {
fetchNextSearchItems(binding.autoCompleteTextView.text.toString())
}
}
fetchSuggestions(s.toString())
}
}
override fun afterTextChanged(s: Editable?) {
if (s!!.isEmpty()) {
binding.historyRecycler.adapter = null
showHistory()
}
}
})
binding.autoCompleteTextView.setOnEditorActionListener(
OnEditorActionListener { textView, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_SEARCH && textView.text.toString() != "") {
view.let { context?.hideKeyboard(it) }
binding.searchRecycler.visibility = VISIBLE
binding.historyRecycler.visibility = GONE
fetchSearch(binding.autoCompleteTextView.text.toString())
return@OnEditorActionListener true
}
false
}
)
} }
private fun fetchSuggestions(query: String) { private fun fetchSuggestions(query: String) {
fun run() { fun run() {
lifecycleScope.launchWhenCreated { lifecycleScope.launchWhenCreated {
binding.searchRecycler.visibility = GONE
binding.historyRecycler.visibility = VISIBLE
val response = try { val response = try {
RetrofitInstance.api.getSuggestions(query) RetrofitInstance.api.getSuggestions(query)
} catch (e: IOException) { } catch (e: IOException) {
@ -168,74 +57,17 @@ class SearchFragment : Fragment() {
return@launchWhenCreated return@launchWhenCreated
} }
// only load the suggestions if the input field didn't get cleared yet // only load the suggestions if the input field didn't get cleared yet
if (binding.autoCompleteTextView.text.toString() != "") { val suggestionsAdapter =
val suggestionsAdapter = SearchSuggestionsAdapter(
SearchSuggestionsAdapter( response
response,
binding.autoCompleteTextView,
this@SearchFragment
)
binding.historyRecycler.adapter = suggestionsAdapter
}
}
}
if (!isFetchingSearch) run()
}
fun fetchSearch(query: String) {
runOnUiThread {
binding.historyRecycler.visibility = GONE
}
lifecycleScope.launchWhenCreated {
isFetchingSearch = true
view?.let { context?.hideKeyboard(it) }
val response = try {
RetrofitInstance.api.getSearchResults(query, apiSearchFilter)
} catch (e: IOException) {
println(e)
Log.e(TAG, "IOException, you might not have internet connection $e")
return@launchWhenCreated
} catch (e: HttpException) {
Log.e(TAG, "HttpException, unexpected response")
return@launchWhenCreated
}
nextPage = response.nextpage
if (response.items!!.isNotEmpty()) {
runOnUiThread {
binding.searchRecycler.visibility = VISIBLE
searchAdapter = SearchAdapter(response.items, childFragmentManager)
binding.searchRecycler.adapter = searchAdapter
}
}
addToHistory(query)
isLoading = false
isFetchingSearch = false
}
}
private fun fetchNextSearchItems(query: String) {
lifecycleScope.launchWhenCreated {
if (!isLoading) {
isLoading = true
val response = try {
RetrofitInstance.api.getSearchResultsNextPage(
query,
apiSearchFilter,
nextPage!!
) )
} catch (e: IOException) { runOnUiThread {
println(e) binding.suggestionsRecycler.layoutManager = LinearLayoutManager(requireContext())
Log.e(TAG, "IOException, you might not have internet connection") binding.suggestionsRecycler.adapter = suggestionsAdapter
return@launchWhenCreated
} catch (e: HttpException) {
Log.e(TAG, "HttpException, unexpected response," + e.response())
return@launchWhenCreated
} }
nextPage = response.nextpage
searchAdapter?.updateItems(response.items!!)
isLoading = false
} }
} }
run()
} }
private fun Fragment?.runOnUiThread(action: () -> Unit) { private fun Fragment?.runOnUiThread(action: () -> Unit) {
@ -244,35 +76,9 @@ class SearchFragment : Fragment() {
activity?.runOnUiThread(action) activity?.runOnUiThread(action)
} }
override fun onResume() { override fun onDestroy() {
super.onResume() // remove the backstack entries
requireActivity().window.setSoftInputMode(SOFT_INPUT_STATE_ALWAYS_HIDDEN) findNavController().popBackStack(R.id.searchFragment, true)
} super.onDestroy()
override fun onStop() {
super.onStop()
view?.let { context?.hideKeyboard(it) }
}
private fun showHistory() {
binding.searchRecycler.visibility = GONE
val historyList = PreferenceHelper.getSearchHistory()
if (historyList.isNotEmpty()) {
binding.historyRecycler.adapter =
SearchHistoryAdapter(
historyList,
binding.autoCompleteTextView,
this
)
binding.historyRecycler.visibility = VISIBLE
}
}
private fun addToHistory(query: String) {
val searchHistoryEnabled =
PreferenceHelper.getBoolean(PreferenceKeys.SEARCH_HISTORY_TOGGLE, true)
if (searchHistoryEnabled && query != "") {
PreferenceHelper.saveToSearchHistory(query)
}
} }
} }

View File

@ -0,0 +1,137 @@
package com.github.libretube.fragments
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import com.github.libretube.R
import com.github.libretube.adapters.SearchAdapter
import com.github.libretube.databinding.FragmentSearchResultBinding
import com.github.libretube.util.RetrofitInstance
import com.github.libretube.util.hideKeyboard
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import retrofit2.HttpException
import java.io.IOException
class SearchResultFragment : Fragment() {
private val TAG = "SearchResultFragment"
private lateinit var binding: FragmentSearchResultBinding
private lateinit var nextPage: String
private var query: String = ""
private var apiSearchFilter: String = "all"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
query = arguments?.getString("query").toString()
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentSearchResultBinding.inflate(layoutInflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.filterMenuImageView.setOnClickListener {
val filterOptions = arrayOf(
getString(R.string.all),
getString(R.string.videos),
getString(R.string.channels),
getString(R.string.playlists),
getString(R.string.music_songs),
getString(R.string.music_videos),
getString(R.string.music_albums),
getString(R.string.music_playlists)
)
MaterialAlertDialogBuilder(requireContext())
.setTitle(getString(R.string.choose_filter))
.setItems(filterOptions) { _, id ->
apiSearchFilter = when (id) {
0 -> "all"
1 -> "videos"
2 -> "channels"
3 -> "playlists"
4 -> "music_songs"
5 -> "music_videos"
6 -> "music_albums"
7 -> "music_playlists"
else -> "all"
}
}
.setNegativeButton(getString(R.string.cancel), null)
.show()
}
fetchSearch()
binding.searchRecycler.viewTreeObserver
.addOnScrollChangedListener {
if (!binding.searchRecycler.canScrollVertically(1)) {
fetchNextSearchItems()
}
}
}
private fun fetchSearch() {
lifecycleScope.launchWhenCreated {
view?.let { context?.hideKeyboard(it) }
val response = try {
RetrofitInstance.api.getSearchResults(query, apiSearchFilter)
} catch (e: IOException) {
println(e)
Log.e(TAG, "IOException, you might not have internet connection $e")
return@launchWhenCreated
} catch (e: HttpException) {
Log.e(TAG, "HttpException, unexpected response")
return@launchWhenCreated
}
runOnUiThread {
if (response.items?.isNotEmpty() == true) {
binding.searchRecycler.layoutManager = LinearLayoutManager(requireContext())
binding.searchRecycler.adapter = SearchAdapter(response.items, childFragmentManager)
}
}
nextPage = response.nextpage!!
}
}
private fun fetchNextSearchItems() {
lifecycleScope.launchWhenCreated {
val response = try {
RetrofitInstance.api.getSearchResultsNextPage(
query,
apiSearchFilter,
nextPage
)
} catch (e: IOException) {
println(e)
Log.e(TAG, "IOException, you might not have internet connection")
return@launchWhenCreated
} catch (e: HttpException) {
Log.e(TAG, "HttpException, unexpected response," + e.response())
return@launchWhenCreated
}
nextPage = response.nextpage!!
with(binding.searchRecycler.adapter as SearchAdapter) {
if (response.items?.isNotEmpty() == true) this.updateItems(response.items)
}
}
}
private fun Fragment?.runOnUiThread(action: () -> Unit) {
this ?: return
if (!isAdded) return // Fragment not attached to an Activity
activity?.runOnUiThread(action)
}
}

View File

@ -1,101 +1,14 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout 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" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".fragments.SearchFragment"> tools:context=".fragments.SearchFragment">
<LinearLayout <androidx.recyclerview.widget.RecyclerView
android:id="@+id/searchbar_holder" android:id="@+id/suggestions_recycler"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent" android:layout_marginVertical="10dp" />
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.card.MaterialCardView </FrameLayout>
android:id="@+id/outlinedTextField"
style="@style/Widget.Material3.CardView.Filled"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:layout_weight="1"
app:cardCornerRadius="24dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginEnd="30dp"
android:background="@android:color/transparent"
app:hintEnabled="false">
<EditText
android:id="@+id/autoCompleteTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:hint="@string/search_hint"
android:imeOptions="actionSearch"
android:inputType="textFilter|textNoSuggestions"
android:maxLines="1"
android:padding="12dp" />
</com.google.android.material.textfield.TextInputLayout>
<ImageView
android:id="@+id/clearSearch_imageView"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginEnd="16dp"
android:src="@drawable/ic_close" />
</RelativeLayout>
</com.google.android.material.card.MaterialCardView>
<ImageView
android:id="@+id/filterMenu_imageView"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginTop="25dp"
android:layout_marginEnd="20dp"
android:layout_weight="0"
android:src="@drawable/ic_filter" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/history_recycler"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginVertical="10dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/searchbar_holder" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/search_recycler"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_margin="10dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/searchbar_holder" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".fragments.SearchFragment">
<ImageView
android:id="@+id/filterMenu_imageView"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginTop="25dp"
android:layout_marginEnd="20dp"
android:src="@drawable/ic_filter" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/search_recycler"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp" />
</LinearLayout>

View File

@ -6,7 +6,8 @@
android:id="@+id/action_search" android:id="@+id/action_search"
android:icon="@drawable/ic_search" android:icon="@drawable/ic_search"
android:title="@string/search_hint" android:title="@string/search_hint"
app:showAsAction="ifRoom" /> app:showAsAction="ifRoom"
app:actionViewClass="androidx.appcompat.widget.SearchView" />
<item <item
android:id="@+id/action_settings" android:id="@+id/action_settings"

View File

@ -19,12 +19,17 @@
android:id="@+id/libraryFragment" android:id="@+id/libraryFragment"
android:name="com.github.libretube.fragments.LibraryFragment" android:name="com.github.libretube.fragments.LibraryFragment"
android:label="fragment_library" android:label="fragment_library"
tools:layout="@layout/fragment_library"></fragment> tools:layout="@layout/fragment_library" />
<fragment <fragment
android:id="@+id/searchFragment" android:id="@+id/searchFragment"
android:name="com.github.libretube.fragments.SearchFragment" android:name="com.github.libretube.fragments.SearchFragment"
android:label="fragment_search" android:label="fragment_search"
tools:layout="@layout/fragment_search" /> tools:layout="@layout/fragment_search" />
<fragment
android:id="@+id/searchResultFragment"
android:name="com.github.libretube.fragments.SearchResultFragment"
android:label="fragment_search"
tools:layout="@layout/fragment_search_result" />
<fragment <fragment
android:id="@+id/channelFragment" android:id="@+id/channelFragment"
android:name="com.github.libretube.fragments.ChannelFragment" android:name="com.github.libretube.fragments.ChannelFragment"