Improve filters list

dev
Aurelien Hubert 7 years ago
parent 1c8ff102c6
commit 85a38071be
  1. 159
      app/src/main/java/net/pokeranalytics/android/model/realm/Filter.kt
  2. 29
      app/src/main/java/net/pokeranalytics/android/ui/activity/DataListActivity.kt
  3. 6
      app/src/main/java/net/pokeranalytics/android/ui/extensions/UIExtensions.kt
  4. 54
      app/src/main/java/net/pokeranalytics/android/ui/fragment/DataListFragment.kt
  5. 2
      app/src/main/java/net/pokeranalytics/android/ui/fragment/FiltersFragment.kt
  6. 10
      app/src/main/java/net/pokeranalytics/android/ui/view/RowRepresentable.kt
  7. 9
      app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt
  8. 8
      app/src/main/res/layout/row_title_value_action.xml

@ -12,7 +12,9 @@ import net.pokeranalytics.android.model.interfaces.Deletable
import net.pokeranalytics.android.model.interfaces.DeleteValidityStatus import net.pokeranalytics.android.model.interfaces.DeleteValidityStatus
import net.pokeranalytics.android.model.interfaces.Identifiable import net.pokeranalytics.android.model.interfaces.Identifiable
import net.pokeranalytics.android.ui.interfaces.FilterableType import net.pokeranalytics.android.ui.interfaces.FilterableType
import net.pokeranalytics.android.ui.view.ImageDecorator
import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.ui.view.rowrepresentable.FilterCategoryRow import net.pokeranalytics.android.ui.view.rowrepresentable.FilterCategoryRow
import java.util.* import java.util.*
@ -21,14 +23,14 @@ import java.util.*
* It contains a list of [FilterCondition] describing the complete query to launch * It contains a list of [FilterCondition] describing the complete query to launch
* The [Filter] is working closely with a [Filterable] interface providing the entity we want the query being launched on * The [Filter] is working closely with a [Filterable] interface providing the entity we want the query being launched on
*/ */
open class Filter : RealmObject(), RowRepresentable, Identifiable, Deletable { open class Filter : RealmObject(), RowRepresentable, Identifiable, Deletable, ImageDecorator {
companion object { companion object {
// Create a new instance // Create a new instance
fun newInstance(realm: Realm, filterableType:Int): Filter { fun newInstance(realm: Realm, filterableType: Int): Filter {
val filter = Filter() val filter = Filter()
filter.filterableTypeOrdinal = filterableType filter.filterableTypeOrdinal = filterableType
return filter return filter
//return realm.copyToRealm(filter) //return realm.copyToRealm(filter)
} }
@ -38,26 +40,26 @@ open class Filter : RealmObject(), RowRepresentable, Identifiable, Deletable {
return realm.where<Filter>().equalTo("id", filterId).findFirst() return realm.where<Filter>().equalTo("id", filterId).findFirst()
} }
inline fun <reified T : Filterable> queryOn(realm: Realm, query: Query, sortField: String? = null): RealmResults<T> { inline fun <reified T : Filterable> queryOn(realm: Realm, query: Query, sortField: String? = null): RealmResults<T> {
val realmQuery = realm.where<T>() val realmQuery = realm.where<T>()
sortField?.let { sortField?.let {
return query.queryWith(realmQuery).sort(it).findAll() return query.queryWith(realmQuery).sort(it).findAll()
} ?: run { } ?: run {
return query.queryWith(realmQuery).findAll() return query.queryWith(realmQuery).findAll()
} }
} }
fun sortedByUsage(realm: Realm): RealmResults<Filter> { fun sortedByUsage(realm: Realm): RealmResults<Filter> {
return realm.where(Filter::class.java).findAll().sort("usageCount") return realm.where(Filter::class.java).findAll().sort("usageCount")
} }
} }
@PrimaryKey @PrimaryKey
override var id = UUID.randomUUID().toString() override var id = UUID.randomUUID().toString()
// the queryWith name // the queryWith name
var name: String = "" var name: String = ""
// the number of use of the queryWith, // the number of use of the queryWith,
// for MutableRealmInteger, see https://realm.io/docs/java/latest/#counters // for MutableRealmInteger, see https://realm.io/docs/java/latest/#counters
@ -66,106 +68,115 @@ open class Filter : RealmObject(), RowRepresentable, Identifiable, Deletable {
var filterConditions: RealmList<FilterCondition> = RealmList() var filterConditions: RealmList<FilterCondition> = RealmList()
private set private set
private var filterableTypeOrdinal: Int? = null private var filterableTypeOrdinal: Int? = null
val filterableType: FilterableType override val viewType: Int
get() { get() = RowViewType.TITLE_VALUE_ACTION.ordinal
this.filterableTypeOrdinal?.let { override val imageRes: Int?
return FilterableType.values()[it] get() = R.drawable.ic_outline_settings
} override val imageTint: Int?
return FilterableType.ALL get() = R.color.green
} override val imageClickable: Boolean?
get() = true
val filterableType: FilterableType
get() {
this.filterableTypeOrdinal?.let {
return FilterableType.values()[it]
}
return FilterableType.ALL
}
fun createOrUpdateFilterConditions(filterConditionRows: ArrayList<QueryCondition>) { fun createOrUpdateFilterConditions(filterConditionRows: ArrayList<QueryCondition>) {
println("list of querys saving: ${filterConditionRows.map { it.id }}") println("list of querys saving: ${filterConditionRows.map { it.id }}")
println("list of querys previous: ${this.filterConditions.map { it.queryCondition.id }}") println("list of querys previous: ${this.filterConditions.map { it.queryCondition.id }}")
filterConditionRows filterConditionRows
.map { .map {
it.groupId it.groupId
} }
.distinct() .distinct()
.forEach { groupId-> .forEach { groupId ->
filterConditionRows filterConditionRows
.filter { .filter {
it.groupId == groupId it.groupId == groupId
} }
.apply { .apply {
println("list of querys: ${this.map { it.id }}") println("list of querys: ${this.map { it.id }}")
val casted = arrayListOf<QueryCondition>() val casted = arrayListOf<QueryCondition>()
casted.addAll(this) casted.addAll(this)
val newFilterCondition = FilterCondition(casted) val newFilterCondition = FilterCondition(casted)
val previousCondition = filterConditions.filter { val previousCondition = filterConditions.filter {
it.filterName == newFilterCondition.filterName && it.operator == newFilterCondition.operator it.filterName == newFilterCondition.filterName && it.operator == newFilterCondition.operator
} }
filterConditions.removeAll(previousCondition) filterConditions.removeAll(previousCondition)
filterConditions.add(newFilterCondition) filterConditions.add(newFilterCondition)
} }
} }
} }
fun remove(filterCategoryRow: FilterCategoryRow) { fun remove(filterCategoryRow: FilterCategoryRow) {
val sections = filterCategoryRow.filterSectionRows.map { it.name } val sections = filterCategoryRow.filterSectionRows.map { it.name }
val savedSections = filterConditions.filter { sections.contains(it.sectionName) } val savedSections = filterConditions.filter { sections.contains(it.sectionName) }
this.filterConditions.removeAll(savedSections) this.filterConditions.removeAll(savedSections)
} }
fun countBy(filterCategoryRow: FilterCategoryRow): Int { fun countBy(filterCategoryRow: FilterCategoryRow): Int {
val sections = filterCategoryRow.filterSectionRows.map { it.name } val sections = filterCategoryRow.filterSectionRows.map { it.name }
println("list of sections $sections") println("list of sections $sections")
val savedSections = filterConditions.filter { sections.contains(it.sectionName) }.flatMap { it.queryCondition.id } val savedSections = filterConditions.filter { sections.contains(it.sectionName) }.flatMap { it.queryCondition.id }
println("list of savedSections $savedSections") println("list of savedSections $savedSections")
return savedSections.size return savedSections.size
} }
fun contains(filterElementRow: QueryCondition): Boolean { fun contains(filterElementRow: QueryCondition): Boolean {
println("list of saved queries ${filterConditions.map { it.queryCondition.id }}") println("list of saved queries ${filterConditions.map { it.queryCondition.id }}")
println("list of contains ${filterElementRow.id}") println("list of contains ${filterElementRow.id}")
val contained = filterConditions.flatMap{ it.queryCondition.id }.contains(filterElementRow.id.first()) val contained = filterConditions.flatMap { it.queryCondition.id }.contains(filterElementRow.id.first())
println("list of : $contained") println("list of : $contained")
return contained return contained
} }
/** /**
* Get the saved value for the given [filterElementRow] * Get the saved value for the given [filterElementRow]
*/ */
fun loadValueForElement(filterElementRow: QueryCondition) { fun loadValueForElement(filterElementRow: QueryCondition) {
val filtered = filterConditions.filter { val filtered = filterConditions.filter {
it.queryCondition.id == filterElementRow.id it.queryCondition.id == filterElementRow.id
} }
if (filtered.isNotEmpty()) { if (filtered.isNotEmpty()) {
return filterElementRow.updateValueBy(filtered.first()) return filterElementRow.updateValueBy(filtered.first())
} }
} }
inline fun <reified T : Filterable> results(firstField: String? = null, secondField: String? = null): RealmResults<T> { inline fun <reified T : Filterable> results(firstField: String? = null, secondField: String? = null): RealmResults<T> {
val realmQuery = realm.where<T>() val realmQuery = realm.where<T>()
if (firstField != null && secondField != null) { if (firstField != null && secondField != null) {
return this.query.queryWith(realmQuery).distinct(firstField, secondField).findAll() return this.query.queryWith(realmQuery).distinct(firstField, secondField).findAll()
} }
if (firstField != null) { if (firstField != null) {
return this.query.queryWith(realmQuery).distinct(firstField).findAll() return this.query.queryWith(realmQuery).distinct(firstField).findAll()
} }
return this.query.queryWith(realmQuery).findAll() return this.query.queryWith(realmQuery).findAll()
} }
val query: Query val query: Query
get() { get() {
val query = Query() val query = Query()
this.filterConditions.forEach { this.filterConditions.forEach {
query.add(it.queryCondition) query.add(it.queryCondition)
} }
return query return query
} }
override fun getDisplayName(context: Context): String { override fun getDisplayName(context: Context): String {
if (name.isNotEmpty()) return name if (name.isNotEmpty()) return name
return this.query.getName(context) return this.query.getName(context)
} }
override fun isValidForDelete(realm: Realm): Boolean { override fun isValidForDelete(realm: Realm): Boolean {
return true return true

@ -14,8 +14,9 @@ class DataListActivity : PokerAnalyticsActivity() {
enum class IntentKey(val keyName: String) { enum class IntentKey(val keyName: String) {
DATA_TYPE("DATA_TYPE"), DATA_TYPE("DATA_TYPE"),
LIVE_DATA_TYPE("LIVE_DATA_TYPE"), LIVE_DATA_TYPE("LIVE_DATA_TYPE"),
ITEM_DELETED("ITEM_DELETED") ITEM_DELETED("ITEM_DELETED"),
SHOW_ADD_BUTTON("SHOW_ADD_BUTTON"),
} }
companion object { companion object {
@ -23,17 +24,18 @@ class DataListActivity : PokerAnalyticsActivity() {
context.startActivity(getIntent(context, dataType)) context.startActivity(getIntent(context, dataType))
} }
fun newSelectInstance(fragment: Fragment, dataType: Int) { fun newSelectInstance(fragment: Fragment, dataType: Int, showAddButton: Boolean = true) {
val context = fragment.requireContext() val context = fragment.requireContext()
fragment.startActivityForResult(getIntent(context, dataType), FilterActivityRequestCode.SELECT_FILTER.ordinal) fragment.startActivityForResult(getIntent(context, dataType, showAddButton), FilterActivityRequestCode.SELECT_FILTER.ordinal)
} }
private fun getIntent(context:Context, dataType:Int) : Intent { private fun getIntent(context: Context, dataType: Int, showAddButton: Boolean = true): Intent {
val intent = Intent(context, DataListActivity::class.java) val intent = Intent(context, DataListActivity::class.java)
intent.putExtra(IntentKey.DATA_TYPE.keyName, dataType) intent.putExtra(IntentKey.DATA_TYPE.keyName, dataType)
return intent intent.putExtra(IntentKey.SHOW_ADD_BUTTON.keyName, showAddButton)
} return intent
} }
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -46,10 +48,11 @@ class DataListActivity : PokerAnalyticsActivity() {
* Init UI * Init UI
*/ */
private fun initUI() { private fun initUI() {
val dataType = intent.getIntExtra(IntentKey.DATA_TYPE.keyName, 0) val dataType = intent.getIntExtra(IntentKey.DATA_TYPE.keyName, 0)
val showAddButton = intent.getBooleanExtra(IntentKey.SHOW_ADD_BUTTON.keyName, true)
val fragment = dataListFragment as DataListFragment val fragment = dataListFragment as DataListFragment
fragment.setData(dataType) fragment.setData(dataType)
fragment.updateUI(showAddButton)
} }
} }

@ -5,6 +5,7 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.res.Resources import android.content.res.Resources
import android.net.Uri import android.net.Uri
import android.util.TypedValue
import android.view.View import android.view.View
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
@ -150,3 +151,8 @@ fun View.showWithAnimation() {
animate().cancel() animate().cancel()
animate().alpha(1f).start() animate().alpha(1f).start()
} }
fun View.addCircleRipple() = with(TypedValue()) {
context.theme.resolveAttribute(android.R.attr.selectableItemBackgroundBorderless, this, true)
setBackgroundResource(resourceId)
}

@ -19,6 +19,7 @@ import net.pokeranalytics.android.ui.activity.FiltersActivity
import net.pokeranalytics.android.ui.adapter.LiveRowRepresentableDataSource import net.pokeranalytics.android.ui.adapter.LiveRowRepresentableDataSource
import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter
import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate
import net.pokeranalytics.android.ui.extensions.toast
import net.pokeranalytics.android.ui.fragment.components.DeletableItemFragment import net.pokeranalytics.android.ui.fragment.components.DeletableItemFragment
import net.pokeranalytics.android.ui.helpers.SwipeToDeleteCallback import net.pokeranalytics.android.ui.helpers.SwipeToDeleteCallback
import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
@ -41,20 +42,6 @@ class DataListFragment : DeletableItemFragment(), LiveRowRepresentableDataSource
return this.items return this.items
} }
/**
* Set fragment data
*/
fun setData(dataType: Int) {
this.dataType = LiveData.values()[dataType]
this.identifiableClass = this.dataType.relatedEntity
setToolbarTitle(this.dataType.localizedTitle(requireContext()))
val realm = getRealm()
this.items = realm.sorted(this.identifiableClass)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
super.onCreateView(inflater, container, savedInstanceState) super.onCreateView(inflater, container, savedInstanceState)
return inflater.inflate(R.layout.fragment_data_list, container, false) return inflater.inflate(R.layout.fragment_data_list, container, false)
@ -124,10 +111,16 @@ class DataListFragment : DeletableItemFragment(), LiveRowRepresentableDataSource
when (this.dataType) { when (this.dataType) {
LiveData.FILTER -> { LiveData.FILTER -> {
val intent = Intent() if (fromAction) {
intent.putExtra(FiltersActivity.IntentKey.FILTER_ID.keyName, (row as Filter).id) // Open filter params
activity?.setResult(Activity.RESULT_OK, intent) toast("Hey")
activity?.finish() } else {
// Select filter
val intent = Intent()
intent.putExtra(FiltersActivity.IntentKey.FILTER_ID.keyName, (row as Filter).id)
activity?.setResult(Activity.RESULT_OK, intent)
activity?.finish()
}
} }
else -> { else -> {
val identifier = (row as Identifiable).id val identifier = (row as Identifiable).id
@ -136,4 +129,29 @@ class DataListFragment : DeletableItemFragment(), LiveRowRepresentableDataSource
} }
} }
/**
* Set fragment data
*/
fun setData(dataType: Int) {
this.dataType = LiveData.values()[dataType]
this.identifiableClass = this.dataType.relatedEntity
setToolbarTitle(this.dataType.localizedTitle(requireContext()))
val realm = getRealm()
this.items = realm.sorted(this.identifiableClass)
}
/**
* Update UI
*/
fun updateUI(showAddButton: Boolean) {
if (showAddButton) {
this.addButton.show()
} else {
this.addButton.hide()
}
}
} }

