Report creation code without much UI

dev
Laurent 7 years ago
parent 185c459066
commit e83914815d
  1. 5
      app/src/main/AndroidManifest.xml
  2. 421
      app/src/main/java/net/pokeranalytics/android/model/Criteria.kt
  3. 14
      app/src/main/java/net/pokeranalytics/android/model/realm/Filter.kt
  4. 11
      app/src/main/java/net/pokeranalytics/android/model/realm/ReportSetup.kt
  5. 24
      app/src/main/java/net/pokeranalytics/android/ui/activity/ReportCreationActivity.kt
  6. 102
      app/src/main/java/net/pokeranalytics/android/ui/fragment/ReportCreationFragment.kt
  7. 9
      app/src/main/java/net/pokeranalytics/android/ui/fragment/ReportsFragment.kt
  8. 15
      app/src/main/res/layout/activity_reportcreation.xml
  9. 20
      app/src/main/res/layout/fragment_report_creation.xml
  10. 13
      app/src/main/res/layout/fragment_reports.xml
  11. 5
      app/src/main/res/values/strings.xml

@ -118,6 +118,11 @@
android:launchMode="singleTop"
android:screenOrientation="portrait" />
<activity
android:name="net.pokeranalytics.android.ui.activity.ReportCreationActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait" />
<meta-data
android:name="preloaded_fonts"
android:resource="@array/preloaded_fonts" />

