From b1d9694432590c87c47932b28407b6710fa5945e Mon Sep 17 00:00:00 2001 From: Bnyro Date: Fri, 10 Jun 2022 10:58:33 +0200 Subject: [PATCH 1/3] search suggestions rewrite --- .../adapters/SearchHistoryAdapter.kt | 4 +- .../adapters/SearchSuggestionsAdapter.kt | 40 +++++++++ .../libretube/fragments/SearchFragment.kt | 82 ++++++++----------- app/src/main/res/layout/fragment_search.xml | 3 +- .../main/res/layout/searchsuggestion_row.xml | 31 +++++++ 5 files changed, 110 insertions(+), 50 deletions(-) create mode 100644 app/src/main/java/com/github/libretube/adapters/SearchSuggestionsAdapter.kt create mode 100644 app/src/main/res/layout/searchsuggestion_row.xml diff --git a/app/src/main/java/com/github/libretube/adapters/SearchHistoryAdapter.kt b/app/src/main/java/com/github/libretube/adapters/SearchHistoryAdapter.kt index 2de707573..13d07cfee 100644 --- a/app/src/main/java/com/github/libretube/adapters/SearchHistoryAdapter.kt +++ b/app/src/main/java/com/github/libretube/adapters/SearchHistoryAdapter.kt @@ -4,7 +4,7 @@ import android.content.Context import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.AutoCompleteTextView +import android.widget.EditText import android.widget.TextView import androidx.preference.PreferenceManager import androidx.recyclerview.widget.RecyclerView @@ -14,7 +14,7 @@ import com.google.android.material.imageview.ShapeableImageView class SearchHistoryAdapter( private val context: Context, private var historyList: List, - private val editText: AutoCompleteTextView + private val editText: EditText ) : RecyclerView.Adapter() { diff --git a/app/src/main/java/com/github/libretube/adapters/SearchSuggestionsAdapter.kt b/app/src/main/java/com/github/libretube/adapters/SearchSuggestionsAdapter.kt new file mode 100644 index 000000000..9762f7fd5 --- /dev/null +++ b/app/src/main/java/com/github/libretube/adapters/SearchSuggestionsAdapter.kt @@ -0,0 +1,40 @@ +package com.github.libretube.adapters + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.EditText +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.github.libretube.R + +class SearchSuggestionsAdapter( + private var suggestionsList: List, + private var autoCompleteTextView: EditText +) : + RecyclerView.Adapter() { + + override fun getItemCount(): Int { + return suggestionsList.size + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchSuggestionsViewHolder { + val layoutInflater = LayoutInflater.from(parent.context) + val cell = layoutInflater.inflate(R.layout.searchsuggestion_row, parent, false) + return SearchSuggestionsViewHolder(cell) + } + + override fun onBindViewHolder(holder: SearchSuggestionsViewHolder, position: Int) { + val suggestion = suggestionsList[position] + val suggestionTextView = holder.v.findViewById(R.id.suggestion_text) + suggestionTextView.text = suggestion + holder.v.setOnClickListener { + autoCompleteTextView.setText(suggestion) + } + } +} + +class SearchSuggestionsViewHolder(val v: View) : RecyclerView.ViewHolder(v) { + init { + } +} diff --git a/app/src/main/java/com/github/libretube/fragments/SearchFragment.kt b/app/src/main/java/com/github/libretube/fragments/SearchFragment.kt index fd7a36619..308f43cd5 100644 --- a/app/src/main/java/com/github/libretube/fragments/SearchFragment.kt +++ b/app/src/main/java/com/github/libretube/fragments/SearchFragment.kt @@ -1,7 +1,6 @@ package com.github.libretube.fragments import android.content.Context -import android.content.DialogInterface import android.os.Bundle import android.text.Editable import android.text.TextWatcher @@ -12,8 +11,7 @@ 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.ArrayAdapter -import android.widget.AutoCompleteTextView +import android.widget.EditText import android.widget.ImageView import android.widget.TextView.GONE import android.widget.TextView.OnEditorActionListener @@ -27,14 +25,14 @@ import androidx.recyclerview.widget.RecyclerView 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.hideKeyboard import com.github.libretube.util.RetrofitInstance import com.google.android.material.dialog.MaterialAlertDialogBuilder -import java.io.IOException import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.delay import kotlinx.coroutines.launch import retrofit2.HttpException +import java.io.IOException class SearchFragment : Fragment() { private val TAG = "SearchFragment" @@ -42,6 +40,7 @@ class SearchFragment : Fragment() { private var apiSearchFilter = "all" private var nextPage: String? = null private lateinit var searchRecView: RecyclerView + private lateinit var historyRecView: RecyclerView private var searchAdapter: SearchAdapter? = null private var isLoading: Boolean = true @@ -62,11 +61,11 @@ class SearchFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - searchRecView = view.findViewById(R.id.search_recycler) + searchRecView = view.findViewById(R.id.search_recycler) + historyRecView = view.findViewById(R.id.history_recycler) - val autoTextView = view.findViewById(R.id.autoCompleteTextView) + val autoTextView = view.findViewById(R.id.autoCompleteTextView) val clearSearchButton = view.findViewById(R.id.clearSearch_imageView) - val historyRecycler = view.findViewById(R.id.history_recycler) val filterImageView = view.findViewById(R.id.filterMenu_imageView) var tempSelectedItem = 0 @@ -92,30 +91,26 @@ class SearchFragment : Fragment() { MaterialAlertDialogBuilder(view.context) .setTitle(getString(R.string.choose_filter)) - .setSingleChoiceItems( - filterOptions, selectedFilter, - DialogInterface.OnClickListener { _, id -> - tempSelectedItem = id - } - ) + .setSingleChoiceItems(filterOptions, selectedFilter) { _, id -> + tempSelectedItem = id + } .setPositiveButton( getString(R.string.okay), - DialogInterface.OnClickListener { _, _ -> - selectedFilter = tempSelectedItem - apiSearchFilter = when (selectedFilter) { - 0 -> "all" - 1 -> "videos" - 2 -> "channels" - 3 -> "playlists" - 4 -> "music_songs" - 5 -> "music_videos" - 6 -> "music_albums" - 7 -> "music_playlists" - else -> "all" - } - fetchSearch(autoTextView.text.toString()) + ) { _, _ -> + selectedFilter = tempSelectedItem + apiSearchFilter = when (selectedFilter) { + 0 -> "all" + 1 -> "videos" + 2 -> "channels" + 3 -> "playlists" + 4 -> "music_songs" + 5 -> "music_videos" + 6 -> "music_albums" + 7 -> "music_playlists" + else -> "all" } - ) + fetchSearch(autoTextView.text.toString()) + } .setNegativeButton(getString(R.string.cancel), null) .create() .show() @@ -124,13 +119,13 @@ class SearchFragment : Fragment() { // show search history searchRecView.visibility = GONE - historyRecycler.visibility = VISIBLE + historyRecView.visibility = VISIBLE - historyRecycler.layoutManager = LinearLayoutManager(view.context) + historyRecView.layoutManager = LinearLayoutManager(view.context) val historyList = getHistory() if (historyList.isNotEmpty()) { - historyRecycler.adapter = + historyRecView.adapter = SearchHistoryAdapter(requireContext(), historyList, autoTextView) } @@ -150,8 +145,6 @@ class SearchFragment : Fragment() { override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { if (s!! != "") { - searchRecView.visibility = VISIBLE - historyRecycler.visibility = GONE searchRecView.adapter = null searchRecView.viewTreeObserver @@ -163,8 +156,6 @@ class SearchFragment : Fragment() { GlobalScope.launch { fetchSuggestions(s.toString(), autoTextView) - delay(1000) - fetchSearch(s.toString()) } } } @@ -172,10 +163,10 @@ class SearchFragment : Fragment() { override fun afterTextChanged(s: Editable?) { if (s!!.isEmpty()) { searchRecView.visibility = GONE - historyRecycler.visibility = VISIBLE + historyRecView.visibility = GONE val historyList = getHistory() if (historyList.isNotEmpty()) { - historyRecycler.adapter = + historyRecView.adapter = SearchHistoryAdapter(requireContext(), historyList, autoTextView) } } @@ -185,7 +176,9 @@ class SearchFragment : Fragment() { OnEditorActionListener { _, actionId, _ -> if (actionId == EditorInfo.IME_ACTION_SEARCH) { hideKeyboard() - autoTextView.dismissDropDown() + searchRecView.visibility = VISIBLE + historyRecView.visibility = GONE + fetchSearch(autoTextView.text.toString()) if (sharedPreferences.getBoolean( "search_history_toggle", true @@ -199,12 +192,9 @@ class SearchFragment : Fragment() { false } ) - autoTextView.setOnItemClickListener { _, _, _, _ -> - hideKeyboard() - } } - private fun fetchSuggestions(query: String, autoTextView: AutoCompleteTextView) { + private fun fetchSuggestions(query: String, autoTextView: EditText) { lifecycleScope.launchWhenCreated { val response = try { RetrofitInstance.api.getSuggestions(query) @@ -216,9 +206,9 @@ class SearchFragment : Fragment() { Log.e(TAG, "HttpException, unexpected response") return@launchWhenCreated } - val adapter = - ArrayAdapter(requireContext(), android.R.layout.simple_list_item_1, response) - autoTextView.setAdapter(adapter) + historyRecView.visibility = VISIBLE + val suggestionsAdapter = SearchSuggestionsAdapter(response, autoTextView) + historyRecView.adapter = suggestionsAdapter } } diff --git a/app/src/main/res/layout/fragment_search.xml b/app/src/main/res/layout/fragment_search.xml index 4dd3a2f5c..cf46d6d0b 100644 --- a/app/src/main/res/layout/fragment_search.xml +++ b/app/src/main/res/layout/fragment_search.xml @@ -42,12 +42,11 @@ android:background="@android:color/transparent" app:hintEnabled="false"> - + + + + + + + \ No newline at end of file From 587bb9d5531190cb154eaeea8f9522a194afc5b9 Mon Sep 17 00:00:00 2001 From: Bnyro Date: Fri, 10 Jun 2022 14:22:32 +0200 Subject: [PATCH 2/3] functionality and improvements --- .../adapters/SearchHistoryAdapter.kt | 11 +- .../adapters/SearchSuggestionsAdapter.kt | 7 +- .../libretube/fragments/SearchFragment.kt | 117 +++++++++--------- 3 files changed, 68 insertions(+), 67 deletions(-) diff --git a/app/src/main/java/com/github/libretube/adapters/SearchHistoryAdapter.kt b/app/src/main/java/com/github/libretube/adapters/SearchHistoryAdapter.kt index 13d07cfee..7dfdeaa72 100644 --- a/app/src/main/java/com/github/libretube/adapters/SearchHistoryAdapter.kt +++ b/app/src/main/java/com/github/libretube/adapters/SearchHistoryAdapter.kt @@ -9,12 +9,14 @@ import android.widget.TextView import androidx.preference.PreferenceManager import androidx.recyclerview.widget.RecyclerView import com.github.libretube.R +import com.github.libretube.fragments.SearchFragment import com.google.android.material.imageview.ShapeableImageView class SearchHistoryAdapter( private val context: Context, private var historyList: List, - private val editText: EditText + private val editText: EditText, + private val searchFragment: SearchFragment ) : RecyclerView.Adapter() { @@ -34,17 +36,14 @@ class SearchHistoryAdapter( holder.v.findViewById(R.id.delete_history).setOnClickListener { val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) - historyList = historyList - history - - sharedPreferences.edit().putStringSet("search_history", HashSet(historyList)) - .apply() - + sharedPreferences.edit().putStringSet("search_history", HashSet(historyList)).apply() notifyDataSetChanged() } holder.v.setOnClickListener { editText.setText(history) + searchFragment.fetchSearch(history) } } } diff --git a/app/src/main/java/com/github/libretube/adapters/SearchSuggestionsAdapter.kt b/app/src/main/java/com/github/libretube/adapters/SearchSuggestionsAdapter.kt index 9762f7fd5..fb899db41 100644 --- a/app/src/main/java/com/github/libretube/adapters/SearchSuggestionsAdapter.kt +++ b/app/src/main/java/com/github/libretube/adapters/SearchSuggestionsAdapter.kt @@ -7,10 +7,12 @@ import android.widget.EditText import android.widget.TextView import androidx.recyclerview.widget.RecyclerView import com.github.libretube.R +import com.github.libretube.fragments.SearchFragment class SearchSuggestionsAdapter( private var suggestionsList: List, - private var autoCompleteTextView: EditText + private var editText: EditText, + private val searchFragment: SearchFragment ) : RecyclerView.Adapter() { @@ -29,7 +31,8 @@ class SearchSuggestionsAdapter( val suggestionTextView = holder.v.findViewById(R.id.suggestion_text) suggestionTextView.text = suggestion holder.v.setOnClickListener { - autoCompleteTextView.setText(suggestion) + editText.setText(suggestion) + searchFragment.fetchSearch(editText.text.toString()) } } } diff --git a/app/src/main/java/com/github/libretube/fragments/SearchFragment.kt b/app/src/main/java/com/github/libretube/fragments/SearchFragment.kt index 308f43cd5..0cde9d183 100644 --- a/app/src/main/java/com/github/libretube/fragments/SearchFragment.kt +++ b/app/src/main/java/com/github/libretube/fragments/SearchFragment.kt @@ -41,8 +41,10 @@ class SearchFragment : Fragment() { private var nextPage: String? = null private lateinit var searchRecView: RecyclerView private lateinit var historyRecView: RecyclerView + private lateinit var autoTextView: EditText private var searchAdapter: SearchAdapter? = null private var isLoading: Boolean = true + private var isFetchingSearch: Boolean = false override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -63,16 +65,13 @@ class SearchFragment : Fragment() { super.onViewCreated(view, savedInstanceState) searchRecView = view.findViewById(R.id.search_recycler) historyRecView = view.findViewById(R.id.history_recycler) + autoTextView = view.findViewById(R.id.autoCompleteTextView) - val autoTextView = view.findViewById(R.id.autoCompleteTextView) val clearSearchButton = view.findViewById(R.id.clearSearch_imageView) val filterImageView = view.findViewById(R.id.filterMenu_imageView) var tempSelectedItem = 0 - val sharedPreferences = - PreferenceManager.getDefaultSharedPreferences(requireContext()) - clearSearchButton.setOnClickListener { autoTextView.text.clear() } @@ -117,23 +116,15 @@ class SearchFragment : Fragment() { } // show search history - - searchRecView.visibility = GONE - historyRecView.visibility = VISIBLE - historyRecView.layoutManager = LinearLayoutManager(view.context) - - val historyList = getHistory() - if (historyList.isNotEmpty()) { - historyRecView.adapter = - SearchHistoryAdapter(requireContext(), historyList, autoTextView) - } + showHistory() searchRecView.layoutManager = GridLayoutManager(view.context, 1) autoTextView.requestFocus() val imm = requireContext().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager imm.showSoftInput(autoTextView, InputMethodManager.SHOW_IMPLICIT) + autoTextView.addTextChangedListener(object : TextWatcher { override fun beforeTextChanged( s: CharSequence?, @@ -162,13 +153,7 @@ class SearchFragment : Fragment() { override fun afterTextChanged(s: Editable?) { if (s!!.isEmpty()) { - searchRecView.visibility = GONE - historyRecView.visibility = GONE - val historyList = getHistory() - if (historyList.isNotEmpty()) { - historyRecView.adapter = - SearchHistoryAdapter(requireContext(), historyList, autoTextView) - } + showHistory() } } }) @@ -179,14 +164,6 @@ class SearchFragment : Fragment() { searchRecView.visibility = VISIBLE historyRecView.visibility = GONE fetchSearch(autoTextView.text.toString()) - if (sharedPreferences.getBoolean( - "search_history_toggle", - true - ) - ) { - val newString = autoTextView.text.toString() - addToHistory(newString) - } return@OnEditorActionListener true } false @@ -195,25 +172,33 @@ class SearchFragment : Fragment() { } private fun fetchSuggestions(query: String, autoTextView: EditText) { - lifecycleScope.launchWhenCreated { - val response = try { - RetrofitInstance.api.getSuggestions(query) - } 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") - return@launchWhenCreated + fun run() { + lifecycleScope.launchWhenCreated { + searchRecView.visibility = GONE + historyRecView.visibility = VISIBLE + val response = try { + RetrofitInstance.api.getSuggestions(query) + } 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") + return@launchWhenCreated + } + val suggestionsAdapter = + SearchSuggestionsAdapter(response, autoTextView, this@SearchFragment) + historyRecView.adapter = suggestionsAdapter } - historyRecView.visibility = VISIBLE - val suggestionsAdapter = SearchSuggestionsAdapter(response, autoTextView) - historyRecView.adapter = suggestionsAdapter } + if (!isFetchingSearch) run() } - private fun fetchSearch(query: String) { + fun fetchSearch(query: String) { lifecycleScope.launchWhenCreated { + isFetchingSearch = true + hideKeyboard() + Log.e("here", "here") val response = try { RetrofitInstance.api.getSearchResults(query, apiSearchFilter) } catch (e: IOException) { @@ -227,11 +212,15 @@ class SearchFragment : Fragment() { nextPage = response.nextpage if (response.items!!.isNotEmpty()) { runOnUiThread { + historyRecView.visibility = GONE + searchRecView.visibility = VISIBLE searchAdapter = SearchAdapter(response.items, childFragmentManager) searchRecView.adapter = searchAdapter } } + addToHistory(query) isLoading = false + isFetchingSearch = false } } @@ -276,27 +265,37 @@ class SearchFragment : Fragment() { hideKeyboard() } + private fun showHistory() { + searchRecView.visibility = GONE + val historyList = getHistory() + if (historyList.isNotEmpty()) { + historyRecView.adapter = + SearchHistoryAdapter(requireContext(), historyList, autoTextView, this) + historyRecView.visibility = VISIBLE + } + } + private fun addToHistory(query: String) { val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext()) + val searchHistoryEnabled = sharedPreferences.getBoolean("search_history_toggle", true) + if (searchHistoryEnabled) { + var historyList = getHistory() - var historyList = getHistory() + if ((historyList.isNotEmpty() && historyList.contains(query)) || query == "") { + return + } else { + historyList = historyList + query + } - if (historyList.isNotEmpty() && query == historyList[historyList.size - 1]) { - return - } else if (query == "") { - return - } else { - historyList = historyList + query + if (historyList.size > 10) { + historyList = historyList.takeLast(10) + } + + val set: Set = HashSet(historyList) + + sharedPreferences.edit().putStringSet("search_history", set) + .apply() } - - if (historyList.size > 10) { - historyList = historyList.takeLast(10) - } - - val set: Set = HashSet(historyList) - - sharedPreferences.edit().putStringSet("search_history", set) - .apply() } private fun getHistory(): List { From 1c566bdb1ce5c851ab742cffce693abba572d210 Mon Sep 17 00:00:00 2001 From: Bnyro Date: Fri, 10 Jun 2022 14:25:06 +0200 Subject: [PATCH 3/3] ktlint --- .../main/java/com/github/libretube/fragments/SearchFragment.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/github/libretube/fragments/SearchFragment.kt b/app/src/main/java/com/github/libretube/fragments/SearchFragment.kt index 0cde9d183..63ddd43e4 100644 --- a/app/src/main/java/com/github/libretube/fragments/SearchFragment.kt +++ b/app/src/main/java/com/github/libretube/fragments/SearchFragment.kt @@ -29,10 +29,10 @@ import com.github.libretube.adapters.SearchSuggestionsAdapter import com.github.libretube.hideKeyboard import com.github.libretube.util.RetrofitInstance import com.google.android.material.dialog.MaterialAlertDialogBuilder +import java.io.IOException import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import retrofit2.HttpException -import java.io.IOException class SearchFragment : Fragment() { private val TAG = "SearchFragment"