Laurent 7 years ago
commit 5ee584f67b
  1. 19
      app/src/main/java/net/pokeranalytics/android/model/LiveData.kt
  2. 7
      app/src/main/java/net/pokeranalytics/android/model/interfaces/Manageable.kt
  3. 4
      app/src/main/java/net/pokeranalytics/android/model/realm/Bankroll.kt
  4. 2
      app/src/main/java/net/pokeranalytics/android/model/realm/CustomField.kt
  5. 3
      app/src/main/java/net/pokeranalytics/android/model/realm/CustomFieldEntry.kt
  6. 166
      app/src/main/java/net/pokeranalytics/android/model/realm/Filter.kt
  7. 2
      app/src/main/java/net/pokeranalytics/android/model/realm/Game.kt
  8. 4
      app/src/main/java/net/pokeranalytics/android/model/realm/Location.kt
  9. 2
      app/src/main/java/net/pokeranalytics/android/model/realm/ReportSetup.kt
  10. 4
      app/src/main/java/net/pokeranalytics/android/model/realm/TournamentFeature.kt
  11. 4
      app/src/main/java/net/pokeranalytics/android/model/realm/TournamentName.kt
  12. 2
      app/src/main/java/net/pokeranalytics/android/model/realm/Transaction.kt
  13. 2
      app/src/main/java/net/pokeranalytics/android/model/realm/TransactionType.kt
  14. 29
      app/src/main/java/net/pokeranalytics/android/ui/activity/DataListActivity.kt
  15. 8
      app/src/main/java/net/pokeranalytics/android/ui/adapter/RowRepresentableDataSource.kt
  16. 6
      app/src/main/java/net/pokeranalytics/android/ui/extensions/UIExtensions.kt
  17. 2
      app/src/main/java/net/pokeranalytics/android/ui/fragment/BankrollFragment.kt
  18. 4
      app/src/main/java/net/pokeranalytics/android/ui/fragment/ComparisonChartFragment.kt
  19. 58
      app/src/main/java/net/pokeranalytics/android/ui/fragment/DataListFragment.kt
  20. 1
      app/src/main/java/net/pokeranalytics/android/ui/fragment/FilterDetailsFragment.kt
  21. 24
      app/src/main/java/net/pokeranalytics/android/ui/fragment/FiltersFragment.kt
  22. 4
      app/src/main/java/net/pokeranalytics/android/ui/fragment/ReportCreationFragment.kt
  23. 8
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/DeletableItemFragment.kt
  24. 10
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/FilterableFragment.kt
  25. 18
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/bottomsheet/BottomSheetDoubleEditTextFragment.kt
  26. 9
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/bottomsheet/BottomSheetEditTextFragment.kt
  27. 5
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/bottomsheet/BottomSheetFragment.kt
  28. 7
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/bottomsheet/BottomSheetListFragment.kt
  29. 5
      app/src/main/java/net/pokeranalytics/android/ui/fragment/data/CustomFieldDataFragment.kt
  30. 4
      app/src/main/java/net/pokeranalytics/android/ui/fragment/data/DataManagerFragment.kt
  31. 4
      app/src/main/java/net/pokeranalytics/android/ui/fragment/data/EditableDataFragment.kt
  32. 48
      app/src/main/java/net/pokeranalytics/android/ui/interfaces/FilterHandler.kt
  33. 10
      app/src/main/java/net/pokeranalytics/android/ui/view/RowRepresentable.kt
  34. 9
      app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt
  35. 2
      app/src/main/res/layout/fragment_feed.xml
  36. 121
      app/src/main/res/layout/row_title_value_action.xml
  37. 6
      app/src/main/res/values/strings.xml