@ -14,231 +14,230 @@ import net.pokeranalytics.android.model.Criteria.TournamentFeatures.comparison
import net.pokeranalytics.android.model.Criteria.TournamentFees.comparison
import net.pokeranalytics.android.model.Criteria.TournamentNames.comparison
import net.pokeranalytics.android.model.Criteria.TournamentTypes.comparison
import net.pokeranalytics.android.model.Criteria.TransactionTypes.comparison
import net.pokeranalytics.android.model.filter.Query
import net.pokeranalytics.android.model.filter.QueryCondition
import net.pokeranalytics.android.model.interfaces.NameManageable
import net.pokeranalytics.android.model.realm.*
import net.pokeranalytics.android.ui.view.RowRepresentable
fun List<Criteria>.combined(): List<Query> {
val comparatorList = ArrayList<List<Query>>()
this.forEach { criteria ->
comparatorList.add(criteria.queries)
}
return getCombinations(comparatorList)
val comparatorList = ArrayList<List<Query>>()
this.forEach { criteria ->
comparatorList.add(criteria.queries)
}
return getCombinations(comparatorList)
}
fun getCombinations(queries: List<List<Query>>): List<Query> {
if (queries.size == 0) { return listOf() }
if (queries.size == 0) {
return listOf()
}
val mutableQueries = queries.toMutableList()
var combinations = mutableQueries.removeAt(0)
val mutableQueries = queries.toMutableList()
var combinations = mutableQueries.removeAt(0)
for (queryList in mutableQueries) {
for (queryList in mutableQueries) {
val newCombinations = mutableListOf<Query>()
combinations.forEach { combinedQuery ->
queryList.forEach { queryToAdd ->
val nq = Query().merge(combinedQuery).merge(queryToAdd)
newCombinations.add(nq)
}
}
combinations = newCombinations
}
val newCombinations = mutableListOf<Query>()
combinations.forEach { combinedQuery ->
queryList.forEach { queryToAdd ->
val nq = Query().merge(combinedQuery).merge(queryToAdd)
newCombinations.add(nq)
}
}
combinations = newCombinations
}
return combinations
return combinations
}
//fun getCombinations(lists: List<Query>): List<Query> {
// var combinations: LinkedHashSet<Query> = LinkedHashSet()
// var newCombinations: LinkedHashSet<Query>
//
// var index = 0
//
// // extract each of the integers in the first list
// // and add each to ints as a new list
// if (lists.isNotEmpty()) {
// for (i in lists[0]) {
// val newList = ArrayList<T>()
// newList.add(i)
// combinations.add(newList)
// }
// index++
// }
// while (index < lists.size) {
// val nextList = lists[index]
// newCombinations = LinkedHashSet()
// for (first in combinations) {
// for (second in nextList) {
// val newList = ArrayList<T>()
// newList.addAll(first)
// newList.add(second)
// newCombinations.add(newList)
// }
// }
// combinations = newCombinations
//
// index++
// }
//
// return combinations.toList()
//}
sealed class Criteria {
abstract class RealmCriteria : Criteria() {
inline fun <reified T: NameManageable> comparison(): List<Query> {
return compare<QueryCondition.QueryDataCondition<NameManageable>, T>()
}
}
abstract class SimpleCriteria(private val conditions:List<QueryCondition>): Criteria() {
fun comparison(): List<Query> {
return conditions.map { Query(it) }
}
}
abstract class ListCriteria : Criteria() {
inline fun <reified T : QueryCondition.ListOfValues<S>, reified S : Comparable<S>> comparison(): List<Query> {
QueryCondition.distinct<Session, T, S>()?.let {
val values = it.mapNotNull { session ->
when (this) {
is Limits -> if (session.limit is S) { session.limit as S } else throw PokerAnalyticsException.QueryValueMapUnexpectedValue
is TournamentTypes -> if (session.tournamentType is S) { session.tournamentType as S } else throw PokerAnalyticsException.QueryValueMapUnexpectedValue
is TableSizes -> if (session.tableSize is S) { session.tableSize as S } else throw PokerAnalyticsException.QueryValueMapUnexpectedValue
is TournamentFees -> if (session.tournamentEntryFee is S) { session.tournamentEntryFee as S } else throw PokerAnalyticsException.QueryValueMapUnexpectedValue
is Blinds -> if (session.blinds is S) { session.blinds as S } else throw PokerAnalyticsException.QueryValueMapUnexpectedValue
else -> null
}
}.distinct()
return compareList<T, S>(values = values)
}
return listOf()
}
}
object Bankrolls: RealmCriteria()
object Games: RealmCriteria()
object TournamentNames: RealmCriteria()
object Locations: RealmCriteria()
object TournamentFeatures: RealmCriteria()
object TransactionTypes: RealmCriteria()
object Limits: ListCriteria()
object TableSizes: ListCriteria()
object TournamentTypes: ListCriteria()
object MonthsOfYear: SimpleCriteria(List(12) { index -> QueryCondition.AnyMonthOfYear().apply { listOfValues = arrayListOf(index)} })
object DaysOfWeek: SimpleCriteria(List(7) { index -> QueryCondition.AnyDayOfWeek().apply { listOfValues = arrayListOf(index + 1) } })
object SessionTypes: SimpleCriteria(listOf(QueryCondition.IsCash, QueryCondition.IsTournament))
object BankrollTypes: SimpleCriteria(listOf(QueryCondition.IsLive, QueryCondition.IsOnline))
object DayPeriods: SimpleCriteria(listOf(QueryCondition.IsWeekDay, QueryCondition.IsWeekEnd))
object Years: ListCriteria()
object AllMonthsUpToNow: ListCriteria()
object Blinds: ListCriteria()
object TournamentFees: ListCriteria()
object Cash: SimpleCriteria(listOf(QueryCondition.IsCash))
object Tournament: SimpleCriteria(listOf(QueryCondition.IsTournament))
val queries: List<Query>
get() {
return when (this) {
is AllMonthsUpToNow -> {
val realm = Realm.getDefaultInstance()
val firstSession= realm.where<Session>().sort("startDate", Sort.ASCENDING).findFirst()
val lastSession = realm.where<Session>().sort("startDate", Sort.DESCENDING).findFirst()
realm.close()
val years: ArrayList<Query> = arrayListOf()
val firstYear = firstSession?.year ?: return years
val firstMonth = firstSession.month ?: return years
val lastYear = lastSession?.year ?: return years
val lastMonth = lastSession.month ?: return years
for (year in firstYear..lastYear) {
val currentYear = QueryCondition.AnyYear(year)
for (month in 0..11) {
if (year == firstYear && month < firstMonth) {
continue
}
if (year == lastYear && month > lastMonth) {
continue
}
val currentMonth = QueryCondition.AnyMonthOfYear(month)
val query = Query(currentYear, currentMonth)
years.add(query)
}
}
years
}
else -> {
return this.queryConditions
}
}
}
val queryConditions: List<Query>
get() {
return when (this) {
is Bankrolls -> comparison<Bankroll>()
is Games -> comparison<Game>()
is TournamentFeatures -> comparison<TournamentFeature>()
is TournamentNames -> comparison<TournamentName>()
is Locations -> comparison<Location>()
is TransactionTypes-> comparison<TransactionType>()
is SimpleCriteria -> comparison()
is Limits -> comparison<QueryCondition.AnyLimit, Int>()
is TournamentTypes -> comparison<QueryCondition.AnyTournamentType, Int>()
is TableSizes -> comparison<QueryCondition.AnyTableSize, Int>()
is TournamentFees -> comparison<QueryCondition.TournamentFee, Double >()
is Years -> {
val years = arrayListOf<Query>()
val realm = Realm.getDefaultInstance()
val lastSession = realm.where<Session>().sort("startDate", Sort.DESCENDING).findFirst()
val yearNow = lastSession?.year ?: return years
realm.where<Session>().sort("year", Sort.ASCENDING).findFirst()?.year?.let {
for (index in 0..(yearNow - it)) {
val yearCondition = QueryCondition.AnyYear().apply {
listOfValues = arrayListOf(it + index)
}
years.add(Query(yearCondition))
}
}
realm.close()
years
}
is Blinds -> comparison<QueryCondition.AnyBlind, String >()
else -> throw PokerAnalyticsException.QueryTypeUnhandled
}
}
companion object {
inline fun <reified S : QueryCondition.QueryDataCondition<NameManageable >, reified T : NameManageable> compare(): List<Query> {
val objects = mutableListOf<S>()
val realm = Realm.getDefaultInstance()
realm.where<T>().findAll().forEach {
val condition = (QueryCondition.getInstance<T>() as S).apply {
setObject(it)
}
objects.add(condition)
}
objects.sorted()
realm.close()
return objects.map { Query(it) }
}
inline fun < reified S : QueryCondition.ListOfValues<T>, T:Any > compareList(values:List<T>): List<Query> {
val objects = mutableListOf<S>()
values.forEach {
val condition =(S::class.java.newInstance()).apply {
listOfValues = arrayListOf(it)
}
objects.add(condition)
}
objects.sorted()
return objects.map { Query(it) }
}
}
sealed class Criteria : RowRepresentable {
abstract class RealmCriteria : Criteria() {
inline fun <reified T : NameManageable> comparison(): List<Query> {
return compare<QueryCondition.QueryDataCondition<NameManageable>, T>()
}
}
abstract class SimpleCriteria(private val conditions: List<QueryCondition>) : Criteria() {
fun comparison(): List<Query> {
return conditions.map { Query(it) }
}
}
abstract class ListCriteria : Criteria() {
inline fun <reified T : QueryCondition.ListOfValues<S>, reified S : Comparable<S>> comparison(): List<Query> {
QueryCondition.distinct<Session, T, S>()?.let {
val values = it.mapNotNull { session ->
when (this) {
is Limits -> if (session.limit is S) {
session.limit as S
} else throw PokerAnalyticsException.QueryValueMapUnexpectedValue
is TournamentTypes -> if (session.tournamentType is S) {
session.tournamentType as S
} else throw PokerAnalyticsException.QueryValueMapUnexpectedValue
is TableSizes -> if (session.tableSize is S) {
session.tableSize as S
} else throw PokerAnalyticsException.QueryValueMapUnexpectedValue
is TournamentFees -> if (session.tournamentEntryFee is S) {
session.tournamentEntryFee as S
} else throw PokerAnalyticsException.QueryValueMapUnexpectedValue
is Blinds -> if (session.blinds is S) {
session.blinds as S
} else throw PokerAnalyticsException.QueryValueMapUnexpectedValue
else -> null
}
}.distinct()
return compareList<T, S>(values = values)
}
return listOf()
}
}
object Bankrolls : RealmCriteria()
object Games : RealmCriteria()
object TournamentNames : RealmCriteria()
object Locations : RealmCriteria()
object TournamentFeatures : RealmCriteria()
object TransactionTypes : RealmCriteria()
object Limits : ListCriteria()
object TableSizes : ListCriteria()
object TournamentTypes : ListCriteria()
object MonthsOfYear : SimpleCriteria(List(12) { index ->
QueryCondition.AnyMonthOfYear().apply { listOfValues = arrayListOf(index) }
})
object DaysOfWeek : SimpleCriteria(List(7) { index ->
QueryCondition.AnyDayOfWeek().apply { listOfValues = arrayListOf(index + 1) }
})
object SessionTypes : SimpleCriteria(listOf(QueryCondition.IsCash, QueryCondition.IsTournament))
object BankrollTypes : SimpleCriteria(listOf(QueryCondition.IsLive, QueryCondition.IsOnline))
object DayPeriods : SimpleCriteria(listOf(QueryCondition.IsWeekDay, QueryCondition.IsWeekEnd))
object Years : ListCriteria()
object AllMonthsUpToNow : ListCriteria()
object Blinds : ListCriteria()
object TournamentFees : ListCriteria()
object Cash : SimpleCriteria(listOf(QueryCondition.IsCash))
object Tournament : SimpleCriteria(listOf(QueryCondition.IsTournament))
val queries: List<Query>
get() {
return when (this) {
is AllMonthsUpToNow -> {
val realm = Realm.getDefaultInstance()
val firstSession = realm.where<Session>().sort("startDate", Sort.ASCENDING).findFirst()
val lastSession = realm.where<Session>().sort("startDate", Sort.DESCENDING).findFirst()
realm.close()
val years: ArrayList<Query> = arrayListOf()
val firstYear = firstSession?.year ?: return years
val firstMonth = firstSession.month ?: return years
val lastYear = lastSession?.year ?: return years
val lastMonth = lastSession.month ?: return years
for (year in firstYear..lastYear) {
val currentYear = QueryCondition.AnyYear(year)
for (month in 0..11) {
if (year == firstYear && month < firstMonth) {
continue
}
if (year == lastYear && month > lastMonth) {
continue
}
val currentMonth = QueryCondition.AnyMonthOfYear(month)
val query = Query(currentYear, currentMonth)
years.add(query)
}
}
years
}
else -> {
return this.queryConditions
}
}
}
val queryConditions: List<Query>
get() {
return when (this) {
is Bankrolls -> comparison<Bankroll>()
is Games -> comparison<Game>()
is TournamentFeatures -> comparison<TournamentFeature>()
is TournamentNames -> comparison<TournamentName>()
is Locations -> comparison<Location>()
is TransactionTypes -> comparison<TransactionType>()
is SimpleCriteria -> comparison()
is Limits -> comparison<QueryCondition.AnyLimit, Int>()
is TournamentTypes -> comparison<QueryCondition.AnyTournamentType, Int>()
is TableSizes -> comparison<QueryCondition.AnyTableSize, Int>()
is TournamentFees -> comparison<QueryCondition.TournamentFee, Double>()
is Years -> {
val years = arrayListOf<Query>()
val realm = Realm.getDefaultInstance()
val lastSession = realm.where<Session>().sort("startDate", Sort.DESCENDING).findFirst()
val yearNow = lastSession?.year ?: return years
realm.where<Session>().sort("year", Sort.ASCENDING).findFirst()?.year?.let {
for (index in 0..(yearNow - it)) {
val yearCondition = QueryCondition.AnyYear().apply {
listOfValues = arrayListOf(it + index)
}
years.add(Query(yearCondition))
}
}
realm.close()
years
}
is Blinds -> comparison<QueryCondition.AnyBlind, String>()
else -> throw PokerAnalyticsException.QueryTypeUnhandled
}
}
companion object {
inline fun <reified S : QueryCondition.QueryDataCondition<NameManageable>, reified T : NameManageable> compare(): List<Query> {
val objects = mutableListOf<S>()
val realm = Realm.getDefaultInstance()
realm.where<T>().findAll().forEach {
val condition = (QueryCondition.getInstance<T>() as S).apply {
setObject(it)
}
objects.add(condition)
}
objects.sorted()
realm.close()
return objects.map { Query(it) }
}
inline fun <reified S : QueryCondition.ListOfValues<T>, T : Any> compareList(values: List<T>): List<Query> {
val objects = mutableListOf<S>()
values.forEach {
val condition = (S::class.java.newInstance()).apply {
listOfValues = arrayListOf(it)
}
objects.add(condition)
}
objects.sorted()
return objects.map { Query(it) }
}
val all: List<Criteria>
get() {
return listOf(
Bankrolls, Games, TournamentNames, Locations,
TournamentFeatures, Limits, TableSizes, TournamentTypes,
MonthsOfYear, DaysOfWeek, SessionTypes,
BankrollTypes, DayPeriods, Years,
AllMonthsUpToNow, Blinds, TournamentFees
)
}
}
}

@ -3,20 +3,11 @@ package net.pokeranalytics.android.model.realm
import io.realm.*
import io.realm.annotations.PrimaryKey
import io.realm.kotlin.where
import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.filter.Filterable
import net.pokeranalytics.android.model.filter.Query
import net.pokeranalytics.android.model.filter.QueryCondition
import net.pokeranalytics.android.model.interfaces.CountableUsage
import net.pokeranalytics.android.model.interfaces.NameManageable
import net.pokeranalytics.android.model.interfaces.SaveValidityStatus
import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowRepresentableEditDescriptor
import net.pokeranalytics.android.ui.view.rowrepresentable.FilterCategoryRow
import net.pokeranalytics.android.ui.view.rowrepresentable.GameRow
import net.pokeranalytics.android.ui.view.rowrepresentable.SimpleRow
import net.pokeranalytics.android.util.NULL_TEXT
import timber.log.Timber
import java.util.*
@ -51,6 +42,11 @@ open class Filter : RealmObject(), RowRepresentable {
Timber.d(">>> Filter query: ${realmQuery.description}")
return realmQuery.findAll()
}
fun sortedByUsage(realm: Realm): RealmResults<Filter> {
return realm.where(Filter::class.java).findAll().sort("usageCount")
}
}
@PrimaryKey

