Merge branch 'dev' of gitlab.com:stax-river/poker-analytics into dev

# Conflicts:
#	app/src/main/java/net/pokeranalytics/android/ui/fragment/BankrollFragment.kt
#	app/src/main/java/net/pokeranalytics/android/ui/fragment/DataListFragment.kt
#	app/src/main/java/net/pokeranalytics/android/ui/fragment/data/EditableDataFragment.kt
#	app/src/main/java/net/pokeranalytics/android/ui/fragment/report/AbstractReportFragment.kt
#	app/src/main/java/net/pokeranalytics/android/ui/fragment/report/ComparisonReportFragment.kt
#	app/src/main/java/net/pokeranalytics/android/ui/fragment/report/ProgressReportFragment.kt
#	app/src/main/java/net/pokeranalytics/android/ui/fragment/report/TableReportFragment.kt
#	app/src/main/res/layout/fragment_reports.xml
dev
Aurelien Hubert 7 years ago
commit 885671be31
  1. 63
      app/src/main/java/net/pokeranalytics/android/calculus/Calculator.kt
  2. 96
      app/src/main/java/net/pokeranalytics/android/model/LiveData.kt
  3. 5
      app/src/main/java/net/pokeranalytics/android/model/realm/Filter.kt
  4. 20
      app/src/main/java/net/pokeranalytics/android/model/realm/ReportSetup.kt
  5. 14
      app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt
  6. 39
      app/src/main/java/net/pokeranalytics/android/ui/activity/ComparisonReportActivity.kt
  7. 5
      app/src/main/java/net/pokeranalytics/android/ui/activity/EditableDataActivity.kt
  8. 26
      app/src/main/java/net/pokeranalytics/android/ui/activity/ProgressReportActivity.kt
  9. 42
      app/src/main/java/net/pokeranalytics/android/ui/activity/TableReportActivity.kt
  10. 39
      app/src/main/java/net/pokeranalytics/android/ui/activity/components/ReportActivity.kt
  11. 2
      app/src/main/java/net/pokeranalytics/android/ui/fragment/BankrollDetailsFragment.kt
  12. 35
      app/src/main/java/net/pokeranalytics/android/ui/fragment/BankrollFragment.kt
  13. 10
      app/src/main/java/net/pokeranalytics/android/ui/fragment/CalendarDetailsFragment.kt
  14. 68
      app/src/main/java/net/pokeranalytics/android/ui/fragment/DataListFragment.kt
  15. 2
      app/src/main/java/net/pokeranalytics/android/ui/fragment/FeedFragment.kt
  16. 51
      app/src/main/java/net/pokeranalytics/android/ui/fragment/ReportsFragment.kt
  17. 2
      app/src/main/java/net/pokeranalytics/android/ui/fragment/SessionFragment.kt
  18. 54
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/DeletableItemFragment.kt
  19. 50
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/bottomsheet/BottomSheetFragment.kt
  20. 4
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/bottomsheet/BottomSheetMultiSelectionFragment.kt
  21. 4
      app/src/main/java/net/pokeranalytics/android/ui/fragment/data/BankrollDataFragment.kt
  22. 2
      app/src/main/java/net/pokeranalytics/android/ui/fragment/data/CustomFieldDataFragment.kt
  23. 158
      app/src/main/java/net/pokeranalytics/android/ui/fragment/data/DataManagerFragment.kt
  24. 186
      app/src/main/java/net/pokeranalytics/android/ui/fragment/data/EditableDataFragment.kt
  25. 2
      app/src/main/java/net/pokeranalytics/android/ui/fragment/data/LocationDataFragment.kt
  26. 8
      app/src/main/java/net/pokeranalytics/android/ui/fragment/data/TransactionDataFragment.kt
  27. 119
      app/src/main/java/net/pokeranalytics/android/ui/fragment/report/AbstractReportFragment.kt
  28. 14
      app/src/main/java/net/pokeranalytics/android/ui/fragment/report/ComparisonReportFragment.kt
  29. 8
      app/src/main/java/net/pokeranalytics/android/ui/fragment/report/ComposableTableReportFragment.kt
  30. 15
      app/src/main/java/net/pokeranalytics/android/ui/fragment/report/ProgressReportFragment.kt
  31. 9
      app/src/main/java/net/pokeranalytics/android/ui/fragment/report/TableReportFragment.kt
  32. 71
      app/src/main/java/net/pokeranalytics/android/util/RealmExtensions.kt
  33. 21
      app/src/main/res/layout/dialog_edit_text.xml
  34. 2
      app/src/main/res/layout/fragment_bankroll.xml
  35. 2
      app/src/main/res/layout/fragment_data_list.xml
  36. 1
      app/src/main/res/layout/fragment_reports.xml
  37. 1
      app/src/main/res/layout/fragment_subscription.xml
  38. 1
      app/src/main/res/values/strings.xml

