diff --git a/app/src/main/java/net/pokeranalytics/android/ui/extensions/UIExtensions.kt b/app/src/main/java/net/pokeranalytics/android/ui/extensions/UIExtensions.kt index b5bb2f08..ce7a444a 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/extensions/UIExtensions.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/extensions/UIExtensions.kt @@ -7,9 +7,11 @@ import android.content.res.Resources import android.net.Uri import android.util.TypedValue import android.view.View +import android.widget.LinearLayout import android.widget.Toast import androidx.appcompat.app.AlertDialog import androidx.appcompat.widget.AppCompatTextView +import androidx.appcompat.widget.SearchView import androidx.browser.customtabs.CustomTabsIntent import androidx.core.content.ContextCompat import androidx.core.content.FileProvider @@ -160,4 +162,12 @@ fun View.showWithAnimation() { fun View.addCircleRipple() = with(TypedValue()) { context.theme.resolveAttribute(android.R.attr.selectableItemBackgroundBorderless, this, true) setBackgroundResource(resourceId) +} + +fun SearchView.removeMargins() { + val searchEditFrame = findViewById(R.id.search_edit_frame) + val layoutParams = searchEditFrame?.layoutParams as LinearLayout.LayoutParams? + layoutParams?.leftMargin = 0 + layoutParams?.rightMargin = 0 + searchEditFrame?.layoutParams = layoutParams } \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/DataListFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/DataListFragment.kt index 13c4a9fc..1bd58013 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/DataListFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/DataListFragment.kt @@ -3,9 +3,8 @@ package net.pokeranalytics.android.ui.fragment import android.app.Activity import android.content.Intent import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup +import android.view.* +import androidx.appcompat.widget.SearchView import androidx.core.view.isVisible import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager @@ -23,13 +22,17 @@ import net.pokeranalytics.android.ui.activity.FiltersActivity import net.pokeranalytics.android.ui.adapter.LiveRowRepresentableDataSource import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate +import net.pokeranalytics.android.ui.extensions.removeMargins import net.pokeranalytics.android.ui.fragment.components.DeletableItemFragment import net.pokeranalytics.android.ui.helpers.SwipeToDeleteCallback import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowViewType +import net.pokeranalytics.android.util.extensions.find import net.pokeranalytics.android.util.extensions.sorted + + open class DataListFragment : DeletableItemFragment(), LiveRowRepresentableDataSource, RowRepresentableDelegate { companion object { @@ -40,6 +43,14 @@ open class DataListFragment : DeletableItemFragment(), LiveRowRepresentableDataS private lateinit var dataType: LiveData private lateinit var items: RealmResults + private var dataListMenu: Menu? = null + + var isSearchable: Boolean = false + set(value) { + field = value + val searchMenuItem = dataListMenu?.findItem(R.id.action_search) + searchMenuItem?.isVisible = value + } /** * Set fragment data @@ -51,13 +62,18 @@ open class DataListFragment : DeletableItemFragment(), LiveRowRepresentableDataS setToolbarTitle(this.dataType.pluralLocalizedTitle(requireContext())) this.items = this.retrieveItems(getRealm()) + + isSearchable = when(this.dataType) { + LiveData.PLAYER, LiveData.LOCATION -> true + else -> false + } } open fun retrieveItems(realm: Realm): RealmResults { - return realm.sorted(this.identifiableClass, editableOnly = true, filterableTypeUniqueIdentifier = dataType.subType) + return realm.sorted(this.identifiableClass, editableOnly = true, filterableTypeUniqueIdentifier = dataType.subType) } - override fun deletableItems() : List { + override fun deletableItems(): List { return this.items } @@ -71,44 +87,31 @@ open class DataListFragment : DeletableItemFragment(), LiveRowRepresentableDataS initUI() } - /** - * Init UI - */ - private fun initUI() { + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { - setDisplayHomeAsUpEnabled(true) + menu.clear() + inflater.inflate(R.menu.toolbar_data_list, menu) + this.dataListMenu = menu - val viewManager = LinearLayoutManager(requireContext()) - dataListAdapter = RowRepresentableAdapter(this, this) + val searchMenuItem = menu.findItem(R.id.action_search) + searchMenuItem.isVisible = isSearchable - val swipeToDelete = SwipeToDeleteCallback(dataListAdapter) { position -> - val item = this.items[position] - if (item != null) { - val itemId = item.id - deleteItem(dataListAdapter, items, itemId) - } else { - throw PAIllegalStateException("Item with position $position not found") + val searchView = searchMenuItem.actionView as SearchView + searchView.removeMargins() + searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { + override fun onQueryTextSubmit(query: String?): Boolean { + return false } - } - - val itemTouchHelper = ItemTouchHelper(swipeToDelete) - - recyclerView.apply { - setHasFixedSize(true) - layoutManager = viewManager - adapter = dataListAdapter - itemTouchHelper.attachToRecyclerView(this) - } + override fun onQueryTextChange(newText: String?): Boolean { + filterItemsWithSearch(newText) + return false + } + }) - this.addButton.setOnClickListener { - EditableDataActivity.newInstance( - requireContext(), - dataType = this.dataType.ordinal, - primaryKey = null - ) - } + super.onCreateOptionsMenu(menu, inflater) } + override fun onResume() { super.onResume() this.recyclerView?.adapter?.notifyDataSetChanged() @@ -143,6 +146,52 @@ open class DataListFragment : DeletableItemFragment(), LiveRowRepresentableDataS } } + /** + * Init UI + */ + private fun initUI() { + + setDisplayHomeAsUpEnabled(true) + + val viewManager = LinearLayoutManager(requireContext()) + dataListAdapter = RowRepresentableAdapter(this, this) + + val swipeToDelete = SwipeToDeleteCallback(dataListAdapter) { position -> + val item = this.items[position] + if (item != null) { + val itemId = item.id + deleteItem(dataListAdapter, items, itemId) + } else { + throw PAIllegalStateException("Item with position $position not found") + } + } + + val itemTouchHelper = ItemTouchHelper(swipeToDelete) + + recyclerView.apply { + setHasFixedSize(true) + layoutManager = viewManager + adapter = dataListAdapter + itemTouchHelper.attachToRecyclerView(this) + } + + this.addButton.setOnClickListener { + EditableDataActivity.newInstance( + requireContext(), + dataType = this.dataType.ordinal, + primaryKey = null + ) + } + } + + /** + * Filter the items list with the given search content + */ + private fun filterItemsWithSearch(searchContent: String?) { + this.items = getRealm().find(this.identifiableClass, searchContent) + dataListAdapter.notifyDataSetChanged() + } + /** * Update UI */ diff --git a/app/src/main/java/net/pokeranalytics/android/util/extensions/RealmExtensions.kt b/app/src/main/java/net/pokeranalytics/android/util/extensions/RealmExtensions.kt index 7a2b5687..caf5f7a4 100644 --- a/app/src/main/java/net/pokeranalytics/android/util/extensions/RealmExtensions.kt +++ b/app/src/main/java/net/pokeranalytics/android/util/extensions/RealmExtensions.kt @@ -1,16 +1,10 @@ package net.pokeranalytics.android.util.extensions -import io.realm.Realm -import io.realm.RealmModel -import io.realm.RealmResults -import io.realm.Sort +import io.realm.* import net.pokeranalytics.android.model.interfaces.CountableUsage import net.pokeranalytics.android.model.interfaces.Identifiable import net.pokeranalytics.android.model.interfaces.NameManageable -import net.pokeranalytics.android.model.realm.Filter -import net.pokeranalytics.android.model.realm.TournamentFeature -import net.pokeranalytics.android.model.realm.Transaction -import net.pokeranalytics.android.model.realm.TransactionType +import net.pokeranalytics.android.model.realm.* fun Realm.count(clazz: Class) : Long { return this.where(clazz).count() @@ -106,5 +100,22 @@ fun Realm.updateUsageCount(clazz: Class) { countable.useCount = count } } +} + +/** + * Returns all entities of the [clazz] which contain the given search content + */ +fun < T : RealmModel> Realm.find(clazz: Class, searchContent: String?) : RealmResults { + val query = this.where(clazz) + when (clazz.kotlin) { + Player::class -> { + query.contains("name", searchContent ?: "", Case.INSENSITIVE).or() + query.contains("summary", searchContent ?: "", Case.INSENSITIVE) + } + } + val items = query.findAll() + val sortField = arrayOf("name") + val resultSort = arrayOf(Sort.ASCENDING) + return items.sort(sortField, resultSort) } \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_outline_search.xml b/app/src/main/res/drawable/ic_outline_search.xml new file mode 100644 index 00000000..6a5ca808 --- /dev/null +++ b/app/src/main/res/drawable/ic_outline_search.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/menu/toolbar_data_list.xml b/app/src/main/res/menu/toolbar_data_list.xml new file mode 100644 index 00000000..6c0816b9 --- /dev/null +++ b/app/src/main/res/menu/toolbar_data_list.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file