@ -3,12 +3,13 @@ package net.pokeranalytics.android.model.realm
import io.realm.RealmList
import io.realm.RealmObject
import io.realm.annotations.PrimaryKey
import net.pokeranalytics.android.ui.view.RowRepresentable
import java.util.*
enum class ReportDisplay {
TABLE,
GRAPH,
MAP
enum class ReportDisplay : RowRepresentable {
FIGURES,
EVO_GRAPH,
COMPARISON_GRAPH
}
open class ReportSetup : RealmObject() {
@ -20,7 +21,7 @@ open class ReportSetup : RealmObject() {
var name: String = ""
// The type of display of the report
var display: Int = ReportDisplay.TABLE.ordinal
var display: Int = ReportDisplay.FIGURES.ordinal
// @todo define the configuration options

@ -0,0 +1,24 @@
package net.pokeranalytics.android.ui.activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import net.pokeranalytics.android.R
import net.pokeranalytics.android.ui.activity.components.PokerAnalyticsActivity
class ReportCreationActivity : PokerAnalyticsActivity() {
companion object {
fun newInstance(context: Context) {
val intent = Intent(context, ReportCreationActivity::class.java)
context.startActivity(intent)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_reportcreation)
}
}

@ -0,0 +1,102 @@
package net.pokeranalytics.android.ui.fragment
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import io.realm.Realm
import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.model.Criteria
import net.pokeranalytics.android.model.realm.Filter
import net.pokeranalytics.android.model.realm.ReportDisplay
import net.pokeranalytics.android.ui.fragment.components.RealmFragment
import net.pokeranalytics.android.ui.view.RowRepresentable
class ReportCreationFragment : RealmFragment() {
class Process {
var step: Step = Step.TYPE
var display: ReportDisplay? = null
var stats = listOf<Stat>()
var comparators = listOf<Criteria>()
var useFilter: Boolean? = null
var filter: Filter? = null
enum class Step {
TYPE,
STAT,
COMPARATOR,
FILTER,
FINALIZE
}
val nextStep: Step
get() {
return when (this.display) {
null -> Step.TYPE
else -> {
if (this.stats.isEmpty()) {
Step.STAT
} else if (this.display!! == ReportDisplay.COMPARISON_GRAPH && this.comparators.isEmpty()) {
Step.COMPARATOR
} else if (this.useFilter == null) {
Step.FILTER
} else {
Step.FINALIZE
}
}
}
}
fun titleForStep(step: Step) : Int? {
return when (step) {
Step.TYPE -> R.string.new_report_step_type
Step.STAT -> R.string.new_report_step_stat
Step.COMPARATOR -> R.string.new_report_step_comparator
Step.FILTER -> R.string.new_report_step_filter
else -> null
}
}
val dataSource: List<RowRepresentable>
get() {
return when (this.step) {
Step.TYPE -> return listOf(ReportDisplay.FIGURES, ReportDisplay.EVO_GRAPH, ReportDisplay.COMPARISON_GRAPH)
Step.STAT -> return Stat.values().toList()
Step.COMPARATOR -> Criteria.all
Step.FILTER -> {
val realm = Realm.getDefaultInstance()
val filters = Filter.sortedByUsage(realm)
realm.close()
filters
}
else -> listOf()
}
}
val nextButtonShouldAppear: Boolean
get() {
return when (this.step) {
Step.STAT, Step.COMPARATOR, Step.FILTER -> true
else -> false
}
}
val nextButtonTitle: Int
get() {
return when (this.step) {
Step.FILTER -> R.string.launch_report
else -> R.string.next
}
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
super.onCreateView(inflater, container, savedInstanceState)
return inflater.inflate(R.layout.fragment_report_creation, container, false)
}
}

@ -7,7 +7,8 @@ import android.view.ViewGroup
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import io.realm.Realm
import kotlinx.android.synthetic.main.fragment_stats.*
import kotlinx.android.synthetic.main.fragment_data_list.*
import kotlinx.android.synthetic.main.fragment_stats.recyclerView
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
@ -16,6 +17,7 @@ import net.pokeranalytics.android.calculus.Calculator
import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.model.Criteria
import net.pokeranalytics.android.model.combined
import net.pokeranalytics.android.ui.activity.ReportCreationActivity
import net.pokeranalytics.android.ui.activity.ReportDetailsActivity
import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter
import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate
@ -100,6 +102,11 @@ class ReportsFragment : PokerAnalyticsFragment(), StaticRowRepresentableDataSour
layoutManager = viewManager
adapter = reportsAdapter
}
this.addButton.setOnClickListener {
ReportCreationActivity.newInstance(requireContext())
}
}
/**

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<fragment
android:id="@+id/reportCreationFragment"
android:name="net.pokeranalytics.android.ui.fragment.ReportCreationFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:layout="@layout/fragment_report_creation" />
</LinearLayout>

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
app:title="@string/new_report"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</LinearLayout>

@ -15,4 +15,17 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/addButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:src="@drawable/ic_add"
android:tint="@color/black"
app:fabSize="normal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

@ -24,6 +24,11 @@
<string name="f_support">Support</string>
<string name="f_support_desc">We try to answer as quickly as we can, in english or french !</string>
<string name="loading_please_wait">Loading, please wait&#8230;</string>
<string name="new_report_step_type">Select your type of report</string>
<string name="new_report_step_stat">Select one or more statistics</string>
<string name="new_report_step_comparator">Select one or more comparison criteria</string>
<string name="new_report_step_filter">Select a filter if you want, or launch report</string>
<string name="launch_report">Launch Report</string>
<string name="address">Address</string>
<string name="suggestions">Naming suggestions</string>

Loading…
Cancel
Save