@ -11,8 +11,10 @@ import net.pokeranalytics.android.model.filter.Query
import net.pokeranalytics.android.model.filter.filter import net.pokeranalytics.android.model.filter.filter
import net.pokeranalytics.android.model.realm.ComputableResult import net.pokeranalytics.android.model.realm.ComputableResult
import net.pokeranalytics.android.model.realm.Filter import net.pokeranalytics.android.model.realm.Filter
import net.pokeranalytics.android.model.realm.ReportSetup
import net.pokeranalytics.android.model.realm.SessionSet import net.pokeranalytics.android.model.realm.SessionSet
import net.pokeranalytics.android.ui.activity.ComparisonReportActivity
import net.pokeranalytics.android.ui.activity.ProgressReportActivity
import net.pokeranalytics.android.ui.activity.TableReportActivity
import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.util.extensions.startOfDay import net.pokeranalytics.android.util.extensions.startOfDay
import java.util.* import java.util.*
@ -35,7 +37,8 @@ class Calculator {
query: Query = Query(), query: Query = Query(),
filter: Filter? = null, filter: Filter? = null,
var aggregationType: AggregationType? = null, var aggregationType: AggregationType? = null,
var userGenerated: Boolean = false var userGenerated: Boolean = false,
var reportSetupId: String? = null
) { ) {
var evolutionValues: EvolutionValues = evolutionValues var evolutionValues: EvolutionValues = evolutionValues
@ -47,17 +50,17 @@ class Calculator {
} }
private var _query: Query = query private var _query: Query = query
private var _filter: Filter? = filter var filter: Filter? = filter
init { init {
if (!this._query.conditions.isEmpty() && this._filter != null) { if (!this._query.conditions.isEmpty() && this.filter != null) {
throw IllegalStateException("Can't specify a query with conditions AND a filter") throw IllegalStateException("Can't specify a query with conditions AND a filter")
} }
} }
val query: Query val query: Query
get() { get() {
this._filter?.let { this.filter?.let {
return it.query return it.query
} }
return this._query return this._query
@ -84,6 +87,19 @@ class Calculator {
} }
} }
val activityClass: Class<*>
get() {
return when (this) {
TABLE -> TableReportActivity::class.java
PROGRESS -> ProgressReportActivity::class.java
COMPARISON -> ComparisonReportActivity::class.java
else -> throw IllegalStateException("undefined activity for report display")
// MAP -> R.string.map
// POLYNOMIAL -> null
}
}
} }
/** /**
@ -134,21 +150,21 @@ class Calculator {
} }
} }
fun reportSetup(name: String): ReportSetup { // fun reportSetup(name: String): ReportSetup {
//
val rs = ReportSetup() // val rs = ReportSetup()
rs.name = name // rs.name = name
rs.display = this.display.ordinal // rs.display = this.display.ordinal
this.stats.forEach { // this.stats.forEach {
rs.statIds.add(it.uniqueIdentifier) // rs.statIds.add(it.uniqueIdentifier)
} // }
this.criterias.forEach { // this.criterias.forEach {
rs.criteriaIds.add(it.uniqueIdentifier) // rs.criteriaIds.add(it.uniqueIdentifier)
} // }
rs.filter = this._filter // rs.filter = this.filter
//
return rs // return rs
} // }
/** /**
* Returns some default name * Returns some default name
@ -172,8 +188,11 @@ class Calculator {
stats: List<Stat>? = null stats: List<Stat>? = null
): Report { ): Report {
val options = Options(evolutionValues = Options.EvolutionValues.STANDARD, aggregationType = aggregationType) val options = Options(display = Options.Display.PROGRESS,
options.stats = listOf(stat) evolutionValues = Options.EvolutionValues.STANDARD,
stats = listOf(stat),
aggregationType = aggregationType)
if (aggregationType == AggregationType.DURATION) { if (aggregationType == AggregationType.DURATION) {
options.evolutionValues = Options.EvolutionValues.TIMED options.evolutionValues = Options.EvolutionValues.TIMED
} }

@ -2,13 +2,9 @@ package net.pokeranalytics.android.model
import android.content.Context import android.content.Context
import io.realm.Realm import io.realm.Realm
import io.realm.RealmObject import io.realm.RealmModel
import io.realm.RealmResults
import io.realm.Sort
import io.realm.kotlin.where
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.interfaces.CountableUsage import net.pokeranalytics.android.model.interfaces.Identifiable
import net.pokeranalytics.android.model.interfaces.Deletable
import net.pokeranalytics.android.model.realm.* import net.pokeranalytics.android.model.realm.*
import net.pokeranalytics.android.ui.view.Localizable import net.pokeranalytics.android.ui.view.Localizable
@ -24,76 +20,10 @@ enum class LiveData : Localizable {
TRANSACTION, TRANSACTION,
TRANSACTION_TYPE, TRANSACTION_TYPE,
FILTER, FILTER,
CUSTOM_FIELD; CUSTOM_FIELD,
REPORT_SETUP;
fun items(realm: Realm, fieldName: String? = null, sortOrder: Sort? = null): RealmResults<*> { val relatedEntity: Class<out Identifiable>
val results = realm.where(this.relatedEntity).findAll().sort(fieldName ?: this.sortingFieldName, sortOrder ?: this.sorting)
if (results.size > 0) {
if (results.first() is CountableUsage) {
this.setUseCount(realm, results)
return results.sort("useCount", Sort.DESCENDING)
}
}
return results
}
fun setUseCount(realm: Realm, realmResults: RealmResults<*>) {
realm.executeTransaction {
realmResults.forEach { countableUsage ->
when (this) {
TOURNAMENT_FEATURE -> {
(countableUsage as CountableUsage).useCount = it.where<Session>().contains(
"tournamentFeatures.id",
countableUsage.id
).count().toInt()
}
else -> {
(countableUsage as CountableUsage).useCount = it.where<Session>().equalTo(
"${relatedEntity.simpleName.decapitalize()}.id",
countableUsage.id
).count().toInt()
}
}
}
}
}
/**
* Return a copy of a RealmResults
*/
fun itemsArray(realm: Realm, fieldName: String? = null, sortOrder: Sort? = null): ArrayList<*> {
val results: ArrayList<Any> = ArrayList()
results.addAll(
realm.copyFromRealm(
realm.where(this.relatedEntity).findAll().sort(
fieldName ?: this.sortingFieldName, sortOrder ?: this.sorting
)
)
)
return results
}
private val sortingFieldName: String
get() {
return when (this) {
TRANSACTION -> "date"
else -> "name"
}
}
private val sorting: Sort
get() {
return when (this) {
TRANSACTION -> Sort.DESCENDING
else -> Sort.ASCENDING
}
}
private val relatedEntity: Class<out RealmObject>
get() { get() {
return when (this) { return when (this) {
BANKROLL -> Bankroll::class.java BANKROLL -> Bankroll::class.java
@ -105,15 +35,12 @@ enum class LiveData : Localizable {
TRANSACTION_TYPE -> TransactionType::class.java TRANSACTION_TYPE -> TransactionType::class.java
FILTER -> Filter::class.java FILTER -> Filter::class.java
CUSTOM_FIELD -> CustomField::class.java CUSTOM_FIELD -> CustomField::class.java
REPORT_SETUP -> ReportSetup::class.java
} }
} }
fun deleteData(realm: Realm, data: Deletable) { fun updateOrCreate(realm: Realm, primaryKey: String?): RealmModel {
realm.where(this.relatedEntity).equalTo("id", data.id).findAll().deleteAllFromRealm() val proxyItem: Identifiable? = this.getData(realm, primaryKey)
}
fun updateOrCreate(realm: Realm, primaryKey: String?): RealmObject {
val proxyItem: RealmObject? = this.getData(realm, primaryKey)
proxyItem?.let { proxyItem?.let {
return realm.copyFromRealm(it) return realm.copyFromRealm(it)
} ?: run { } ?: run {
@ -121,12 +48,12 @@ enum class LiveData : Localizable {
} }
} }
fun newEntity(): RealmObject { private fun newEntity(): RealmModel{
return this.relatedEntity.newInstance() return this.relatedEntity.newInstance()
} }
fun getData(realm: Realm, primaryKey: String?): RealmObject? { fun getData(realm: Realm, primaryKey: String?): Identifiable? {
var proxyItem: RealmObject? = null var proxyItem: Identifiable? = null
primaryKey?.let { primaryKey?.let {
val t = realm.where(this.relatedEntity).equalTo("id", it).findFirst() val t = realm.where(this.relatedEntity).equalTo("id", it).findFirst()
t?.let { t?.let {
@ -148,6 +75,7 @@ enum class LiveData : Localizable {
TRANSACTION_TYPE -> R.string.operation_types TRANSACTION_TYPE -> R.string.operation_types
FILTER -> R.string.filter FILTER -> R.string.filter
CUSTOM_FIELD -> R.string.custom_fields CUSTOM_FIELD -> R.string.custom_fields
REPORT_SETUP -> R.string.custom
} }
} }

@ -7,6 +7,7 @@ import io.realm.kotlin.where
import net.pokeranalytics.android.model.filter.Filterable import net.pokeranalytics.android.model.filter.Filterable
import net.pokeranalytics.android.model.filter.Query import net.pokeranalytics.android.model.filter.Query
import net.pokeranalytics.android.model.filter.QueryCondition import net.pokeranalytics.android.model.filter.QueryCondition
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.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.rowrepresentable.FilterCategoryRow import net.pokeranalytics.android.ui.view.rowrepresentable.FilterCategoryRow
@ -17,7 +18,7 @@ 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 { open class Filter : RealmObject(), RowRepresentable, Identifiable {
companion object { companion object {
@ -49,7 +50,7 @@ open class Filter : RealmObject(), RowRepresentable {
} }
@PrimaryKey @PrimaryKey
var id = UUID.randomUUID().toString() override var id = UUID.randomUUID().toString()
// the queryWith name // the queryWith name
var name: String = "" var name: String = ""

@ -1,6 +1,7 @@
package net.pokeranalytics.android.model.realm package net.pokeranalytics.android.model.realm
import android.content.Context import android.content.Context
import io.realm.Realm
import io.realm.RealmList import io.realm.RealmList
import io.realm.RealmObject import io.realm.RealmObject
import io.realm.annotations.Ignore import io.realm.annotations.Ignore
@ -8,15 +9,17 @@ import io.realm.annotations.PrimaryKey
import net.pokeranalytics.android.calculus.Calculator import net.pokeranalytics.android.calculus.Calculator
import net.pokeranalytics.android.calculus.Stat import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.model.Criteria import net.pokeranalytics.android.model.Criteria
import net.pokeranalytics.android.model.interfaces.Deletable
import net.pokeranalytics.android.model.interfaces.DeleteValidityStatus
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.RowViewType
import java.util.* import java.util.*
open class ReportSetup : RealmObject(), RowRepresentable { open class ReportSetup : RealmObject(), RowRepresentable, Deletable {
@PrimaryKey @PrimaryKey
var id = UUID.randomUUID().toString() override var id = UUID.randomUUID().toString()
// The name of the report // The name of the report
var name: String = "" var name: String = ""
@ -60,8 +63,19 @@ open class ReportSetup : RealmObject(), RowRepresentable {
stats = stats, stats = stats,
criterias = criteria, criterias = criteria,
filter = this.filter, filter = this.filter,
userGenerated = true userGenerated = true,
reportSetupId = this.id
) )
} }
// Deletable
override fun isValidForDelete(realm: Realm): Boolean {
return true
}
override fun getFailedDeleteMessage(status: DeleteValidityStatus): Int {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
} }

@ -18,7 +18,6 @@ import net.pokeranalytics.android.calculus.StatFormattingException
import net.pokeranalytics.android.calculus.TextFormat import net.pokeranalytics.android.calculus.TextFormat
import net.pokeranalytics.android.exceptions.ModelException import net.pokeranalytics.android.exceptions.ModelException
import net.pokeranalytics.android.model.Limit import net.pokeranalytics.android.model.Limit
import net.pokeranalytics.android.model.LiveData
import net.pokeranalytics.android.model.TableSize import net.pokeranalytics.android.model.TableSize
import net.pokeranalytics.android.model.TournamentType import net.pokeranalytics.android.model.TournamentType
import net.pokeranalytics.android.model.extensions.SessionState import net.pokeranalytics.android.model.extensions.SessionState
@ -39,6 +38,7 @@ import net.pokeranalytics.android.ui.view.rowrepresentable.SessionRow
import net.pokeranalytics.android.util.NULL_TEXT import net.pokeranalytics.android.util.NULL_TEXT
import net.pokeranalytics.android.util.UserDefaults import net.pokeranalytics.android.util.UserDefaults
import net.pokeranalytics.android.util.extensions.* import net.pokeranalytics.android.util.extensions.*
import net.pokeranalytics.android.util.sorted
import java.text.DateFormat import java.text.DateFormat
import java.util.* import java.util.*
import java.util.Currency import java.util.Currency
@ -697,7 +697,7 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
// Add custom fields // Add custom fields
realm?.let { realm?.let {
rows.add(SeparatorRow()) rows.add(SeparatorRow())
rows.addAll(LiveData.CUSTOM_FIELD.itemsArray(it) as ArrayList<RowRepresentable>) rows.addAll(it.sorted<CustomField>())
} }
return rows return rows
@ -778,32 +778,32 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
SessionRow.BANKROLL -> row.editingDescriptors( SessionRow.BANKROLL -> row.editingDescriptors(
mapOf( mapOf(
"defaultValue" to this.bankroll, "defaultValue" to this.bankroll,
"data" to LiveData.BANKROLL.items(realm) "data" to realm.sorted<Bankroll>() // LiveData.BANKROLL.items(realm)
) )
) )
SessionRow.GAME -> row.editingDescriptors( SessionRow.GAME -> row.editingDescriptors(
mapOf( mapOf(
"limit" to this.limit, "limit" to this.limit,
"defaultValue" to this.game, "defaultValue" to this.game,
"data" to LiveData.GAME.items(realm) "data" to realm.sorted<Game>() //LiveData.GAME.items(realm)
) )
) )
SessionRow.LOCATION -> row.editingDescriptors( SessionRow.LOCATION -> row.editingDescriptors(
mapOf( mapOf(
"defaultValue" to this.location, "defaultValue" to this.location,
"data" to LiveData.LOCATION.items(realm) "data" to realm.sorted<Location>() // LiveData.LOCATION.items(realm)
) )
) )
SessionRow.TOURNAMENT_FEATURE -> row.editingDescriptors( SessionRow.TOURNAMENT_FEATURE -> row.editingDescriptors(
mapOf( mapOf(
"defaultValue" to this.tournamentFeatures, "defaultValue" to this.tournamentFeatures,
"data" to LiveData.TOURNAMENT_FEATURE.items(realm) "data" to realm.sorted<TournamentFeature>() //LiveData.TOURNAMENT_FEATURE.items(realm)
) )
) )
SessionRow.TOURNAMENT_NAME -> row.editingDescriptors( SessionRow.TOURNAMENT_NAME -> row.editingDescriptors(
mapOf( mapOf(
"defaultValue" to this.tournamentName, "defaultValue" to this.tournamentName,
"data" to LiveData.TOURNAMENT_NAME.items(realm) "data" to realm.sorted<TournamentName>() //LiveData.TOURNAMENT_NAME.items(realm)
) )
) )
SessionRow.TOURNAMENT_TYPE -> row.editingDescriptors( SessionRow.TOURNAMENT_TYPE -> row.editingDescriptors(

@ -1,35 +1,12 @@
package net.pokeranalytics.android.ui.activity package net.pokeranalytics.android.ui.activity
import android.content.Context
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.Report import net.pokeranalytics.android.ui.activity.components.ReportActivity
import net.pokeranalytics.android.ui.activity.components.PokerAnalyticsActivity
import net.pokeranalytics.android.ui.fragment.report.ComparisonReportFragment import net.pokeranalytics.android.ui.fragment.report.ComparisonReportFragment
class ComparisonReportActivity : ReportActivity() {
class ComparisonReportActivity : PokerAnalyticsActivity() {
companion object {
// Unparcel fails when setting a custom Parcelable object on Entry so we use a static reference to passe objects
private var report: Report? = null
private var reportTitle: String = ""
/**
* Default constructor
*/
fun newInstance(context: Context, report: Report, reportTitle: String) {
//parameters = GraphParameters(stat, group, report)
this.report = report
this.reportTitle = reportTitle
val intent = Intent(context, ComparisonReportActivity::class.java)
context.startActivity(intent)
}
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -42,14 +19,18 @@ class ComparisonReportActivity : PokerAnalyticsActivity() {
*/ */
private fun initUI() { private fun initUI() {
report?.let { parameters?.let {
val report = it.report
val title = it.title
val fragmentTransaction = supportFragmentManager.beginTransaction() val fragmentTransaction = supportFragmentManager.beginTransaction()
val reportDetailsFragment = ComparisonReportFragment.newInstance(it, reportTitle) val reportDetailsFragment = ComparisonReportFragment.newInstance(report, title)
fragmentTransaction.add(R.id.reportDetailsContainer, reportDetailsFragment) fragmentTransaction.add(R.id.reportDetailsContainer, reportDetailsFragment)
fragmentTransaction.commit() fragmentTransaction.commit()
report = null
} }
parameters = null
} }
} }

@ -10,6 +10,7 @@ import net.pokeranalytics.android.ui.activity.components.PokerAnalyticsActivity
import net.pokeranalytics.android.ui.fragment.data.* import net.pokeranalytics.android.ui.fragment.data.*
class EditableDataActivity : PokerAnalyticsActivity() { class EditableDataActivity : PokerAnalyticsActivity() {
enum class IntentKey(val keyName: String) { enum class IntentKey(val keyName: String) {
DATA_TYPE("DATA_TYPE"), DATA_TYPE("DATA_TYPE"),
PRIMARY_KEY("PRIMARY_KEY"); PRIMARY_KEY("PRIMARY_KEY");
@ -31,9 +32,9 @@ class EditableDataActivity : PokerAnalyticsActivity() {
/** /**
* Create a new instance for result * Create a new instance for result
*/ */
fun newInstanceForResult(fragment: Fragment, dataType: Int, primaryKey: String? = null, requestCode: Int) { fun newInstanceForResult(fragment: Fragment, dataType: LiveData, primaryKey: String? = null, requestCode: Int) {
val intent = Intent(fragment.requireContext(), EditableDataActivity::class.java) val intent = Intent(fragment.requireContext(), EditableDataActivity::class.java)
intent.putExtra(IntentKey.DATA_TYPE.keyName, dataType) intent.putExtra(IntentKey.DATA_TYPE.keyName, dataType.ordinal)
primaryKey?.let { primaryKey?.let {
intent.putExtra(IntentKey.PRIMARY_KEY.keyName, it) intent.putExtra(IntentKey.PRIMARY_KEY.keyName, it)
} }

@ -3,33 +3,35 @@ package net.pokeranalytics.android.ui.activity
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.fragment.app.Fragment
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.Report import net.pokeranalytics.android.calculus.Report
import net.pokeranalytics.android.calculus.Stat import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.ui.activity.components.PokerAnalyticsActivity import net.pokeranalytics.android.ui.activity.components.ReportActivity
import net.pokeranalytics.android.ui.activity.components.ReportParameters
import net.pokeranalytics.android.ui.fragment.report.ProgressReportFragment import net.pokeranalytics.android.ui.fragment.report.ProgressReportFragment
class ProgressReportActivity : ReportActivity() {
class StatisticsDetailsParameters(var stat: Stat, var report: Report, var title: String? = null)
class ProgressReportActivity : PokerAnalyticsActivity() {
companion object { companion object {
// Unparcel fails when setting a custom Parcelable object on Entry so we use a static reference to passe objects // Unparcel fails when setting a custom Parcelable object on Entry so we use a static reference to passe objects
private var parameters: StatisticsDetailsParameters? = null
private var displayAggregationChoices: Boolean = true
/** /**
* Default constructor * Default constructor
*/ */
fun newInstance(context: Context, stat: Stat, report: Report, displayAggregationChoices: Boolean = true, title: String? = null) { fun newInstance(context: Context, report: Report, title: String, stat: Stat? = null, displayAggregationChoices: Boolean = true) {
parameters = StatisticsDetailsParameters(stat, report, title) parameters = ReportParameters(report, title, stat, showAggregationChoices = displayAggregationChoices)
this.displayAggregationChoices = displayAggregationChoices
val intent = Intent(context, ProgressReportActivity::class.java) val intent = Intent(context, ProgressReportActivity::class.java)
context.startActivity(intent) context.startActivity(intent)
} }
fun newInstanceForResult(fragment: Fragment, report: Report, title: String, stat: Stat? = null, displayAggregationChoices: Boolean = true) {
parameters = ReportParameters(report, title, stat, showAggregationChoices = displayAggregationChoices)
val intent = Intent(fragment.context, ProgressReportActivity::class.java)
fragment.startActivityForResult(intent, DEFAULT_REQUEST_CODE)
}
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -49,7 +51,9 @@ class ProgressReportActivity : PokerAnalyticsActivity() {
fragmentTransaction.commit() fragmentTransaction.commit()
parameters?.let { parameters?.let {
statisticDetailsFragment.setData(it.stat, it.report, displayAggregationChoices, it.title) val report = it.report
val stat = it.stat ?: report.options.stats.first()
statisticDetailsFragment.setData(report, stat, it.showAggregationChoices, it.title)
parameters = null parameters = null
} }

@ -1,32 +1,11 @@
package net.pokeranalytics.android.ui.activity package net.pokeranalytics.android.ui.activity
import android.content.Context
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.Report import net.pokeranalytics.android.ui.activity.components.ReportActivity
import net.pokeranalytics.android.ui.activity.components.PokerAnalyticsActivity
import net.pokeranalytics.android.ui.fragment.report.TableReportFragment import net.pokeranalytics.android.ui.fragment.report.TableReportFragment
class TableReportActivity : PokerAnalyticsActivity() { class TableReportActivity : ReportActivity() {
companion object {
// Unparcel fails when setting a custom Parcelable object on Entry so we use a static reference to passe objects
private var report: Report? = null
private var reportTitle: String = ""
/**
* Default constructor
*/
fun newInstance(context: Context, report: Report, reportTitle: String) {
this.report = report
this.reportTitle = reportTitle
val intent = Intent(context, TableReportActivity::class.java)
context.startActivity(intent)
}
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -39,11 +18,18 @@ class TableReportActivity : PokerAnalyticsActivity() {
*/ */
private fun initUI() { private fun initUI() {
val fragmentTransaction = supportFragmentManager.beginTransaction() parameters?.let {
val fragment = TableReportFragment.newInstance(report, reportTitle)
fragmentTransaction.add(R.id.reportDetailsContainer, fragment) val report = it.report
fragmentTransaction.commit() val title = it.title
report = null
val fragmentTransaction = supportFragmentManager.beginTransaction()
val fragment = TableReportFragment.newInstance(report, title)
fragmentTransaction.add(R.id.reportDetailsContainer, fragment)
fragmentTransaction.commit()
}
parameters = null
} }
} }

@ -0,0 +1,39 @@
package net.pokeranalytics.android.ui.activity.components
import android.content.Context
import android.content.Intent
import androidx.fragment.app.Fragment
import net.pokeranalytics.android.calculus.Report
import net.pokeranalytics.android.calculus.Stat
class ReportParameters(var report: Report, var title: String, var stat: Stat? = null, var showAggregationChoices: Boolean = true)
abstract class ReportActivity : PokerAnalyticsActivity() {
companion object {
const val DEFAULT_REQUEST_CODE = 999
// Unparcel fails when setting a custom Parcelable object on Entry so we use a static reference to passe objects
var parameters: ReportParameters? = null
/**
* Default constructor
*/
fun newInstance(context: Context, report: Report, reportTitle: String, stat: Stat? = null) {
val options = report.options
this.parameters = ReportParameters(report, reportTitle, stat)
val intent = Intent(context, options.display.activityClass)
context.startActivity(intent)
}
fun newInstanceForResult(fragment: Fragment, report: Report, reportTitle: String, stat: Stat? = null) {
val options = report.options
this.parameters = ReportParameters(report, reportTitle, stat)
val intent = Intent(fragment.requireContext(), options.display.activityClass)
fragment.startActivityForResult(intent, DEFAULT_REQUEST_CODE)
}
}
}

@ -161,7 +161,7 @@ class BankrollDetailsFragment : PokerAnalyticsFragment(), StaticRowRepresentable
* Open Bankroll edit activity * Open Bankroll edit activity
*/ */
private fun editBankroll() { private fun editBankroll() {
EditableDataActivity.newInstanceForResult(this, LiveData.BANKROLL.ordinal, bankrollReport.setup.bankroll?.id, REQUEST_CODE_EDIT) EditableDataActivity.newInstanceForResult(this, LiveData.BANKROLL, bankrollReport.setup.bankroll?.id, REQUEST_CODE_EDIT)
} }
} }

@ -28,6 +28,7 @@ import net.pokeranalytics.android.ui.activity.BankrollDetailsActivity
import net.pokeranalytics.android.ui.activity.DataListActivity import net.pokeranalytics.android.ui.activity.DataListActivity
import net.pokeranalytics.android.ui.activity.EditableDataActivity import net.pokeranalytics.android.ui.activity.EditableDataActivity
import net.pokeranalytics.android.ui.activity.GraphActivity import net.pokeranalytics.android.ui.activity.GraphActivity
import net.pokeranalytics.android.ui.activity.components.PokerAnalyticsActivity
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.adapter.StaticRowRepresentableDataSource import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource
@ -36,6 +37,8 @@ import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable
import net.pokeranalytics.android.ui.view.rowrepresentable.GraphRow import net.pokeranalytics.android.ui.view.rowrepresentable.GraphRow
import net.pokeranalytics.android.util.sorted
import net.pokeranalytics.android.util.sorted
import timber.log.Timber import timber.log.Timber
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
@ -66,6 +69,12 @@ class BankrollFragment : DeletableItemFragment(), StaticRowRepresentableDataSour
private var lastItemClickedId: String = "" private var lastItemClickedId: String = ""
private var deletedRow: RowRepresentable? = null private var deletedRow: RowRepresentable? = null
private lateinit var bankrolls: RealmResults<Bankroll>
override fun deletableItems() : List<Identifiable> {
return this.bankrolls
}
// Life Cycle // Life Cycle
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
@ -83,13 +92,14 @@ class BankrollFragment : DeletableItemFragment(), StaticRowRepresentableDataSour
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_CODE_DETAILS && resultCode == Activity.RESULT_OK) { if (requestCode == REQUEST_CODE_DETAILS && resultCode == Activity.RESULT_OK) {
val needToDeleteItem = data?.getBooleanExtra(DataListActivity.IntentKey.ITEM_DELETED.keyName, false) ?: false val itemToDeleteId = data?.getStringExtra(DataListActivity.IntentKey.ITEM_DELETED.keyName)
if (needToDeleteItem) { itemToDeleteId?.let { id ->
GlobalScope.launch(Dispatchers.Main) { GlobalScope.launch(Dispatchers.Main) {
delay(300) delay(300)
deleteItem(bankrollAdapter, LiveData.BANKROLL.items(getRealm()), lastItemClickedId) deleteItem(dataListAdapter, bankrolls, id)
} }
} }
} else if (requestCode == REQUEST_CODE_CREATE && resultCode == Activity.RESULT_OK) { } else if (requestCode == REQUEST_CODE_CREATE && resultCode == Activity.RESULT_OK) {
//TODO: Refresh bankrolls //TODO: Refresh bankrolls
initData() initData()
@ -126,6 +136,9 @@ class BankrollFragment : DeletableItemFragment(), StaticRowRepresentableDataSour
*/ */
private fun initData() { private fun initData() {
val realm = getRealm()
this.bankrolls = realm.sorted()
rows.clear() rows.clear()
bankrollReportForRow.clear() bankrollReportForRow.clear()
@ -150,7 +163,7 @@ class BankrollFragment : DeletableItemFragment(), StaticRowRepresentableDataSour
Timber.d("initData: ${System.currentTimeMillis() - startDate.time}ms") Timber.d("initData: ${System.currentTimeMillis() - startDate.time}ms")
val bankrolls = LiveData.BANKROLL.items(getRealm()) as RealmResults<Bankroll> // val bankrolls = LiveData.BANKROLL.items(getRealm()) as RealmResults<Bankroll>
bankrolls.forEach { bankroll -> bankrolls.forEach { bankroll ->
val bankrollReportSetup = BankrollReportSetup(bankroll) val bankrollReportSetup = BankrollReportSetup(bankroll)
@ -165,7 +178,7 @@ class BankrollFragment : DeletableItemFragment(), StaticRowRepresentableDataSour
} }
if (!isDetached) { if (!isDetached) {
bankrollAdapter.notifyDataSetChanged() dataListAdapter.notifyDataSetChanged()
} }
} }
} }
@ -179,20 +192,18 @@ class BankrollFragment : DeletableItemFragment(), StaticRowRepresentableDataSour
setDisplayHomeAsUpEnabled(true) setDisplayHomeAsUpEnabled(true)
bankrollAdapter = RowRepresentableAdapter(this, this) dataListAdapter = RowRepresentableAdapter(this, this)
val viewManager = LinearLayoutManager(requireContext()) val viewManager = LinearLayoutManager(requireContext())
recyclerView.apply { recyclerView.apply {
setHasFixedSize(true) setHasFixedSize(true)
layoutManager = viewManager layoutManager = viewManager
adapter = bankrollAdapter adapter = dataListAdapter
} }
addButton.setOnClickListener { addButton.setOnClickListener {
LiveData.BANKROLL?.let { EditableDataActivity.newInstanceForResult(this@BankrollFragment, dataType = LiveData.BANKROLL, primaryKey = null, requestCode = REQUEST_CODE_CREATE)
EditableDataActivity.newInstanceForResult(this@BankrollFragment, dataType = it.ordinal, primaryKey = null, requestCode = REQUEST_CODE_CREATE)
}
} }
} }
@ -200,7 +211,7 @@ class BankrollFragment : DeletableItemFragment(), StaticRowRepresentableDataSour
lastItemClickedPosition = rows.indexOfFirst { if (it is Identifiable) it.id == lastItemClickedId else false } lastItemClickedPosition = rows.indexOfFirst { if (it is Identifiable) it.id == lastItemClickedId else false }
deletedRow = rows.find { if (it is Identifiable) it.id == lastItemClickedId else false } deletedRow = rows.find { if (it is Identifiable) it.id == lastItemClickedId else false }
rows.removeAt(lastItemClickedPosition) rows.removeAt(lastItemClickedPosition)
bankrollAdapter.notifyItemRemoved(lastItemClickedPosition) dataListAdapter.notifyItemRemoved(lastItemClickedPosition)
} }
override fun updateUIAfterUndoDeletion(newItem: RealmObject) { override fun updateUIAfterUndoDeletion(newItem: RealmObject) {
@ -214,7 +225,7 @@ class BankrollFragment : DeletableItemFragment(), StaticRowRepresentableDataSour
bankrollReportForRow[row] = bankrollReport bankrollReportForRow[row] = bankrollReport
rows.add(lastItemClickedPosition, row) rows.add(lastItemClickedPosition, row)
bankrollAdapter.notifyItemInserted(lastItemClickedPosition) dataListAdapter.notifyItemInserted(lastItemClickedPosition)
} }
} }

@ -137,10 +137,12 @@ class CalendarDetailsFragment : PokerAnalyticsFragment(), StaticRowRepresentable
override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) { override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) {
when (row) { when (row) {
is GraphRow -> { is GraphRow -> {
row.report?.let { report -> val report = row.report
row.stat?.let { stat -> val stat = row.stat
ProgressReportActivity.newInstance(requireContext(), stat, report, false, row.title)
} if (report != null && stat != null) {
val title = row.title ?: stat.localizedTitle(requireContext())
ProgressReportActivity.newInstance(requireContext(), report, title, stat, false)
} }
} }
} }

@ -10,15 +10,10 @@ import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import io.realm.RealmResults import io.realm.RealmResults
import kotlinx.android.synthetic.main.fragment_data_list.* import kotlinx.android.synthetic.main.fragment_data_list.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.LiveData import net.pokeranalytics.android.model.LiveData
import net.pokeranalytics.android.model.interfaces.Identifiable import net.pokeranalytics.android.model.interfaces.Identifiable
import net.pokeranalytics.android.model.realm.Filter import net.pokeranalytics.android.model.realm.Filter
import net.pokeranalytics.android.ui.activity.DataListActivity
import net.pokeranalytics.android.ui.activity.EditableDataActivity import net.pokeranalytics.android.ui.activity.EditableDataActivity
import net.pokeranalytics.android.ui.activity.FiltersActivity import net.pokeranalytics.android.ui.activity.FiltersActivity
import net.pokeranalytics.android.ui.adapter.LiveRowRepresentableDataSource import net.pokeranalytics.android.ui.adapter.LiveRowRepresentableDataSource
@ -28,6 +23,8 @@ 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
import net.pokeranalytics.android.ui.view.RowViewType import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.util.sorted
class DataListFragment : DeletableItemFragment(), LiveRowRepresentableDataSource, RowRepresentableDelegate { class DataListFragment : DeletableItemFragment(), LiveRowRepresentableDataSource, RowRepresentableDelegate {
@ -35,20 +32,27 @@ class DataListFragment : DeletableItemFragment(), LiveRowRepresentableDataSource
const val REQUEST_CODE_DETAILS = 1000 const val REQUEST_CODE_DETAILS = 1000
} }
private lateinit var identifiableClass: Class<out Identifiable>
private lateinit var dataType: LiveData private lateinit var dataType: LiveData
private lateinit var items: RealmResults<*> private lateinit var items: RealmResults<out Identifiable>
private lateinit var dataListAdapter: RowRepresentableAdapter
private var lastItemClickedId: String = "" override fun deletableItems() : List<Identifiable> {
return this.items
}
/** /**
* Set fragment data * Set fragment data
*/ */
fun setData(dataType: Int) { fun setData(dataType: Int) {
this.dataType = LiveData.values()[dataType] this.dataType = LiveData.values()[dataType]
this.identifiableClass = this.dataType.relatedEntity
setToolbarTitle(this.dataType.localizedTitle(requireContext())) setToolbarTitle(this.dataType.localizedTitle(requireContext()))
this.dataType.let {
this.items = it.items(getRealm()) 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? {
@ -61,7 +65,6 @@ class DataListFragment : DeletableItemFragment(), LiveRowRepresentableDataSource
initUI() initUI()
} }
/** /**
* Init UI * Init UI
*/ */
@ -87,27 +90,11 @@ class DataListFragment : DeletableItemFragment(), LiveRowRepresentableDataSource
} }
this.addButton.setOnClickListener { this.addButton.setOnClickListener {
this.dataType?.let { EditableDataActivity.newInstance(
EditableDataActivity.newInstance( requireContext(),
requireContext(), dataType = this.dataType.ordinal,
dataType = it.ordinal, primaryKey = null
primaryKey = null )
)
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_CODE_DETAILS && resultCode == Activity.RESULT_OK) {
val needToDeleteItem =
data?.getBooleanExtra(DataListActivity.IntentKey.ITEM_DELETED.keyName, false) ?: false
if (needToDeleteItem) {
GlobalScope.launch(Dispatchers.Main) {
delay(300)
deleteItem(dataListAdapter, items, lastItemClickedId)
}
}
} }
} }
@ -130,21 +117,24 @@ class DataListFragment : DeletableItemFragment(), LiveRowRepresentableDataSource
} }
override fun indexForRow(row: RowRepresentable): Int { override fun indexForRow(row: RowRepresentable): Int {
return this.items.indexOf(row) return this.items.indexOf(row as Identifiable)
} }
override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) { override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) {
this.dataType?.let {
if (it == LiveData.FILTER) { when (this.dataType) {
LiveData.FILTER -> {
val intent = Intent() val intent = Intent()
intent.putExtra(FiltersActivity.IntentKey.FILTER_ID.keyName, (row as Filter).id) intent.putExtra(FiltersActivity.IntentKey.FILTER_ID.keyName, (row as Filter).id)
activity?.setResult(Activity.RESULT_OK, intent) activity?.setResult(Activity.RESULT_OK, intent)
activity?.finish() activity?.finish()
} else { }
lastItemClickedId = (row as Identifiable).id else -> {
EditableDataActivity.newInstanceForResult(this, it.ordinal, lastItemClickedId, REQUEST_CODE_DETAILS) val identifier = (row as Identifiable).id
EditableDataActivity.newInstanceForResult(this, this.dataType, identifier, REQUEST_CODE_DETAILS)
} }
} }
} }
} }

@ -105,7 +105,7 @@ class FeedFragment : RealmFragment(), RowRepresentableDelegate {
is Transaction -> { is Transaction -> {
selectedTransaction = row selectedTransaction = row
selectedTransactionPosition = position selectedTransactionPosition = position
EditableDataActivity.newInstanceForResult(this, LiveData.TRANSACTION.ordinal, row.id, REQUEST_CODE_TRANSACTION_DETAILS) EditableDataActivity.newInstanceForResult(this, LiveData.TRANSACTION, row.id, REQUEST_CODE_TRANSACTION_DETAILS)
} }
} }
} }

@ -13,21 +13,22 @@ import io.realm.RealmResults
import kotlinx.android.synthetic.main.fragment_data_list.* import kotlinx.android.synthetic.main.fragment_data_list.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.Calculator import net.pokeranalytics.android.calculus.Calculator
import net.pokeranalytics.android.calculus.Stat import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.model.Criteria import net.pokeranalytics.android.model.Criteria
import net.pokeranalytics.android.model.combined import net.pokeranalytics.android.model.combined
import net.pokeranalytics.android.model.interfaces.Identifiable
import net.pokeranalytics.android.model.realm.ReportSetup import net.pokeranalytics.android.model.realm.ReportSetup
import net.pokeranalytics.android.ui.activity.ComparisonReportActivity import net.pokeranalytics.android.ui.activity.DataListActivity
import net.pokeranalytics.android.ui.activity.ProgressReportActivity
import net.pokeranalytics.android.ui.activity.ReportCreationActivity import net.pokeranalytics.android.ui.activity.ReportCreationActivity
import net.pokeranalytics.android.ui.activity.TableReportActivity import net.pokeranalytics.android.ui.activity.components.ReportActivity
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.adapter.StaticRowRepresentableDataSource import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource
import net.pokeranalytics.android.ui.fragment.components.RealmFragment import net.pokeranalytics.android.ui.fragment.components.DeletableItemFragment
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.RowViewType
import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable
@ -35,12 +36,16 @@ import net.pokeranalytics.android.ui.view.rowrepresentable.ReportRow
import timber.log.Timber import timber.log.Timber
import java.util.* import java.util.*
class ReportsFragment : RealmFragment(), StaticRowRepresentableDataSource, RowRepresentableDelegate { class ReportsFragment : DeletableItemFragment(), StaticRowRepresentableDataSource, RowRepresentableDelegate {
private lateinit var reportsAdapter: RowRepresentableAdapter // private lateinit var dataListAdapter: RowRepresentableAdapter
private lateinit var reportSetups: RealmResults<ReportSetup> private lateinit var reportSetups: RealmResults<ReportSetup>
private var adapterRows = mutableListOf<RowRepresentable>() private var adapterRows = mutableListOf<RowRepresentable>()
override fun deletableItems(): List<Identifiable> {
return this.reportSetups
}
companion object { companion object {
/** /**
@ -77,9 +82,17 @@ class ReportsFragment : RealmFragment(), StaticRowRepresentableDataSource, RowRe
this.launchReportWithOptions(options, options.getName(requireContext())) this.launchReportWithOptions(options, options.getName(requireContext()))
} }
ReportCreationActivity.options = null ReportCreationActivity.options = null
} else if (requestCode == ReportActivity.DEFAULT_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
val itemToDeleteId = data?.getStringExtra(DataListActivity.IntentKey.ITEM_DELETED.keyName)
itemToDeleteId?.let { id ->
GlobalScope.launch(Dispatchers.Main) {
delay(300)
deleteItem(dataListAdapter, reportSetups, id)
}
}
} }
}
}
// Business // Business
@ -98,14 +111,14 @@ class ReportsFragment : RealmFragment(), StaticRowRepresentableDataSource, RowRe
*/ */
private fun initUI() { private fun initUI() {
reportsAdapter = RowRepresentableAdapter(this, this) dataListAdapter = RowRepresentableAdapter(this, this)
val viewManager = LinearLayoutManager(requireContext()) val viewManager = LinearLayoutManager(requireContext())
recyclerView.apply { recyclerView.apply {
setHasFixedSize(true) setHasFixedSize(true)
layoutManager = viewManager layoutManager = viewManager
adapter = reportsAdapter adapter = dataListAdapter
} }
this.addButton.setOnClickListener { this.addButton.setOnClickListener {
@ -116,14 +129,14 @@ class ReportsFragment : RealmFragment(), StaticRowRepresentableDataSource, RowRe
// Rows // Rows
fun updateRows() { private fun updateRows() {
this.adapterRows.clear() this.adapterRows.clear()
if (this.reportSetups.size > 0) { if (this.reportSetups.size > 0) {
adapterRows.add(CustomizableRowRepresentable(customViewType = RowViewType.HEADER_TITLE, resId = R.string.custom)) adapterRows.add(CustomizableRowRepresentable(customViewType = RowViewType.HEADER_TITLE, resId = R.string.custom))
adapterRows.addAll(this.reportSetups) adapterRows.addAll(this.reportSetups)
} }
adapterRows.addAll(ReportRow.getRows()) adapterRows.addAll(ReportRow.getRows())
this.reportsAdapter.notifyDataSetChanged() this.dataListAdapter.notifyDataSetChanged()
} }
override fun adapterRows(): List<RowRepresentable>? { override fun adapterRows(): List<RowRepresentable>? {
@ -185,21 +198,7 @@ class ReportsFragment : RealmFragment(), StaticRowRepresentableDataSource, RowRe
launch(Dispatchers.Main) { launch(Dispatchers.Main) {
if (!isDetached) { if (!isDetached) {
hideLoader() hideLoader()
ReportActivity.newInstanceForResult(this@ReportsFragment, report, reportName)
when (options.display) {
Calculator.Options.Display.TABLE -> {
TableReportActivity.newInstance(requireContext(), report, reportName)
}
Calculator.Options.Display.PROGRESS -> {
ProgressReportActivity.newInstance(requireContext(), options.stats.first(), report)
}
Calculator.Options.Display.COMPARISON -> {
ComparisonReportActivity.newInstance(requireContext(), report, reportName)
}
else -> {
Timber.d("Report type not handled at the moment")
}
}
} }
} }
realm.close() realm.close()

@ -289,7 +289,7 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate {
* Add new custom field * Add new custom field
*/ */
private fun addNewCustomField() { private fun addNewCustomField() {
EditableDataActivity.newInstanceForResult(this, LiveData.CUSTOM_FIELD.ordinal, requestCode = REQUEST_CODE_NEW_CUSTOM_FIELD) EditableDataActivity.newInstanceForResult(this, LiveData.CUSTOM_FIELD, requestCode = REQUEST_CODE_NEW_CUSTOM_FIELD)
} }
/** /**

@ -1,32 +1,47 @@
package net.pokeranalytics.android.ui.fragment.components package net.pokeranalytics.android.ui.fragment.components
import android.app.Activity
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.coordinatorlayout.widget.CoordinatorLayout
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import io.realm.RealmObject import io.realm.RealmObject
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.interfaces.Deletable import net.pokeranalytics.android.model.interfaces.Deletable
import net.pokeranalytics.android.model.interfaces.Identifiable import net.pokeranalytics.android.model.interfaces.Identifiable
import net.pokeranalytics.android.ui.activity.DataListActivity
import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter
/** /**
* Deletable Item Fragment * Deletable Item Fragment
* Don't forget to add a CoordinatorLayout at the top of your XML if you want to display correctly the snack bar * Don't forget to add a CoordinatorLayout at the top of your XML if you want to display correctly the snack bar
*/ */
open class DeletableItemFragment : RealmFragment() { abstract class DeletableItemFragment : RealmFragment() {
companion object {
const val REQUEST_CODE_DELETION = 1000
}
lateinit var dataListAdapter: RowRepresentableAdapter
private var deletedItem: RealmObject? = null private var deletedItem: RealmObject? = null
private var itemHasBeenReInserted: Boolean = false private var itemHasBeenReInserted: Boolean = false
private var lastDeletedItemPosition: Int = 0 private var lastDeletedItemPosition: Int = 0
private var dataListAdapter: RowRepresentableAdapter? = null
private var coordinatorLayout: CoordinatorLayout? = null private var mainLayout: ViewGroup? = null
private var snackBar: Snackbar? = null private var snackBar: Snackbar? = null
abstract fun deletableItems() : List<Identifiable>
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
this.coordinatorLayout = view.findViewById(R.id.coordinatorLayout) this.mainLayout = view.findViewById(R.id.mainLayout)
} }
override fun onPause() { override fun onPause() {
@ -34,6 +49,21 @@ open class DeletableItemFragment : RealmFragment() {
snackBar?.dismiss() snackBar?.dismiss()
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_CODE_DELETION && resultCode == Activity.RESULT_OK) {
val itemToDeleteId = data?.getStringExtra(DataListActivity.IntentKey.ITEM_DELETED.keyName)
itemToDeleteId?.let { id ->
GlobalScope.launch(Dispatchers.Main) {
delay(300)
deleteItem(dataListAdapter, deletableItems(), id)
}
}
}
}
/** /**
* Delete item * Delete item
* [dataListAdapter]: Adapter to update * [dataListAdapter]: Adapter to update
@ -41,7 +71,7 @@ open class DeletableItemFragment : RealmFragment() {
* [itemId]: Id of the item to delete * [itemId]: Id of the item to delete
* [container]: View to display the Snackbar * [container]: View to display the Snackbar
*/ */
fun deleteItem(dataListAdapter: RowRepresentableAdapter, items: List<*>, itemId: String) { fun deleteItem(dataListAdapter: RowRepresentableAdapter, items: List<Identifiable>, itemId: String) {
if (isDetached || activity == null) { if (isDetached || activity == null) {
return return
@ -50,8 +80,8 @@ open class DeletableItemFragment : RealmFragment() {
this.dataListAdapter = dataListAdapter this.dataListAdapter = dataListAdapter
// Save the delete position & create a copy of the object // Save the delete position & create a copy of the object
val itemPosition = items.indexOfFirst { (it as Identifiable).id == itemId } val itemPosition = items.indexOfFirst { it.id == itemId }
val itemToDelete = items.find { (it as Identifiable).id == itemId } val itemToDelete = items.find { it.id == itemId }
if (itemToDelete is RealmObject && itemPosition != -1) { if (itemToDelete is RealmObject && itemPosition != -1) {
@ -85,7 +115,7 @@ open class DeletableItemFragment : RealmFragment() {
*/ */
private fun showUndoSnackBar() { private fun showUndoSnackBar() {
val message = String.format(getString(R.string.data_deleted)) val message = String.format(getString(R.string.data_deleted))
this.coordinatorLayout?.let { view -> this.mainLayout?.let { view ->
snackBar = Snackbar.make(view, message, Snackbar.LENGTH_INDEFINITE) snackBar = Snackbar.make(view, message, Snackbar.LENGTH_INDEFINITE)
snackBar?.setAction(R.string.cancel) { snackBar?.setAction(R.string.cancel) {
if (!itemHasBeenReInserted) { if (!itemHasBeenReInserted) {
@ -99,6 +129,8 @@ open class DeletableItemFragment : RealmFragment() {
} }
} }
snackBar?.show() snackBar?.show()
} ?: run {
throw IllegalStateException("mainLayout is not defined")
} }
} }
@ -106,14 +138,14 @@ open class DeletableItemFragment : RealmFragment() {
* Called once the object has been deleted * Called once the object has been deleted
*/ */
open fun updateUIAfterDeletion(itemPosition: Int) { open fun updateUIAfterDeletion(itemPosition: Int) {
dataListAdapter?.notifyItemRemoved(itemPosition) dataListAdapter.notifyItemRemoved(itemPosition)
} }
/** /**
* Called once the object has been restored * Called once the object has been restored
*/ */
open fun updateUIAfterUndoDeletion(newItem: RealmObject) { open fun updateUIAfterUndoDeletion(newItem: RealmObject) {
dataListAdapter?.notifyItemInserted(lastDeletedItemPosition) dataListAdapter.notifyItemInserted(lastDeletedItemPosition)
} }
} }

@ -12,7 +12,7 @@ import android.view.WindowManager
import androidx.appcompat.view.ContextThemeWrapper import androidx.appcompat.view.ContextThemeWrapper
import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager
import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import io.realm.RealmObject import io.realm.RealmModel
import kotlinx.android.synthetic.main.fragment_bottom_sheet.* import kotlinx.android.synthetic.main.fragment_bottom_sheet.*
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.LiveData import net.pokeranalytics.android.model.LiveData
@ -82,7 +82,7 @@ open class BottomSheetFragment : BottomSheetDialogFragment() {
val primaryKey = data.getStringExtra(EditableDataActivity.IntentKey.PRIMARY_KEY.keyName) val primaryKey = data.getStringExtra(EditableDataActivity.IntentKey.PRIMARY_KEY.keyName)
val pokerAnalyticsActivity = activity as PokerAnalyticsActivity val pokerAnalyticsActivity = activity as PokerAnalyticsActivity
val liveDataType = LiveData.values()[dataType] val liveDataType = LiveData.values()[dataType]
val proxyItem: RealmObject? = liveDataType.getData(pokerAnalyticsActivity.getRealm(), primaryKey) val proxyItem: RealmModel? = liveDataType.getData(pokerAnalyticsActivity.getRealm(), primaryKey)
this.delegate.onRowValueChanged(proxyItem, this.row) this.delegate.onRowValueChanged(proxyItem, this.row)
dismiss() dismiss()
} }
@ -122,38 +122,22 @@ open class BottomSheetFragment : BottomSheetDialogFragment() {
} }
bottomSheetToolbar.menu.findItem(R.id.actionAdd).setOnMenuItemClickListener { bottomSheetToolbar.menu.findItem(R.id.actionAdd).setOnMenuItemClickListener {
when (row) { val liveData = when (row) {
SessionRow.GAME -> EditableDataActivity.newInstanceForResult( SessionRow.GAME -> LiveData.GAME
this, SessionRow.BANKROLL, TransactionRow.BANKROLL -> LiveData.BANKROLL
LiveData.GAME.ordinal, SessionRow.LOCATION -> LiveData.LOCATION
requestCode = REQUEST_CODE_ADD_NEW_OBJECT SessionRow.TOURNAMENT_NAME -> LiveData.TOURNAMENT_NAME
) SessionRow.TOURNAMENT_FEATURE -> LiveData.TOURNAMENT_FEATURE
SessionRow.BANKROLL, TransactionRow.BANKROLL -> EditableDataActivity.newInstanceForResult( TransactionRow.TYPE -> LiveData.TRANSACTION_TYPE
this, else -> throw IllegalStateException("row $row does not have an associated LiveData value")
LiveData.BANKROLL.ordinal,
requestCode = REQUEST_CODE_ADD_NEW_OBJECT
)
SessionRow.LOCATION -> EditableDataActivity.newInstanceForResult(
this,
LiveData.LOCATION.ordinal,
requestCode = REQUEST_CODE_ADD_NEW_OBJECT
)
SessionRow.TOURNAMENT_NAME -> EditableDataActivity.newInstanceForResult(
this,
LiveData.TOURNAMENT_NAME.ordinal,
requestCode = REQUEST_CODE_ADD_NEW_OBJECT
)
SessionRow.TOURNAMENT_FEATURE -> EditableDataActivity.newInstanceForResult(
this,
LiveData.TOURNAMENT_FEATURE.ordinal,
requestCode = REQUEST_CODE_ADD_NEW_OBJECT
)
TransactionRow.TYPE -> EditableDataActivity.newInstanceForResult(
this,
LiveData.TRANSACTION_TYPE.ordinal,
requestCode = REQUEST_CODE_ADD_NEW_OBJECT
)
} }
EditableDataActivity.newInstanceForResult(
this,
liveData,
requestCode = REQUEST_CODE_ADD_NEW_OBJECT
)
true true
} }
bottomSheetToolbar.menu.findItem(R.id.actionCheck).setOnMenuItemClickListener { bottomSheetToolbar.menu.findItem(R.id.actionCheck).setOnMenuItemClickListener {

@ -3,7 +3,7 @@ package net.pokeranalytics.android.ui.fragment.components.bottomsheet
import android.app.Activity import android.app.Activity
import android.content.Intent import android.content.Intent
import io.realm.RealmList import io.realm.RealmList
import io.realm.RealmObject import io.realm.RealmModel
import net.pokeranalytics.android.exceptions.RowRepresentableEditDescriptorException import net.pokeranalytics.android.exceptions.RowRepresentableEditDescriptorException
import net.pokeranalytics.android.model.LiveData import net.pokeranalytics.android.model.LiveData
import net.pokeranalytics.android.ui.activity.EditableDataActivity import net.pokeranalytics.android.ui.activity.EditableDataActivity
@ -28,7 +28,7 @@ open class BottomSheetMultiSelectionFragment : BottomSheetListFragment() {
val primaryKey = data.getStringExtra(EditableDataActivity.IntentKey.PRIMARY_KEY.keyName) val primaryKey = data.getStringExtra(EditableDataActivity.IntentKey.PRIMARY_KEY.keyName)
val pokerAnalyticsActivity = activity as PokerAnalyticsActivity val pokerAnalyticsActivity = activity as PokerAnalyticsActivity
val liveDataType = LiveData.values()[dataType] val liveDataType = LiveData.values()[dataType]
val proxyItem: RealmObject? = liveDataType.getData(pokerAnalyticsActivity.getRealm(), primaryKey) val proxyItem: RealmModel? = liveDataType.getData(pokerAnalyticsActivity.getRealm(), primaryKey)
selectedRows.add(proxyItem as RowRepresentable) selectedRows.add(proxyItem as RowRepresentable)
dataAdapter.refreshRow(proxyItem as RowRepresentable) dataAdapter.refreshRow(proxyItem as RowRepresentable)
} }

@ -157,11 +157,11 @@ class BankrollDataFragment : EditableDataFragment(), StaticRowRepresentableDataS
/** /**
* Init data * Init data
*/ */
private fun initData() { override fun initData() {
defaultCurrency = UserDefaults.currency defaultCurrency = UserDefaults.currency
if (!isUpdating) { if (!deleteButtonShouldAppear) {
bankroll.currency = net.pokeranalytics.android.model.realm.Currency() bankroll.currency = net.pokeranalytics.android.model.realm.Currency()
bankroll.currency?.code = defaultCurrency.currencyCode bankroll.currency?.code = defaultCurrency.currencyCode
bankroll.currency?.rate = 1.0 bankroll.currency?.rate = 1.0

@ -258,7 +258,7 @@ class CustomFieldDataFragment : EditableDataFragment(), StaticRowRepresentableDa
updateUI() updateUI()
rowRepresentableAdapter.notifyDataSetChanged() rowRepresentableAdapter.notifyDataSetChanged()
if (!isUpdating) { if (!this.deleteButtonShouldAppear) {
rowRepresentableForPosition(0)?.let { rowRepresentableForPosition(0)?.let {
onRowSelected(0, it) onRowSelected(0, it)
} }

@ -0,0 +1,158 @@
package net.pokeranalytics.android.ui.fragment.data
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import androidx.appcompat.app.AlertDialog
import io.realm.RealmModel
import net.pokeranalytics.android.R
import net.pokeranalytics.android.exceptions.ConfigurationException
import net.pokeranalytics.android.model.LiveData
import net.pokeranalytics.android.model.interfaces.Deletable
import net.pokeranalytics.android.model.interfaces.Savable
import net.pokeranalytics.android.model.interfaces.SaveValidityStatus
import net.pokeranalytics.android.ui.activity.DataListActivity
import net.pokeranalytics.android.ui.activity.EditableDataActivity
import net.pokeranalytics.android.ui.fragment.components.RealmFragment
open class DataManagerFragment : RealmFragment() {
lateinit var item: RealmModel
lateinit var liveDataType: LiveData
protected var primaryKey: String? = null
var deleteButtonShouldAppear = false
set(value) {
field = value
this.updateMenuUI()
}
var saveButtonShouldAppear = true
set(value) {
field = value
this.updateMenuUI()
}
protected var dataType: Int? = null
private var editableMenu: Menu? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initData()
}
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
menu?.clear()
inflater?.inflate(R.menu.toolbar_editable_data, menu)
this.editableMenu = menu
updateMenuUI()
super.onCreateOptionsMenu(menu, inflater)
}
/**
* Update menu UI
*/
private fun updateMenuUI() {
editableMenu?.findItem(R.id.delete)?.isVisible = this.deleteButtonShouldAppear
editableMenu?.findItem(R.id.save)?.isVisible = this.saveButtonShouldAppear
}
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
when (item!!.itemId) {
R.id.save -> saveData()
R.id.delete -> deleteData()
}
return true
}
/**
* Init data
*/
protected open fun initData() {
this.deleteButtonShouldAppear = this.primaryKey != null
this.item = this.liveDataType.updateOrCreate(this.getRealm(), primaryKey)
}
/**
* Save data
*/
protected open fun saveData() {
val savable = this.item
when (savable) {
is Savable -> {
val status = savable.getSaveValidityStatus(realm = this.getRealm())
when (status) {
SaveValidityStatus.VALID -> {
this.getRealm().executeTransaction {
val managedItem = it.copyToRealmOrUpdate(this.item)
if (managedItem is Savable) {
val uniqueIdentifier = (managedItem as Savable).id
finishActivityWithResult(uniqueIdentifier)
}
}
onDataSaved()
}
else -> {
val message = savable.getFailedSaveMessage(status)
val builder = AlertDialog.Builder(requireContext())
.setMessage(message)
.setNegativeButton(R.string.ok, null)
builder.show()
}
}
}
else -> {
throw ConfigurationException("Save action called on un-Savable object")
}
}
}
/**
* Delete data
*/
protected open fun deleteData() {
val deletable = this.item as Deletable
val realm = this.getRealm()
if (deletable.isValidForDelete(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 message = deletable.getFailedDeleteMessage(status)
val builder = AlertDialog.Builder(requireContext())
.setMessage(message)
.setNegativeButton(R.string.ok, null)
builder.show()
}
}
/**
* Finish the activity with a result
*/
private fun finishActivityWithResult(uniqueIdentifier: String) {
val intent = Intent()
intent.putExtra(EditableDataActivity.IntentKey.DATA_TYPE.keyName, dataType)
intent.putExtra(EditableDataActivity.IntentKey.PRIMARY_KEY.keyName, uniqueIdentifier)
activity?.setResult(Activity.RESULT_OK, intent)
activity?.finish()
}
open fun onDataSaved() {}
}

@ -1,41 +1,28 @@
package net.pokeranalytics.android.ui.fragment.data package net.pokeranalytics.android.ui.fragment.data
import android.app.Activity.RESULT_OK
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.* import android.view.LayoutInflater
import androidx.appcompat.app.AlertDialog import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import io.realm.RealmObject import io.realm.RealmModel
import kotlinx.android.synthetic.main.fragment_editable_data.* import kotlinx.android.synthetic.main.fragment_editable_data.*
import kotlinx.android.synthetic.main.fragment_editable_data.view.*
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.exceptions.ConfigurationException
import net.pokeranalytics.android.model.LiveData import net.pokeranalytics.android.model.LiveData
import net.pokeranalytics.android.model.interfaces.Deletable
import net.pokeranalytics.android.model.interfaces.Editable import net.pokeranalytics.android.model.interfaces.Editable
import net.pokeranalytics.android.model.interfaces.Savable import net.pokeranalytics.android.ui.activity.components.PokerAnalyticsActivity
import net.pokeranalytics.android.model.interfaces.SaveValidityStatus
import net.pokeranalytics.android.ui.activity.DataListActivity
import net.pokeranalytics.android.ui.activity.EditableDataActivity
import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter
import net.pokeranalytics.android.ui.adapter.RowRepresentableDataSource import net.pokeranalytics.android.ui.adapter.RowRepresentableDataSource
import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate
import net.pokeranalytics.android.ui.fragment.components.RealmFragment
import net.pokeranalytics.android.ui.fragment.components.bottomsheet.BottomSheetFragment import net.pokeranalytics.android.ui.fragment.components.bottomsheet.BottomSheetFragment
import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
open class EditableDataFragment : RealmFragment(), RowRepresentableDelegate { open class EditableDataFragment : DataManagerFragment(), RowRepresentableDelegate {
lateinit var item: RealmObject
lateinit var liveDataType: LiveData
lateinit var rowRepresentableAdapter: RowRepresentableAdapter lateinit var rowRepresentableAdapter: RowRepresentableAdapter
private var editableMenu: Menu? = null
private var dataType: Int? = null
private var primaryKey: String? = null
var isUpdating = false
var shouldOpenKeyboard = true var shouldOpenKeyboard = true
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
@ -46,24 +33,15 @@ open class EditableDataFragment : RealmFragment(), RowRepresentableDelegate {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
initUI() initUI()
initData()
}
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
menu?.clear()
inflater?.inflate(R.menu.toolbar_editable_data, menu)
this.editableMenu = menu
updateMenuUI()
super.onCreateOptionsMenu(menu, inflater)
} }
/**
override fun onOptionsItemSelected(item: MenuItem?): Boolean { * Set fragment data
when (item!!.itemId) { */
R.id.save -> saveData() fun setData(dataType: Int, primaryKey: String?) {
R.id.delete -> deleteData() this.dataType = dataType
} this.liveDataType = LiveData.values()[dataType]
return true this.primaryKey = primaryKey
} }
override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) { override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) {
@ -85,6 +63,16 @@ open class EditableDataFragment : RealmFragment(), RowRepresentableDelegate {
setDisplayHomeAsUpEnabled(true) setDisplayHomeAsUpEnabled(true)
val proxyItem: RealmModel? = this.liveDataType.getData(this.getRealm(), primaryKey)
proxyItem?.let {
//TODO: Localize
this.appBar.toolbar.title = "Update ${this.liveDataType.localizedTitle(requireContext()).toLowerCase().capitalize()}"
deleteButtonShouldAppear = true
} ?: run {
//TODO: Localize
this.appBar.toolbar.title = this.liveDataType.newEntityLocalizedTitle(requireContext())
}
val viewManager = LinearLayoutManager(requireContext()) val viewManager = LinearLayoutManager(requireContext())
recyclerView.apply { recyclerView.apply {
setHasFixedSize(true) setHasFixedSize(true)
@ -99,126 +87,20 @@ open class EditableDataFragment : RealmFragment(), RowRepresentableDelegate {
return this.item as RowRepresentableDataSource return this.item as RowRepresentableDataSource
} }
/** override fun initData() {
* Init data super.initData()
*/
private fun initData() {
if (this.dataType != null) {
val proxyItem: RealmObject? = this.liveDataType.getData(this.getRealm(), primaryKey)
proxyItem?.let {
//TODO: Localize
//this.appBar.toolbar.title = "Update ${this.liveDataType.localizedTitle(requireContext()).toLowerCase().capitalize()}"
setToolbarTitle("Update ${this.liveDataType.localizedTitle(requireContext()).toLowerCase().capitalize()}")
isUpdating = true
} ?: run {
//TODO: Localize
//this.appBar.toolbar.title = this.liveDataType.newEntityLocalizedTitle(requireContext())
setToolbarTitle(this.liveDataType.newEntityLocalizedTitle(requireContext()))
}
this.item = this.liveDataType.updateOrCreate(this.getRealm(), primaryKey)
val dataSource = getDataSource()
this.rowRepresentableAdapter = RowRepresentableAdapter(getDataSource(), this)
this.rowRepresentableAdapter.setHasStableIds(true)
this.recyclerView.adapter = rowRepresentableAdapter
// When creating an object, open automatically the keyboard for the first row
if (!isUpdating && shouldOpenKeyboard) {
val row = dataSource.adapterRows()?.firstOrNull()
row?.let {
onRowSelected(0, it)
}
}
}
}
/** val dataSource = getDataSource()
* Update menu UI this.rowRepresentableAdapter = RowRepresentableAdapter(getDataSource(), this)
*/ this.recyclerView.adapter = rowRepresentableAdapter
private fun updateMenuUI() {
editableMenu?.findItem(R.id.delete)?.isVisible = isUpdating
editableMenu?.findItem(R.id.save)?.isVisible = true
}
/** // When creating an object, open automatically the keyboard for the first row
* Save data if (!deleteButtonShouldAppear && shouldOpenKeyboard) {
*/ val row = dataSource.adapterRows()?.firstOrNull()
fun saveData() { row?.let {
onRowSelected(0, it)
val savable = this.item
when (savable) {
is Savable -> {
val status = savable.getSaveValidityStatus(realm = this.getRealm())
when (status) {
SaveValidityStatus.VALID -> {
this.getRealm().executeTransaction {
val managedItem = it.copyToRealmOrUpdate(this.item)
if (managedItem is Savable) {
val uniqueIdentifier = (managedItem as Savable).id
finishActivityWithResult(uniqueIdentifier)
}
}
onDataSaved()
}
else -> {
val message = savable.getFailedSaveMessage(status)
val builder = AlertDialog.Builder(requireContext())
.setMessage(message)
.setNegativeButton(R.string.ok, null)
builder.show()
}
}
} else -> {
throw ConfigurationException("Save action called on un-Savable object")
} }
} }
}
/**
* Delete data
*/
private fun deleteData() {
val deletable = this.item as Deletable
val realm = this.getRealm()
if (deletable.isValidForDelete(realm)) {
val intent = Intent()
intent.putExtra(DataListActivity.IntentKey.ITEM_DELETED.keyName, true)
activity?.setResult(RESULT_OK, intent)
activity?.finish()
} else {
val status = deletable.getDeleteStatus(realm)
val message = deletable.getFailedDeleteMessage(status)
val builder = AlertDialog.Builder(requireContext())
.setMessage(message)
.setNegativeButton(R.string.ok, null)
builder.show()
}
}
/**
* Finish the activity with a result
*/
private fun finishActivityWithResult(uniqueIdentifier: String) {
val intent = Intent()
intent.putExtra(EditableDataActivity.IntentKey.DATA_TYPE.keyName, dataType)
intent.putExtra(EditableDataActivity.IntentKey.PRIMARY_KEY.keyName, uniqueIdentifier)
activity?.setResult(RESULT_OK, intent)
activity?.finish()
} }
/**
* Set fragment data
*/
fun setData(dataType: Int, primaryKey: String?) {
this.dataType = dataType
this.liveDataType = LiveData.values()[dataType]
this.primaryKey = primaryKey
}
open fun onDataSaved() {}
} }

@ -40,7 +40,7 @@ class LocationDataFragment : EditableDataFragment(), StaticRowRepresentableDataS
shouldOpenKeyboard = false shouldOpenKeyboard = false
locationActivated = parentActivity?.hasLocationPermissionGranted() ?: false locationActivated = parentActivity?.hasLocationPermissionGranted() ?: false
if (isUpdating) { if (deleteButtonShouldAppear) {
// If we update a location, we set the switch to the correct value // If we update a location, we set the switch to the correct value
locationActivated = location.latitude != null && location.longitude != null locationActivated = location.latitude != null && location.longitude != null

@ -6,8 +6,9 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import net.pokeranalytics.android.model.LiveData import net.pokeranalytics.android.model.realm.Bankroll
import net.pokeranalytics.android.model.realm.Transaction import net.pokeranalytics.android.model.realm.Transaction
import net.pokeranalytics.android.model.realm.TransactionType
import net.pokeranalytics.android.ui.adapter.RowRepresentableDataSource import net.pokeranalytics.android.ui.adapter.RowRepresentableDataSource
import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource
import net.pokeranalytics.android.ui.helpers.DateTimePickerManager import net.pokeranalytics.android.ui.helpers.DateTimePickerManager
@ -17,6 +18,7 @@ import net.pokeranalytics.android.ui.view.rowrepresentable.TransactionRow
import net.pokeranalytics.android.util.NULL_TEXT import net.pokeranalytics.android.util.NULL_TEXT
import net.pokeranalytics.android.util.extensions.round import net.pokeranalytics.android.util.extensions.round
import net.pokeranalytics.android.util.extensions.shortDate import net.pokeranalytics.android.util.extensions.shortDate
import net.pokeranalytics.android.util.sorted
import java.util.* import java.util.*
/** /**
@ -56,8 +58,8 @@ class TransactionDataFragment : EditableDataFragment(), StaticRowRepresentableDa
override fun editDescriptors(row: RowRepresentable): ArrayList<RowRepresentableEditDescriptor>? { override fun editDescriptors(row: RowRepresentable): ArrayList<RowRepresentableEditDescriptor>? {
return when (row) { return when (row) {
TransactionRow.BANKROLL -> row.editingDescriptors(mapOf("defaultValue" to this.transaction.bankroll, "data" to LiveData.BANKROLL.items(getRealm()))) TransactionRow.BANKROLL -> row.editingDescriptors(mapOf("defaultValue" to this.transaction.bankroll, "data" to getRealm().sorted<Bankroll>() ))
TransactionRow.TYPE -> row.editingDescriptors(mapOf("defaultValue" to this.transaction.type, "data" to LiveData.TRANSACTION_TYPE.items(getRealm()))) TransactionRow.TYPE -> row.editingDescriptors(mapOf("defaultValue" to this.transaction.type, "data" to getRealm().sorted<TransactionType>() ))
TransactionRow.AMOUNT -> row.editingDescriptors(mapOf("defaultValue" to (if (this.transaction.amount != 0.0) this.transaction.amount.round() else ""))) TransactionRow.AMOUNT -> row.editingDescriptors(mapOf("defaultValue" to (if (this.transaction.amount != 0.0) this.transaction.amount.round() else "")))
TransactionRow.COMMENT -> row.editingDescriptors(mapOf("defaultValue" to this.transaction.comment)) TransactionRow.COMMENT -> row.editingDescriptors(mapOf("defaultValue" to this.transaction.comment))
else -> super.editDescriptors(row) else -> super.editDescriptors(row)

@ -1,53 +1,110 @@
package net.pokeranalytics.android.ui.fragment.report package net.pokeranalytics.android.ui.fragment.report
import android.view.Menu import android.os.Bundle
import android.view.MenuInflater import android.view.View
import android.view.MenuItem import android.widget.EditText
import androidx.appcompat.app.AlertDialog
import kotlinx.android.synthetic.main.fragment_progress_report.*
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.Report import net.pokeranalytics.android.calculus.Report
import net.pokeranalytics.android.ui.fragment.components.RealmFragment import net.pokeranalytics.android.model.LiveData
import net.pokeranalytics.android.model.realm.ReportSetup
import net.pokeranalytics.android.ui.activity.components.PokerAnalyticsActivity
import net.pokeranalytics.android.ui.fragment.data.DataManagerFragment
abstract class AbstractReportFragment : RealmFragment() { abstract class AbstractReportFragment : DataManagerFragment() {
protected lateinit var selectedReport: Report private lateinit var _selectedReport: Report
val selectedReport: Report
get() {
return this._selectedReport
}
fun setReport(report: Report) {
this._selectedReport = report
this.primaryKey = report.options.reportSetupId
}
protected var reportTitle: String? = null protected var reportTitle: String? = null
private var editableMenu: Menu? = null protected lateinit var parentActivity: PokerAnalyticsActivity
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { override fun onCreate(savedInstanceState: Bundle?) {
menu?.clear() super.onCreate(savedInstanceState)
inflater?.inflate(R.menu.toolbar_report, menu)
this.editableMenu = menu
updateMenuUI()
super.onCreateOptionsMenu(menu, inflater)
}
override fun onOptionsItemSelected(item: MenuItem?): Boolean { this.liveDataType = LiveData.REPORT_SETUP
when (item!!.itemId) { this.saveButtonShouldAppear = this._selectedReport.options.userGenerated
R.id.save -> this.saveReportRequest() this.deleteButtonShouldAppear = (this.primaryKey != null)
}
return true
} }
/** override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
* Update menu UI super.onViewCreated(view, savedInstanceState)
*/
private fun updateMenuUI() { parentActivity = activity as PokerAnalyticsActivity
editableMenu?.findItem(R.id.save)?.let {
it.isVisible = this.selectedReport.options.userGenerated // Avoid a bug during setting the titleResId
it.icon.setTint(requireContext().getColor(R.color.white)) toolbar.title = ""
}
parentActivity.setSupportActionBar(toolbar)
parentActivity.supportActionBar?.setDisplayHomeAsUpEnabled(true)
setHasOptionsMenu(true)
toolbar.title = reportTitle
} }
private fun saveReportRequest() { override fun saveData() {
activity?.let {
val builder = AlertDialog.Builder(it)
// Get the layout inflater
val inflater = requireActivity().layoutInflater;
// Inflate and set the layout for the dialog
// Pass null as the parent view because its going in the dialog layout
val view = inflater.inflate(R.layout.dialog_edit_text, null)
val nameEditText = view.findViewById<EditText>(R.id.reportName)
builder.setView(view)
// Add action buttons
.setPositiveButton(R.string.save) { dialog, id ->
saveReport(nameEditText.text.toString())
dialog.dismiss()
}
.setNegativeButton(R.string.cancel) { dialog, id ->
dialog.cancel()
}
val dialog = builder.create()
dialog.show()
} ?: throw IllegalStateException("Activity cannot be null")
} }
private fun saveReport(name: String) { private fun saveReport(name: String) {
getRealm().executeTransaction { getRealm().executeTransaction { realm ->
val report = this.selectedReport.options.reportSetup(name)
it.insert(report) val rs = this.item as ReportSetup
val options = this._selectedReport.options
rs.name = name
rs.display = options.display.ordinal
options.stats.forEach {
rs.statIds.add(it.uniqueIdentifier)
}
options.criterias.forEach {
rs.criteriaIds.add(it.uniqueIdentifier)
}
rs.filter = options.filter
this.item = rs
this.deleteButtonShouldAppear = true
toolbar.title = name
realm.copyToRealmOrUpdate(rs)
} }
} }
} }

@ -13,28 +13,22 @@ import net.pokeranalytics.android.ui.adapter.ReportPagerAdapter
class ComparisonReportFragment : AbstractReportFragment() { class ComparisonReportFragment : AbstractReportFragment() {
companion object { companion object {
fun newInstance(report: Report?, reportTitle: String): ComparisonReportFragment {
fun newInstance(report: Report, reportTitle: String): ComparisonReportFragment {
val fragment = ComparisonReportFragment() val fragment = ComparisonReportFragment()
fragment.reportTitle = reportTitle fragment.reportTitle = reportTitle
report?.let { fragment.setReport(report)
fragment.selectedReport = it
}
val bundle = Bundle() val bundle = Bundle()
fragment.arguments = bundle fragment.arguments = bundle
return fragment return fragment
} }
} }
// private var reports: MutableMap<AggregationType, Report> = hashMapOf()
// private var stat: Stat = Stat.NET_RESULT
// private var displayAggregationChoices: Boolean = true
/** /**
* Set data * Set data
*/ */
fun setData(report: Report) { fun setData(report: Report) {
this.selectedReport = report this.setReport(report)
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {

@ -12,7 +12,7 @@ import kotlinx.coroutines.*
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.* import net.pokeranalytics.android.calculus.*
import net.pokeranalytics.android.model.realm.ComputableResult import net.pokeranalytics.android.model.realm.ComputableResult
import net.pokeranalytics.android.ui.activity.ProgressReportActivity import net.pokeranalytics.android.ui.activity.components.ReportActivity
import net.pokeranalytics.android.ui.adapter.DisplayDescriptor import net.pokeranalytics.android.ui.adapter.DisplayDescriptor
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
@ -212,7 +212,11 @@ open class ComposableTableReportFragment : RealmFragment(), StaticRowRepresentab
if (!isDetached) { if (!isDetached) {
hideLoader() hideLoader()
report?.let { report?.let {
ProgressReportActivity.newInstance(requireContext(), stat, it) val title = stat.localizedTitle(requireContext())
ReportActivity.newInstance(requireContext(), it, title, stat)
// ProgressReportActivity.newInstance(requireContext(), stat, it, title = title)
} }
} }
} }

@ -64,9 +64,9 @@ class ProgressReportFragment : AbstractReportFragment() {
/** /**
* Set data * Set data
*/ */
fun setData(stat: Stat, report: Report, displayAggregationChoices: Boolean, title: String? = null) { fun setData(report: Report, stat: Stat, displayAggregationChoices: Boolean, title: String) {
this.stat = stat this.stat = stat
this.selectedReport = report this.setReport(report)
this.displayAggregationChoices = displayAggregationChoices this.displayAggregationChoices = displayAggregationChoices
this.reportTitle = title this.reportTitle = title
@ -80,15 +80,12 @@ class ProgressReportFragment : AbstractReportFragment() {
*/ */
private fun initUI() { private fun initUI() {
setDisplayHomeAsUpEnabled(true) val fragmentManager = parentActivity.supportFragmentManager
setToolbarTitle(this.reportTitle ?: stat.localizedTitle(requireContext())) val fragmentTransaction = fragmentManager.beginTransaction()
val fragmentManager = parentActivity?.supportFragmentManager
val fragmentTransaction = fragmentManager?.beginTransaction()
graphFragment = GraphFragment() graphFragment = GraphFragment()
fragmentTransaction?.add(R.id.graphContainer, graphFragment) fragmentTransaction.add(R.id.graphContainer, graphFragment)
fragmentTransaction?.commit() fragmentTransaction.commit()
stat.aggregationTypes.firstOrNull()?.let { aggregationType -> stat.aggregationTypes.firstOrNull()?.let { aggregationType ->
reports[aggregationType]?.let { report -> reports[aggregationType]?.let { report ->

@ -13,15 +13,10 @@ class TableReportFragment : AbstractReportFragment() {
companion object { companion object {
/** fun newInstance(report: Report, title: String): TableReportFragment {
* Create new instance
*/
fun newInstance(report: Report? = null, title: String? = null): TableReportFragment {
val fragment = TableReportFragment() val fragment = TableReportFragment()
fragment.reportTitle = title fragment.reportTitle = title
report?.let { fragment.setReport(report)
fragment.selectedReport = it
}
val bundle = Bundle() val bundle = Bundle()
fragment.arguments = bundle fragment.arguments = bundle
return fragment return fragment

@ -0,0 +1,71 @@
package net.pokeranalytics.android.util
import io.realm.Realm
import io.realm.RealmModel
import io.realm.RealmResults
import io.realm.Sort
import io.realm.kotlin.where
import net.pokeranalytics.android.model.interfaces.CountableUsage
import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.model.realm.TournamentFeature
import net.pokeranalytics.android.model.realm.Transaction
/**
* Returns all entities of the [clazz] sorted with their default sorting
*/
fun <T : RealmModel> Realm.sorted(clazz: Class<T>) : RealmResults<T> {
if (clazz is CountableUsage) {
this.updateUsageCount(clazz)
}
val sortField = when (clazz) {
is CountableUsage -> "useCount"
is Transaction -> "date"
else -> "name"
}
val resultSort = when (clazz) {
is CountableUsage -> Sort.DESCENDING
is Transaction -> Sort.DESCENDING
else -> Sort.ASCENDING
}
return this.where(clazz).findAll().sort(sortField, resultSort)
}
/**
* Returns all entities of the [clazz] sorted with their default sorting
*/
inline fun <reified C : RealmModel> Realm.sorted() : RealmResults<C> {
return this.sorted(C::class.java)
}
/**
* Updates the useCount variable of the CountableUsage entity
*/
fun <T : RealmModel>Realm.updateUsageCount(clazz: Class<T>) {
val results = this.where(clazz).findAll()
this.executeTransaction {
results.forEach { countableUsage ->
val countable = (countableUsage as CountableUsage)
when (clazz) {
is TournamentFeature -> {
countable.useCount = it.where<Session>().contains(
"tournamentFeatures.id",
countable.id
).count().toInt()
}
else -> {
countable.useCount = it.where<Session>().equalTo(
"${clazz.simpleName.decapitalize()}.id",
countable.id
).count().toInt()
}
}
}
}
}

@ -0,0 +1,21 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="24dp">
<androidx.appcompat.widget.AppCompatTextView
android:text="@string/save_report"
style="@style/PokerAnalyticsTheme.TextView.Title"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<androidx.appcompat.widget.AppCompatEditText
android:id="@+id/reportName"
style="@style/PokerAnalyticsTheme.EditText"
android:layout_width="240dp"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:hint="@string/name" />
</LinearLayout>

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/coordinatorLayout" android:id="@+id/mainLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">

@ -2,7 +2,7 @@
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/coordinatorLayout" android:id="@+id/mainLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/mainLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">

@ -39,6 +39,7 @@
<androidx.viewpager.widget.ViewPager <androidx.viewpager.widget.ViewPager
android:id="@+id/pager" android:id="@+id/pager"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_marginBottom="8dp" android:layout_marginBottom="8dp"

@ -30,6 +30,7 @@
<string name="new_report_step_filter">Select a filter or launch report</string> <string name="new_report_step_filter">Select a filter or launch report</string>
<string name="launch_report">Launch Report</string> <string name="launch_report">Launch Report</string>
<string name="progress">Progress</string> <string name="progress">Progress</string>
<string name="save_report">Save Report</string>
<string name="address">Address</string> <string name="address">Address</string>
<string name="suggestions">Naming suggestions</string> <string name="suggestions">Naming suggestions</string>

Loading…
Cancel
Save