@ -19,9 +19,9 @@ enum class LiveData : Localizable {
TOURNAMENT_FEATURE,
TRANSACTION,
TRANSACTION_TYPE,
FILTER,
CUSTOM_FIELD,
REPORT_SETUP;
FILTER,
CUSTOM_FIELD,
REPORT_SETUP;
val relatedEntity: Class<out Identifiable>
get() {
@ -33,7 +33,7 @@ enum class LiveData : Localizable {
TOURNAMENT_FEATURE -> TournamentFeature::class.java
TRANSACTION -> Transaction::class.java
TRANSACTION_TYPE -> TransactionType::class.java
FILTER -> Filter::class.java
FILTER -> Filter::class.java
CUSTOM_FIELD -> CustomField::class.java
REPORT_SETUP -> ReportSetup::class.java
}
@ -48,7 +48,7 @@ enum class LiveData : Localizable {
}
}
private fun newEntity(): RealmModel{
private fun newEntity(): RealmModel {
return this.relatedEntity.newInstance()
}
@ -73,7 +73,7 @@ enum class LiveData : Localizable {
TOURNAMENT_FEATURE -> R.string.tournament_feature
TRANSACTION -> R.string.operations
TRANSACTION_TYPE -> R.string.operation_types
FILTER -> R.string.filter
FILTER -> R.string.filter
CUSTOM_FIELD -> R.string.custom_fields
REPORT_SETUP -> R.string.custom
}
@ -86,4 +86,11 @@ enum class LiveData : Localizable {
return "${context.getString(R.string.new_str)} ${this.localizedTitle(context).toLowerCase()}"
}
/**
* Return the update entity titleResId
*/
fun updateEntityLocalizedTitle(context: Context): String {
return "${context.getString(R.string.update_entity)} ${this.localizedTitle(context).toLowerCase()}"
}
}

@ -1,5 +1,6 @@
package net.pokeranalytics.android.model.interfaces
import android.content.Context
import io.realm.Realm
import io.realm.RealmModel
import net.pokeranalytics.android.R
@ -106,11 +107,11 @@ interface Deletable : Identifiable {
/**
* A method to define if an object is safe for deletion in database
*/
fun isValidForDelete(realm: Realm): Boolean
fun isValidForDelete(context: Context, realm: Realm): Boolean
fun getDeleteStatus(realm: Realm): DeleteValidityStatus {
if (!isValidForDelete(realm)) {
fun getDeleteStatus(context: Context, realm: Realm): DeleteValidityStatus {
if (!isValidForDelete(context, realm)) {
return DeleteValidityStatus.INVALID
}
return DeleteValidityStatus.VALID

@ -66,12 +66,12 @@ open class Bankroll : RealmObject(), NameManageable, RowRepresentable {
}
}
override fun isValidForDelete(realm: Realm): Boolean {
override fun isValidForDelete(context: Context, realm: Realm): Boolean {
return realm.where<Session>().equalTo("bankroll.id", id).findAll().isEmpty()
&& realm.where<Transaction>().equalTo("bankroll.id", id).findAll().isEmpty()
}
override fun getDeleteStatus(realm: Realm): DeleteValidityStatus {
override fun getDeleteStatus(context: Context, realm: Realm): DeleteValidityStatus {
return if (!realm.where<Session>().equalTo("bankroll.id", id).findAll().isEmpty()) {
DeleteValidityStatus.SESSIONS_LINKED
} else if (!realm.where<Transaction>().equalTo("bankroll.id", id).findAll().isEmpty()) {

@ -117,7 +117,7 @@ open class CustomField : RealmObject(), NameManageable, StaticRowRepresentableDa
return realm.where(this::class.java).equalTo("name", this.name).and().notEqualTo("id", this.id).findAll().isNotEmpty()
}
override fun isValidForDelete(realm: Realm): Boolean {
override fun isValidForDelete(context: Context, realm: Realm): Boolean {
return true
}

@ -10,7 +10,6 @@ import io.realm.kotlin.where
import net.pokeranalytics.android.R
import net.pokeranalytics.android.exceptions.ModelException
import net.pokeranalytics.android.model.interfaces.DeleteValidityStatus
import net.pokeranalytics.android.model.interfaces.Manageable
import net.pokeranalytics.android.model.interfaces.NameManageable
import net.pokeranalytics.android.model.interfaces.SaveValidityStatus
import net.pokeranalytics.android.ui.fragment.components.bottomsheet.BottomSheetType
@ -95,7 +94,7 @@ open class CustomFieldEntry : RealmObject(), NameManageable, RowRepresentable {
this.value = value as String? ?: ""
}
override fun isValidForDelete(realm: Realm): Boolean {
override fun isValidForDelete(context: Context, realm: Realm): Boolean {
if (realm.where<Session>().contains("customFieldEntries.id", id).findAll().isNotEmpty()) {
return false
}

@ -12,8 +12,11 @@ import net.pokeranalytics.android.model.interfaces.Deletable
import net.pokeranalytics.android.model.interfaces.DeleteValidityStatus
import net.pokeranalytics.android.model.interfaces.Identifiable
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.RowViewType
import net.pokeranalytics.android.ui.view.rowrepresentable.FilterCategoryRow
import net.pokeranalytics.android.util.Preferences
import java.util.*
/**
@ -21,14 +24,14 @@ import java.util.*
* 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
*/
open class Filter : RealmObject(), RowRepresentable, Identifiable, Deletable {
open class Filter : RealmObject(), RowRepresentable, Identifiable, Deletable, ImageDecorator {
companion object {
// Create a new instance
fun newInstance(realm: Realm, filterableType:Int): Filter {
fun newInstance(realm: Realm, filterableType: Int): Filter {
val filter = Filter()
filter.filterableTypeOrdinal = filterableType
filter.filterableTypeOrdinal = filterableType
return filter
//return realm.copyToRealm(filter)
}
@ -38,26 +41,26 @@ open class Filter : RealmObject(), RowRepresentable, Identifiable, Deletable {
return realm.where<Filter>().equalTo("id", filterId).findFirst()
}
inline fun <reified T : Filterable> queryOn(realm: Realm, query: Query, sortField: String? = null): RealmResults<T> {
val realmQuery = realm.where<T>()
sortField?.let {
return query.queryWith(realmQuery).sort(it).findAll()
} ?: run {
return query.queryWith(realmQuery).findAll()
}
}
inline fun <reified T : Filterable> queryOn(realm: Realm, query: Query, sortField: String? = null): RealmResults<T> {
val realmQuery = realm.where<T>()
sortField?.let {
return query.queryWith(realmQuery).sort(it).findAll()
} ?: run {
return query.queryWith(realmQuery).findAll()
}
}
fun sortedByUsage(realm: Realm): RealmResults<Filter> {
return realm.where(Filter::class.java).findAll().sort("usageCount")
}
}
}
@PrimaryKey
override var id = UUID.randomUUID().toString()
override var id = UUID.randomUUID().toString()
// the queryWith name
var name: String = ""
var name: String = ""
// the number of use of the queryWith,
// for MutableRealmInteger, see https://realm.io/docs/java/latest/#counters
@ -66,112 +69,121 @@ open class Filter : RealmObject(), RowRepresentable, Identifiable, Deletable {
var filterConditions: RealmList<FilterCondition> = RealmList()
private set
private var filterableTypeOrdinal: Int? = null
val filterableType: FilterableType
get() {
this.filterableTypeOrdinal?.let {
return FilterableType.values()[it]
}
return FilterableType.ALL
}
private var filterableTypeOrdinal: Int? = null
override val viewType: Int
get() = RowViewType.TITLE_VALUE_ACTION.ordinal
override val imageRes: Int?
get() = R.drawable.ic_outline_settings
override val imageTint: Int?
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>) {
println("list of querys saving: ${filterConditionRows.map { it.id }}")
println("list of querys previous: ${this.filterConditions.map { it.queryCondition.id }}")
filterConditionRows
println("list of querys saving: ${filterConditionRows.map { it.id }}")
println("list of querys previous: ${this.filterConditions.map { it.queryCondition.id }}")
filterConditionRows
.map {
it.groupId
}
.distinct()
.forEach { groupId->
.forEach { groupId ->
filterConditionRows
.filter {
it.groupId == groupId
}
.apply {
println("list of querys: ${this.map { it.id }}")
val casted = arrayListOf<QueryCondition>()
casted.addAll(this)
val newFilterCondition = FilterCondition(casted)
val previousCondition = filterConditions.filter {
it.filterName == newFilterCondition.filterName && it.operator == newFilterCondition.operator
}
filterConditions.removeAll(previousCondition)
filterConditions.add(newFilterCondition)
}
println("list of querys: ${this.map { it.id }}")
val casted = arrayListOf<QueryCondition>()
casted.addAll(this)
val newFilterCondition = FilterCondition(casted)
val previousCondition = filterConditions.filter {
it.filterName == newFilterCondition.filterName && it.operator == newFilterCondition.operator
}
filterConditions.removeAll(previousCondition)
filterConditions.add(newFilterCondition)
}
}
}
fun remove(filterCategoryRow: FilterCategoryRow) {
val sections = filterCategoryRow.filterSectionRows.map { it.name }
val savedSections = filterConditions.filter { sections.contains(it.sectionName) }
this.filterConditions.removeAll(savedSections)
}
fun remove(filterCategoryRow: FilterCategoryRow) {
val sections = filterCategoryRow.filterSectionRows.map { it.name }
val savedSections = filterConditions.filter { sections.contains(it.sectionName) }
this.filterConditions.removeAll(savedSections)
}
fun countBy(filterCategoryRow: FilterCategoryRow): Int {
val sections = filterCategoryRow.filterSectionRows.map { it.name }
println("list of sections $sections")
val savedSections = filterConditions.filter { sections.contains(it.sectionName) }.flatMap { it.queryCondition.id }
println("list of savedSections $savedSections")
return savedSections.size
println("list of sections $sections")
val savedSections = filterConditions.filter { sections.contains(it.sectionName) }.flatMap { it.queryCondition.id }
println("list of savedSections $savedSections")
return savedSections.size
}
fun contains(filterElementRow: QueryCondition): Boolean {
println("list of saved queries ${filterConditions.map { it.queryCondition.id }}")
println("list of contains ${filterElementRow.id}")
val contained = filterConditions.flatMap{ it.queryCondition.id }.contains(filterElementRow.id.first())
println("list of : $contained")
return contained
println("list of saved queries ${filterConditions.map { it.queryCondition.id }}")
println("list of contains ${filterElementRow.id}")
val contained = filterConditions.flatMap { it.queryCondition.id }.contains(filterElementRow.id.first())
println("list of : $contained")
return contained
}
/**
* Get the saved value for the given [filterElementRow]
*/
fun loadValueForElement(filterElementRow: QueryCondition) {
fun loadValueForElement(filterElementRow: QueryCondition) {
val filtered = filterConditions.filter {
it.queryCondition.id == filterElementRow.id
}
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> {
val realmQuery = realm.where<T>()
val realmQuery = realm.where<T>()
if (firstField != null && secondField != null) {
return this.query.queryWith(realmQuery).distinct(firstField, secondField).findAll()
}
if (firstField != null && secondField != null) {
return this.query.queryWith(realmQuery).distinct(firstField, secondField).findAll()
}
if (firstField != null) {
return this.query.queryWith(realmQuery).distinct(firstField).findAll()
}
if (firstField != null) {
return this.query.queryWith(realmQuery).distinct(firstField).findAll()
}
return this.query.queryWith(realmQuery).findAll()
}
return this.query.queryWith(realmQuery).findAll()
}
val query: Query
get() {
val query = Query()
this.filterConditions.forEach {
query.add(it.queryCondition)
}
return query
}
val query: Query
get() {
val query = Query()
this.filterConditions.forEach {
query.add(it.queryCondition)
}
return query
}
override fun getDisplayName(context: Context): String {
if (name.isNotEmpty()) return name
return this.query.getName(context)
}
override fun getDisplayName(context: Context): String {
if (name.isNotEmpty()) return name
return this.query.getName(context)
}
override fun isValidForDelete(realm: Realm): Boolean {
return true
override fun isValidForDelete(context: Context, realm: Realm): Boolean {
return id != Preferences.getActiveFilterId(context)
}
override fun getFailedDeleteMessage(status: DeleteValidityStatus): Int {
return R.string.relationship_error
return R.string.filter_currently_selected
}
}

@ -91,7 +91,7 @@ open class Game : RealmObject(), NameManageable, StaticRowRepresentableDataSourc
}
}
override fun isValidForDelete(realm: Realm): Boolean {
override fun isValidForDelete(context: Context, realm: Realm): Boolean {
return realm.where<Session>().equalTo("game.id", id).findAll().isEmpty()
}
}

@ -7,8 +7,8 @@ import io.realm.RealmObject
import io.realm.annotations.PrimaryKey
import io.realm.kotlin.where
import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.interfaces.SaveValidityStatus
import net.pokeranalytics.android.model.interfaces.NameManageable
import net.pokeranalytics.android.model.interfaces.SaveValidityStatus
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.rowrepresentable.SimpleRow
import java.util.*
@ -59,7 +59,7 @@ open class Location : RealmObject(), NameManageable, RowRepresentable {
}
}
override fun isValidForDelete(realm: Realm): Boolean {
override fun isValidForDelete(context: Context, realm: Realm): Boolean {
return realm.where<Session>().equalTo("location.id", id).findAll().isEmpty()
}
}

@ -70,7 +70,7 @@ open class ReportSetup : RealmObject(), RowRepresentable, Deletable {
// Deletable
override fun isValidForDelete(realm: Realm): Boolean {
override fun isValidForDelete(context: Context, realm: Realm): Boolean {
return true
}

@ -7,8 +7,8 @@ import io.realm.annotations.PrimaryKey
import io.realm.kotlin.where
import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.interfaces.CountableUsage
import net.pokeranalytics.android.model.interfaces.SaveValidityStatus
import net.pokeranalytics.android.model.interfaces.NameManageable
import net.pokeranalytics.android.model.interfaces.SaveValidityStatus
import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowRepresentableEditDescriptor
@ -73,7 +73,7 @@ open class TournamentFeature : RealmObject(), NameManageable, StaticRowRepresent
}
}
override fun isValidForDelete(realm: Realm): Boolean {
override fun isValidForDelete(context: Context, realm: Realm): Boolean {
return realm.where<Session>().equalTo("tournamentFeatures.id", id).findAll().isEmpty()
}
}

@ -6,8 +6,8 @@ import io.realm.RealmObject
import io.realm.annotations.PrimaryKey
import io.realm.kotlin.where
import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.interfaces.SaveValidityStatus
import net.pokeranalytics.android.model.interfaces.NameManageable
import net.pokeranalytics.android.model.interfaces.SaveValidityStatus
import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowRepresentableEditDescriptor
@ -67,7 +67,7 @@ open class TournamentName : RealmObject(), NameManageable, StaticRowRepresentabl
}
}
override fun isValidForDelete(realm: Realm): Boolean {
override fun isValidForDelete(context: Context, realm: Realm): Boolean {
return realm.where<Session>().equalTo("tournamentName.id", id).findAll().isEmpty()
}
}

@ -115,7 +115,7 @@ open class Transaction : RealmObject(), Manageable, StaticRowRepresentableDataSo
return realm.where<Transaction>().equalTo("id", id).findFirst() != null
}
override fun isValidForDelete(realm: Realm): Boolean {
override fun isValidForDelete(context: Context, realm: Realm): Boolean {
return true
}

@ -90,7 +90,7 @@ open class TransactionType : RealmObject(), NameManageable, StaticRowRepresentab
}
}
override fun isValidForDelete(realm: Realm): Boolean {
override fun isValidForDelete(context: Context, realm: Realm): Boolean {
return true
}

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

@ -30,8 +30,6 @@ interface RowRepresentableDataSource: EditableDataSource, DisplayableDataSource,
*/
fun viewTypeForPosition(position:Int): Int
//TODO should be removed
fun indexForRow(row: RowRepresentable): Int
}
/**
@ -61,12 +59,6 @@ interface StaticRowRepresentableDataSource: RowRepresentableDataSource {
throw IllegalStateException("Need to implement Data Source")
}
override fun indexForRow(row: RowRepresentable): Int {
this.adapterRows()?.let {
return it.indexOf(row)
}
throw IllegalStateException("Need to implement Data Source")
}
}

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

@ -215,7 +215,7 @@ class BankrollFragment : DeletableItemFragment(), StaticRowRepresentableDataSour
override fun updateUIAfterUndoDeletion(newItem: RealmObject) {
// TODO: Improve that
// We are recreating a Bankroll report because the last one if invalid => the bankroll of the setup has been deleted
// We are recreating a Bankroll report because the last one is invalid => the bankroll of the setup has been deleted
deletedRow?.let { row ->
val bankrollReportSetup = BankrollReportSetup(newItem as Bankroll)

@ -93,9 +93,7 @@ class ComparisonChartFragment : PokerAnalyticsFragment(), StaticRowRepresentable
private fun initUI() {
setDisplayHomeAsUpEnabled(true)
//TODO: Localize title
setToolbarTitle("Comparison chart")
setToolbarTitle(getString(R.string.comparison_chart))
parentActivity?.let {
viewPagerAdapter = ComparisonChartPagerAdapter(requireContext(), it.supportFragmentManager)

@ -41,20 +41,6 @@ class DataListFragment : DeletableItemFragment(), LiveRowRepresentableDataSource
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? {
super.onCreateView(inflater, container, savedInstanceState)
return inflater.inflate(R.layout.fragment_data_list, container, false)
@ -116,18 +102,21 @@ class DataListFragment : DeletableItemFragment(), LiveRowRepresentableDataSource
return if (viewType != -1) viewType else RowViewType.DATA.ordinal
}
override fun indexForRow(row: RowRepresentable): Int {
return this.items.indexOf(row as Identifiable)
}
override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) {
when (this.dataType) {
LiveData.FILTER -> {
val intent = Intent()
intent.putExtra(FiltersActivity.IntentKey.FILTER_ID.keyName, (row as Filter).id)
activity?.setResult(Activity.RESULT_OK, intent)
activity?.finish()
if (fromAction) {
// Open filter params
val filter = row as Filter
FiltersActivity.newInstanceForResult(fragment = this, filterId = filter.id, currentFilterable = filter.filterableType)
} 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 -> {
val identifier = (row as Identifiable).id
@ -136,4 +125,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()
}
}
}

@ -55,7 +55,6 @@ open class FilterDetailsFragment : RealmFragment(), StaticRowRepresentableDataSo
override fun onBackPressed() {
super.onBackPressed()
println("<<<<< back pressed")
saveData()
}

@ -27,13 +27,13 @@ import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.rowrepresentable.FilterCategoryRow
import net.pokeranalytics.android.util.Preferences
import net.pokeranalytics.android.util.extensions.sorted
import timber.log.Timber
open class FiltersFragment : RealmFragment(), StaticRowRepresentableDataSource, RowRepresentableDelegate {
companion object {
const val REQUEST_CODE_FILTER_DETAILS = 100
const val MOST_USED_FILTERS_DISPLAYED = 6
var currentFilter: Filter? = null
@ -156,7 +156,7 @@ open class FiltersFragment : RealmFragment(), StaticRowRepresentableDataSource,
}
moreFilters.setOnClickListener {
DataListActivity.newSelectInstance(this, LiveData.FILTER.ordinal)
DataListActivity.newSelectInstance(this, LiveData.FILTER.ordinal, false)
}
}
@ -196,19 +196,19 @@ open class FiltersFragment : RealmFragment(), StaticRowRepresentableDataSource,
*/
private fun updateMostUsedFilters() {
if (isUpdating) {
var nbChips = 0
val filters = getRealm().sorted(Filter::class.java)
val currentFilterId = Preferences.getActiveFilterId(requireContext())
if (isUpdating || filters.isEmpty() || (filters.size == 1 && filters.first()?.id == currentFilterId)) {
mostUsedFiltersLayout.visibility = View.GONE
return
}
val filters = getRealm().sorted(Filter::class.java)
val currentFilterId = Preferences.getActiveFilterId(requireContext())
mostUsedFilters.removeAllViews()
var nbChips = 0
filters.forEach { filter ->
if (nbChips < 3) {
if (nbChips < MOST_USED_FILTERS_DISPLAYED) {
if (filter.id != currentFilterId) {
val chip = Chip(requireContext())
@ -220,7 +220,6 @@ open class FiltersFragment : RealmFragment(), StaticRowRepresentableDataSource,
chip.isChecked = filter.id == currentFilterId
chip.setOnCloseIconClickListener {
chip.isChecked = false
}
chip.setOnClickListener {
@ -235,7 +234,6 @@ open class FiltersFragment : RealmFragment(), StaticRowRepresentableDataSource,
}
}
}
}
/**
@ -250,13 +248,10 @@ open class FiltersFragment : RealmFragment(), StaticRowRepresentableDataSource,
* Validate the updates of the queryWith
*/
private fun validateUpdates() {
Timber.d("Validate queryWith updates")
val realm = getRealm()
realm.beginTransaction()
currentFilter?.let {
it.name = it.query.getName(requireContext())
Timber.d("name: ${it.name}")
realm.copyToRealmOrUpdate(it)
}
realm.commitTransaction()
@ -269,10 +264,7 @@ open class FiltersFragment : RealmFragment(), StaticRowRepresentableDataSource,
* Cancel the latest updates of the queryWith
*/
private fun cancelUpdates() {
Timber.d("Cancel queryWith updates")
val filterId = filterCopy?.id ?: ""
val realm = getRealm()
realm.beginTransaction()
filterCopy?.let {

@ -120,10 +120,6 @@ class ReportCreationFragment : RealmFragment(), RowRepresentableDataSource, RowR
}
}
override fun indexForRow(row: RowRepresentable): Int {
return this.currentRows.indexOf(row)
}
override fun isSelected(row: RowRepresentable): Boolean {
return this.assistant.isSelected(row)
}

@ -15,10 +15,8 @@ import kotlinx.coroutines.launch
import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.interfaces.Deletable
import net.pokeranalytics.android.model.interfaces.Identifiable
import net.pokeranalytics.android.model.realm.Filter
import net.pokeranalytics.android.ui.activity.DataListActivity
import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter
import timber.log.Timber
/**
* Deletable Item Fragment
@ -89,10 +87,8 @@ abstract class DeletableItemFragment : RealmFragment() {
val deletableItem = (itemToDelete as Deletable)
Timber.d("deletableItem: ${(deletableItem as Filter).getDisplayName(requireContext())}")
// Check if the object is valid for the deletion
if (deletableItem.isValidForDelete(this.getRealm())) {
if (deletableItem.isValidForDelete(requireContext(), this.getRealm())) {
deletedItem = getRealm().copyFromRealm(itemToDelete)
lastDeletedItemPosition = itemPosition
getRealm().executeTransaction {
@ -104,7 +100,7 @@ abstract class DeletableItemFragment : RealmFragment() {
showUndoSnackBar()
} else {
dataListAdapter.notifyItemChanged(itemPosition)
val status = deletableItem.getDeleteStatus(this.getRealm())
val status = deletableItem.getDeleteStatus(requireContext(), this.getRealm())
val message = deletableItem.getFailedDeleteMessage(status)
val builder = AlertDialog.Builder(requireContext())
.setMessage(message)

@ -22,9 +22,9 @@ import timber.log.Timber
/**
* A class which define the fragment as Filterable
* - Add an filter icon menu
* - Access to the filters actions (new, select, modify, remove)
* - ...
* - Add a filter icon menu
* - Listen for INTENT_FILTER_UPDATE_FILTER_UI
* -
*/
open class FilterableFragment : RealmFragment(), FilterHandler {
@ -46,13 +46,11 @@ open class FilterableFragment : RealmFragment(), FilterHandler {
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
Timber.d("onActivityCreated: $parentActivity")
parentActivity?.registerReceiver(updateFilterUIBroadcast, IntentFilter(INTENT_FILTER_UPDATE_FILTER_UI))
}
override fun onDestroy() {
super.onDestroy()
Timber.d("onDestroy: $parentActivity")
parentActivity?.unregisterReceiver(updateFilterUIBroadcast)
}
@ -84,12 +82,10 @@ open class FilterableFragment : RealmFragment(), FilterHandler {
}
override fun applyFilter() {
Timber.d("applyFilter")
updateFilterUI()
}
override fun removeFilter() {
Timber.d("removeFilter")
hideSelectedFilter()
}

@ -27,7 +27,6 @@ class BottomSheetDoubleEditTextFragment : BottomSheetFragment() {
override fun onStart() {
super.onStart()
if (isEditingBlinds) {
editText2.requestFocus()
} else {
@ -43,6 +42,7 @@ class BottomSheetDoubleEditTextFragment : BottomSheetFragment() {
* Init data
*/
private fun initData() {
valueHasPlaceholder = true
isEditingBlinds = row == SessionRow.BLINDS
}
@ -64,17 +64,23 @@ class BottomSheetDoubleEditTextFragment : BottomSheetFragment() {
values[0] = (data[0].defaultValue ?: "").toString()
values[1] = (data[1].defaultValue ?: "").toString()
editText1.setText(values[0])
editText2.setText(values[1])
data[0].hint?.let { editText1.hint = getString(it) }
editText1.inputType = data[0].inputType ?: InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
data[1].hint?.let { editText2.hint = getString(it) }
editText2.inputType = data[1].inputType ?: InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
if (valueHasPlaceholder) {
editText1.hint = values[0]
editText2.hint = values[1]
} else {
editText1.setText(values[0])
editText2.setText(values[1])
}
editText1.addTextChangedListener {
values[0] = it?.toString() ?: ""
}
data[1].hint?.let { editText2.hint = getString(it) }
editText2.inputType = data[1].inputType ?: InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
editText2.addTextChangedListener {
values[1] = it?.toString() ?: ""
if (isEditingBlinds) {

@ -43,12 +43,14 @@ class BottomSheetEditTextFragment : BottomSheetFragment() {
* Init data
*/
private fun initData() {
valueHasPlaceholder = true
}
/**
* Init UI
*/
private fun initUI() {
val data = getData()?:throw RowRepresentableEditDescriptorException("RowRepresentableEditDescriptor not found")
if (data.size != 1) {
throw RowRepresentableEditDescriptorException("RowRepresentableEditDescriptor inconsistency")
@ -62,7 +64,12 @@ class BottomSheetEditTextFragment : BottomSheetFragment() {
this.value = it?.toString()
}
data[0].defaultValue?.let {
editText1.setText(it.toString())
if (valueHasPlaceholder) {
this.value = it.toString()
editText1.hint = it.toString()
} else {
editText1.setText(it.toString())
}
}
editText1.setOnEditorActionListener { _, actionId, _ ->

@ -30,6 +30,7 @@ open class BottomSheetFragment : BottomSheetDialogFragment() {
lateinit var row: RowRepresentable
lateinit var delegate: RowRepresentableDelegate
var currentCurrency: Currency? = null
var valueHasPlaceholder: Boolean = false
private var isClearable: Boolean = true
private var isDeletable: Boolean = false
@ -46,7 +47,8 @@ open class BottomSheetFragment : BottomSheetDialogFragment() {
rowRepresentableEditDescriptors: ArrayList<RowRepresentableEditDescriptor>?,
isClearable: Boolean? = true,
currentCurrency: Currency? = null,
isDeletable: Boolean? = false
isDeletable: Boolean? = false,
valueHasPlaceholder: Boolean? = false
): BottomSheetFragment {
val bottomSheetFragment = row.bottomSheetType.newInstance()
bottomSheetFragment.show(fragmentManager, "bottomSheet")
@ -55,6 +57,7 @@ open class BottomSheetFragment : BottomSheetDialogFragment() {
bottomSheetFragment.rowRepresentableEditDescriptors = rowRepresentableEditDescriptors
bottomSheetFragment.isClearable = isClearable ?: true
bottomSheetFragment.isDeletable = isDeletable ?: true
bottomSheetFragment.valueHasPlaceholder = valueHasPlaceholder ?: true
bottomSheetFragment.currentCurrency = currentCurrency
return bottomSheetFragment
}

@ -49,13 +49,6 @@ open class BottomSheetListFragment : BottomSheetFragment(), LiveRowRepresentable
return RowViewType.BOTTOM_SHEET_DATA.ordinal
}
override fun indexForRow(row: RowRepresentable): Int {
realmData?.let {
return it.indexOf(row)
}
throw IllegalStateException("Need to implement Data Source")
}
override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) {
realmData?.let {
val selectedData = it[position]

@ -24,7 +24,6 @@ import net.pokeranalytics.android.ui.view.rowrepresentable.CustomFieldRow
import net.pokeranalytics.android.ui.view.rowrepresentable.SimpleRow
import net.pokeranalytics.android.util.NULL_TEXT
import java.util.*
import kotlin.collections.ArrayList
/**
* Custom EditableDataFragment to manage the Transaction data
@ -180,8 +179,8 @@ class CustomFieldDataFragment : EditableDataFragment(), StaticRowRepresentableDa
super.onRowDeleted(row)
when (row) {
is CustomFieldEntry -> {
if (!row.isValidForDelete(getRealm())) {
val status = row.getDeleteStatus(getRealm())
if (!row.isValidForDelete(requireContext(), getRealm())) {
val status = row.getDeleteStatus(requireContext(), getRealm())
val message = row.getFailedDeleteMessage(status)
showAlertDialog(requireContext(), R.string.cf_entry_delete_popup_title, message, showCancelButton = true, positiveAction = {
customField.deleteEntry(row)

@ -126,13 +126,13 @@ open class DataManagerFragment : RealmFragment() {
val deletable = this.item as Deletable
val realm = this.getRealm()
if (deletable.isValidForDelete(realm)) {
if (deletable.isValidForDelete(requireContext(), realm)) {
val intent = Intent()
intent.putExtra(DataListActivity.IntentKey.ITEM_DELETED.keyName, deletable.id)
activity?.setResult(Activity.RESULT_OK, intent)
activity?.finish()
} else {
val status = deletable.getDeleteStatus(realm)
val status = deletable.getDeleteStatus(requireContext(), realm)
val message = deletable.getFailedDeleteMessage(status)
val builder = AlertDialog.Builder(requireContext())
.setMessage(message)

@ -65,11 +65,9 @@ open class EditableDataFragment : DataManagerFragment(), RowRepresentableDelegat
val proxyItem: RealmModel? = this.liveDataType.getData(this.getRealm(), primaryKey)
proxyItem?.let {
//TODO: Localize
this.appBar.toolbar.title = "Update ${this.liveDataType.localizedTitle(requireContext()).toLowerCase().capitalize()}"
this.appBar.toolbar.title = this.liveDataType.updateEntityLocalizedTitle(requireContext())
deleteButtonShouldAppear = true
} ?: run {
//TODO: Localize
this.appBar.toolbar.title = this.liveDataType.newEntityLocalizedTitle(requireContext())
}

@ -57,54 +57,6 @@ interface FilterHandler {
* Manage filters
*/
fun manageFilters(fragment: Fragment) {
val context = fragment.requireContext()
val filterId = Preferences.getActiveFilterId(context)
FiltersActivity.newInstanceForResult(fragment = fragment, currentFilterable = currentFilterable)
//TODO: Clean
/*
val filterSelected = filterId != null
val realm = Realm.getDefaultInstance()
val count = realm.where<Filter>().count().toInt()
realm.close()
val choices = ArrayList<CharSequence>()
choices.add(fragment.getString(R.string.new_str))
if (count > 0) {
choices.add(fragment.getString(R.string.load_from_db))
}
if (filterSelected) {
choices.add(fragment.getString(R.string.modify_current_filter))
choices.add(fragment.getString(R.string.remove_filter))
}
val builder = AlertDialog.Builder(context)
builder.setTitle(R.string.filter_selection)
.setCancelable(true)
.setItems(choices.toTypedArray()) { _, which ->
Timber.d("Click on $which")
when (which) {
0 -> FiltersActivity.newInstanceForResult(fragment = fragment, currentFilterable = currentFilterable)
1 -> DataListActivity.newSelectInstance(fragment, LiveData.FILTER.ordinal)
2 -> FiltersActivity.newInstanceForResult(fragment = fragment, filterId = filterId, currentFilterable = currentFilterable)
3 -> {
Preferences.removeActiveFilterId(context)
filterWasUpdated = true
removeFilter()
}
}
}
.setNegativeButton(R.string.cancel) { _, _ ->
Timber.d("Click on cancel")
}
builder.show()
*/
}
}

@ -34,15 +34,13 @@ interface DefaultEditDataSource : EditDataSource, Localizable {
interface ImageDecorator {
val imageRes: Int?
get() {
return null
}
get() = null
val imageTint: Int?
get() {
return null
}
get() = 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.ui.adapter.RowRepresentableAdapter
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.setTextFormat
import net.pokeranalytics.android.ui.graph.AxisFormatting
@ -204,6 +205,14 @@ enum class RowViewType(private var layoutRes: Int) {
row.imageTint?.let { 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

@ -61,6 +61,8 @@
android:id="@+id/recyclerView"
android:layout_width="0dp"
android:layout_height="0dp"
android:paddingBottom="72dp"
android:clipToPadding="false"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"

@ -1,66 +1,79 @@
<?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"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="?selectableItemBackground">
android:layout_height="48dp">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="@+id/guidelineStart"
app:layout_constraintTop_toTopOf="parent"
tools:text="Title" />
<include layout="@layout/layout_swipe_to_delete" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/value"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:ellipsize="end"
android:gravity="end|center_vertical"
android:maxLines="1"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/action"
app:layout_constraintStart_toEndOf="@+id/title"
app:layout_constraintTop_toTopOf="parent"
tools:text="Value" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/foreground"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/gray_dark"
android:foreground="?selectableItemBackground">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/action"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginStart="16dp"
android:padding="4dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@+id/guidelineEnd"
app:layout_constraintStart_toEndOf="@+id/value"
app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/ic_close" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="@+id/guidelineStart"
app:layout_constraintTop_toTopOf="parent"
tools:text="Title" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guidelineStart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_begin="16dp" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/value"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="8dp"
android:ellipsize="end"
android:gravity="end|center_vertical"
android:maxLines="1"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/action"
app:layout_constraintStart_toEndOf="@+id/title"
app:layout_constraintTop_toTopOf="parent"
tools:text="Value" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guidelineEnd"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_end="16dp" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/action"
android:layout_width="32dp"
android:layout_height="32dp"
android:background="?selectableItemBackgroundBorderless"
android:padding="4dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@+id/guidelineEnd"
app:layout_constraintStart_toEndOf="@+id/value"
app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/ic_close"
tools:visibility="visible" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guidelineStart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_begin="16dp" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guidelineEnd"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_end="8dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

@ -31,7 +31,13 @@
<string name="launch_report">Launch Report</string>
<string name="progress">Progress</string>
<string name="save_report">Save Report</string>
<<<<<<< HEAD
<string name="import_confirmation">Do you want to proceed with the file import?</string>
=======
<string name="update_entity" formatted="false">Update %s</string>
<string name="comparison_chart">Comparison chart</string>
<string name="filter_currently_selected">The filter cannot be deleted because it is currently selected.</string>
>>>>>>> 5d02ab88722ad9a85d1b7d57dac44a54369a9b74
<string name="address">Address</string>
<string name="suggestions">Naming suggestions</string>

Loading…
Cancel
Save