diff --git a/app/src/main/java/ani/dantotsu/media/SearchActivity.kt b/app/src/main/java/ani/dantotsu/media/SearchActivity.kt index 4c71664b5d..e06e67c5d2 100644 --- a/app/src/main/java/ani/dantotsu/media/SearchActivity.kt +++ b/app/src/main/java/ani/dantotsu/media/SearchActivity.kt @@ -33,6 +33,7 @@ class SearchActivity : AppCompatActivity() { private lateinit var mediaAdaptor: MediaAdaptor private lateinit var progressAdapter: ProgressAdapter private lateinit var concatAdapter: ConcatAdapter + private lateinit var headerAdaptor: SearchAdapter lateinit var result: SearchResults lateinit var updateChips: (() -> Unit) @@ -76,7 +77,7 @@ class SearchActivity : AppCompatActivity() { progressAdapter = ProgressAdapter(searched = model.searched) mediaAdaptor = MediaAdaptor(style, model.searchResults.results, this, matchParent = true) - val headerAdaptor = SearchAdapter(this) + headerAdaptor = SearchAdapter(this, model.searchResults.type) val gridSize = (screenWidth / 120f).toInt() val gridLayoutManager = GridLayoutManager(this, gridSize) @@ -154,9 +155,15 @@ class SearchActivity : AppCompatActivity() { } } + fun emptyMediaAdapter() { + mediaAdaptor.notifyItemRangeRemoved(0, model.searchResults.results.size) + model.searchResults.results.clear() + } + private var searchTimer = Timer() private var loading = false fun search() { + headerAdaptor.setHistoryVisibility(false) val size = model.searchResults.results.size model.searchResults.results.clear() binding.searchRecyclerView.post { @@ -188,6 +195,7 @@ class SearchActivity : AppCompatActivity() { var state: Parcelable? = null override fun onPause() { + headerAdaptor.addHistory() super.onPause() state = binding.searchRecyclerView.layoutManager?.onSaveInstanceState() } diff --git a/app/src/main/java/ani/dantotsu/media/SearchAdapter.kt b/app/src/main/java/ani/dantotsu/media/SearchAdapter.kt index 43c8f34438..da28862da7 100644 --- a/app/src/main/java/ani/dantotsu/media/SearchAdapter.kt +++ b/app/src/main/java/ani/dantotsu/media/SearchAdapter.kt @@ -2,6 +2,7 @@ package ani.dantotsu.media import android.annotation.SuppressLint import android.content.Context +import android.content.SharedPreferences import android.graphics.drawable.Drawable import android.text.Editable import android.text.TextWatcher @@ -9,6 +10,8 @@ import android.view.LayoutInflater import android.view.MotionEvent import android.view.View import android.view.ViewGroup +import android.view.animation.AlphaAnimation +import android.view.animation.Animation import android.view.inputmethod.EditorInfo import android.view.inputmethod.InputMethodManager import androidx.appcompat.app.AppCompatActivity @@ -22,16 +25,26 @@ import ani.dantotsu.connections.anilist.Anilist import ani.dantotsu.currContext import ani.dantotsu.databinding.ItemChipBinding import ani.dantotsu.databinding.ItemSearchHeaderBinding +import ani.dantotsu.logger +import ani.dantotsu.others.SharedPreferenceStringSetLiveData import ani.dantotsu.saveData import com.google.android.material.checkbox.MaterialCheckBox.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get -class SearchAdapter(private val activity: SearchActivity) : +class SearchAdapter(private val activity: SearchActivity, private val type: String) : RecyclerView.Adapter() { private val itemViewType = 6969 var search: Runnable? = null var requestFocus: Runnable? = null private var textWatcher: TextWatcher? = null + private lateinit var searchHistoryAdapter: SearchHistoryAdapter + private lateinit var binding: ItemSearchHeaderBinding override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchHeaderViewHolder { val binding = @@ -41,8 +54,11 @@ class SearchAdapter(private val activity: SearchActivity) : @SuppressLint("ClickableViewAccessibility") override fun onBindViewHolder(holder: SearchHeaderViewHolder, position: Int) { - val binding = holder.binding + binding = holder.binding + searchHistoryAdapter = SearchHistoryAdapter(type) { s -> logger(s) } + binding.searchHistoryList.layoutManager = LinearLayoutManager(binding.root.context) + binding.searchHistoryList.adapter = searchHistoryAdapter val imm: InputMethodManager = activity.getSystemService(AppCompatActivity.INPUT_METHOD_SERVICE) as InputMethodManager @@ -104,7 +120,18 @@ class SearchAdapter(private val activity: SearchActivity) : override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { - searchTitle() + if (s.toString().isBlank()) { + activity.emptyMediaAdapter() + CoroutineScope(Dispatchers.IO).launch { + delay(200) + activity.runOnUiThread { + setHistoryVisibility(true) + } + } + } else { + setHistoryVisibility(false) + searchTitle() + } } } binding.searchBarText.addTextChangedListener(textWatcher) @@ -177,6 +204,40 @@ class SearchAdapter(private val activity: SearchActivity) : requestFocus = Runnable { binding.searchBarText.requestFocus() } } + fun setHistoryVisibility(visible: Boolean) { + if (visible) { + binding.searchResultLayout.startAnimation(fadeOutAnimation()) + binding.searchHistoryList.startAnimation(fadeInAnimation()) + binding.searchResultLayout.visibility = View.GONE + binding.searchHistoryList.visibility = View.VISIBLE + } else { + if (binding.searchResultLayout.visibility != View.VISIBLE) { + binding.searchResultLayout.startAnimation(fadeInAnimation()) + binding.searchHistoryList.startAnimation(fadeOutAnimation()) + } + binding.searchResultLayout.visibility = View.VISIBLE + binding.searchHistoryList.visibility = View.GONE + } + } + + private fun fadeInAnimation(): Animation { + return AlphaAnimation(0f, 1f).apply { + duration = 150 + fillAfter = true + } + } + + private fun fadeOutAnimation(): Animation { + return AlphaAnimation(1f, 0f).apply { + duration = 150 + fillAfter = true + } + } + + + fun addHistory() { + searchHistoryAdapter.add(binding.searchBarText.text.toString()) + } override fun getItemCount(): Int = 1 diff --git a/app/src/main/java/ani/dantotsu/media/SearchHistoryAdapter.kt b/app/src/main/java/ani/dantotsu/media/SearchHistoryAdapter.kt new file mode 100644 index 0000000000..f350f75895 --- /dev/null +++ b/app/src/main/java/ani/dantotsu/media/SearchHistoryAdapter.kt @@ -0,0 +1,93 @@ +package ani.dantotsu.media + +import android.content.SharedPreferences +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import ani.dantotsu.R +import ani.dantotsu.databinding.ItemSearchHistoryBinding +import ani.dantotsu.others.SharedPreferenceStringSetLiveData +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get + +class SearchHistoryAdapter(private val type: String, private val searchClicked: (String) -> Unit) : ListAdapter( + DIFF_CALLBACK_INSTALLED +) { + private var searchHistoryLiveData: SharedPreferenceStringSetLiveData? = null + private var searchHistory: MutableSet? = null + private var sharedPreferences: SharedPreferences? = null + + init { + sharedPreferences = Injekt.get() + searchHistoryLiveData = SharedPreferenceStringSetLiveData( + sharedPreferences!!, + "searchHistory_$type", + mutableSetOf() + ) + searchHistoryLiveData?.observeForever { + searchHistory = it.toMutableSet() + submitList(searchHistory?.reversed()) + } + } + + fun remove(item: String) { + searchHistory?.remove(item) + sharedPreferences?.edit()?.putStringSet("searchHistory_$type", searchHistory)?.apply() + } + + fun add(item: String) { + if (searchHistory?.contains(item) == true || item.isBlank()) return + if (sharedPreferences?.getBoolean("incognito", false) == true) return + searchHistory?.add(item) + sharedPreferences?.edit()?.putStringSet("searchHistory_$type", searchHistory)?.apply() + } + + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): SearchHistoryAdapter.SearchHistoryViewHolder { + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.item_search_history, parent, false) + return SearchHistoryViewHolder(view) + } + + override fun onBindViewHolder( + holder: SearchHistoryAdapter.SearchHistoryViewHolder, + position: Int + ) { + holder.binding.searchHistoryTextView.text = getItem(position) + holder.binding.closeTextView.setOnClickListener { + if (position >= itemCount || position < 0) return@setOnClickListener + remove(getItem(position)) + } + holder.binding.searchHistoryTextView.setOnClickListener { + if (position >= itemCount || position < 0) return@setOnClickListener + searchClicked(getItem(position)) + } + } + + inner class SearchHistoryViewHolder(view: View) : RecyclerView.ViewHolder(view) { + val binding = ItemSearchHistoryBinding.bind(view) + } + + companion object { + val DIFF_CALLBACK_INSTALLED = object : DiffUtil.ItemCallback() { + override fun areItemsTheSame( + oldItem: String, + newItem: String + ): Boolean { + return oldItem == newItem + } + + override fun areContentsTheSame( + oldItem: String, + newItem: String + ): Boolean { + return oldItem == newItem + } + } + } +} diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 2966ae1c88..0b731292e0 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -36,6 +36,7 @@ android:layout_margin="16dp" android:translationZ="7dp" app:cardBackgroundColor="@color/bg_opp" + android:visibility="gone" app:cardCornerRadius="16dp"> - + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_search_history.xml b/app/src/main/res/layout/item_search_history.xml new file mode 100644 index 0000000000..d9d703c914 --- /dev/null +++ b/app/src/main/res/layout/item_search_history.xml @@ -0,0 +1,34 @@ + + + + + + + + + +