@ -156,7 +156,7 @@ open class FiltersFragment : RealmFragment(), StaticRowRepresentableDataSource,
} }
moreFilters.setOnClickListener { moreFilters.setOnClickListener {
DataListActivity.newSelectInstance(this, LiveData.FILTER.ordinal) DataListActivity.newSelectInstance(this, LiveData.FILTER.ordinal, false)
} }
} }

@ -34,15 +34,13 @@ interface DefaultEditDataSource : EditDataSource, Localizable {
interface ImageDecorator { interface ImageDecorator {
val imageRes: Int? val imageRes: Int?
get() { get() = null
return null
}
val imageTint: Int? val imageTint: Int?
get() { get() = null
return null
}
val imageClickable: Boolean?
get() = false
} }
/** /**

@ -31,6 +31,7 @@ import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.model.realm.Transaction import net.pokeranalytics.android.model.realm.Transaction
import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter
import net.pokeranalytics.android.ui.extensions.ChipGroupExtension import net.pokeranalytics.android.ui.extensions.ChipGroupExtension
import net.pokeranalytics.android.ui.extensions.addCircleRipple
import net.pokeranalytics.android.ui.extensions.px import net.pokeranalytics.android.ui.extensions.px
import net.pokeranalytics.android.ui.extensions.setTextFormat import net.pokeranalytics.android.ui.extensions.setTextFormat
import net.pokeranalytics.android.ui.graph.AxisFormatting import net.pokeranalytics.android.ui.graph.AxisFormatting
@ -204,6 +205,14 @@ enum class RowViewType(private var layoutRes: Int) {
row.imageTint?.let { color -> row.imageTint?.let { color ->
imageView.setColorFilter(ContextCompat.getColor(imageView.context, color)) imageView.setColorFilter(ContextCompat.getColor(imageView.context, color))
} }
if (row.imageClickable == true) {
imageView.addCircleRipple()
imageView.setOnClickListener {
adapter.delegate?.onRowSelected(position, row, true)
}
} else {
imageView.setBackgroundResource(0)
}
} }
// Listener // Listener

@ -25,6 +25,7 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginEnd="8dp"
android:ellipsize="end" android:ellipsize="end"
android:gravity="end|center_vertical" android:gravity="end|center_vertical"
android:maxLines="1" android:maxLines="1"
@ -39,14 +40,15 @@
android:id="@+id/action" android:id="@+id/action"
android:layout_width="32dp" android:layout_width="32dp"
android:layout_height="32dp" android:layout_height="32dp"
android:layout_marginStart="16dp" android:background="?selectableItemBackgroundBorderless"
android:padding="4dp" android:padding="4dp"
android:visibility="gone" android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@+id/guidelineEnd" app:layout_constraintEnd_toEndOf="@+id/guidelineEnd"
app:layout_constraintStart_toEndOf="@+id/value" app:layout_constraintStart_toEndOf="@+id/value"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/ic_close" /> tools:src="@drawable/ic_close"
tools:visibility="visible" />
<androidx.constraintlayout.widget.Guideline <androidx.constraintlayout.widget.Guideline
android:id="@+id/guidelineStart" android:id="@+id/guidelineStart"
@ -60,7 +62,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
app:layout_constraintGuide_end="16dp" /> app:layout_constraintGuide_end="8dp" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
Loading…
Cancel
Save