Improve mass creation performances

feature/top10
Laurent 7 years ago
commit 90be7a995d
  1. 18
      app/src/main/java/net/pokeranalytics/android/PokerAnalyticsApplication.kt
  2. 6
      app/src/main/java/net/pokeranalytics/android/calculus/Calculator.kt
  3. 2
      app/src/main/java/net/pokeranalytics/android/calculus/Stat.kt
  4. 4
      app/src/main/java/net/pokeranalytics/android/exceptions/Exceptions.kt
  5. 22
      app/src/main/java/net/pokeranalytics/android/model/LiveData.kt
  6. 2
      app/src/main/java/net/pokeranalytics/android/model/extensions/SessionExtensions.kt
  7. 2
      app/src/main/java/net/pokeranalytics/android/model/interfaces/CountableUsage.kt
  8. 2
      app/src/main/java/net/pokeranalytics/android/model/interfaces/Manageable.kt
  9. 20
      app/src/main/java/net/pokeranalytics/android/model/realm/Bankroll.kt
  10. 20
      app/src/main/java/net/pokeranalytics/android/model/realm/Game.kt
  11. 65
      app/src/main/java/net/pokeranalytics/android/model/realm/Location.kt
  12. 70
      app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt
  13. 20
      app/src/main/java/net/pokeranalytics/android/model/realm/TournamentFeature.kt
  14. 19
      app/src/main/java/net/pokeranalytics/android/model/realm/TournamentName.kt
  15. 19
      app/src/main/java/net/pokeranalytics/android/model/realm/TransactionType.kt
  16. 29
      app/src/main/java/net/pokeranalytics/android/ui/activity/components/PokerAnalyticsActivity.kt
  17. 49
      app/src/main/java/net/pokeranalytics/android/ui/fragment/CurrenciesFragment.kt
  18. 2
      app/src/main/java/net/pokeranalytics/android/ui/fragment/DataListFragment.kt
  19. 30
      app/src/main/java/net/pokeranalytics/android/ui/fragment/EditableDataFragment.kt
  20. 10
      app/src/main/java/net/pokeranalytics/android/ui/fragment/HistoryFragment.kt
  21. 207
      app/src/main/java/net/pokeranalytics/android/ui/fragment/LocationDataFragment.kt
  22. 2
      app/src/main/java/net/pokeranalytics/android/ui/fragment/SessionFragment.kt
  23. 10
      app/src/main/java/net/pokeranalytics/android/ui/fragment/SettingsFragment.kt
  24. 52
      app/src/main/java/net/pokeranalytics/android/ui/fragment/StatsFragment.kt
  25. 14
      app/src/main/java/net/pokeranalytics/android/ui/view/HistorySessionDiffCallback.kt
  26. 26
      app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt
  27. 9
      app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/CustomizableRowRepresentable.kt
  28. 16
      app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/LocationRow.kt
  29. 10
      app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/SettingRow.kt
  30. 26
      app/src/main/java/net/pokeranalytics/android/util/LocationManager.kt
  31. 37
      app/src/main/res/layout/row_loader.xml
  32. 1
      app/src/main/res/values/strings.xml

@ -6,6 +6,7 @@ import io.realm.RealmConfiguration
import io.realm.RealmResults import io.realm.RealmResults
import io.realm.kotlin.where import io.realm.kotlin.where
import net.pokeranalytics.android.model.Limit import net.pokeranalytics.android.model.Limit
import net.pokeranalytics.android.model.realm.Bankroll
import net.pokeranalytics.android.model.realm.Game import net.pokeranalytics.android.model.realm.Game
import net.pokeranalytics.android.model.realm.Session import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.model.utils.Seed import net.pokeranalytics.android.model.utils.Seed
@ -75,18 +76,21 @@ class PokerAnalyticsApplication : Application() {
-2500.0, -2000.0, -1500.0, -1000.0, -500.0, 200.0, 1000.0, 1500.0, 2500.0 -2500.0, -2000.0, -1500.0, -1000.0, -500.0, 200.0, 1000.0, 1500.0, 2500.0
) )
Thread().run() { val commitFrequency = 100
Thread() {
try { try {
val realm = Realm.getDefaultInstance() val realm = Realm.getDefaultInstance()
val games = realm.where<Game>().findAll() val games = realm.where<Game>().findAll()
val bankroll = realm.where<Bankroll>().findAll().firstOrNull()
// Test endedSessions // Test endedSessions
val sessions = realm.where<Session>().findAll() val sessions = realm.where<Session>().findAll()
if (sessions.size < 10) { if (sessions.size < 10) {
val numberOfSessions = 10000 val numberOfSessions = 2000
Timber.d("*** Start creating ${numberOfSessions} fake sessions...") Timber.d("*** Start creating ${numberOfSessions} fake sessions...")
val s = Date() val s = Date()
@ -95,17 +99,17 @@ class PokerAnalyticsApplication : Application() {
for (index in 0..numberOfSessions) { for (index in 0..numberOfSessions) {
if (index % 1000 == 999) { if (index % commitFrequency == 0) {
Timber.d("****** committing at ${index} sessions...") Timber.d("****** committing at ${index} sessions...")
realm.commitTransaction() realm.commitTransaction()
realm.beginTransaction() realm.beginTransaction()
} }
val session = Session.newInstance(realm, false) val session = Session.newInstance(realm, false, bankroll)
val calendar = Calendar.getInstance() val calendar = Calendar.getInstance()
calendar.set( calendar.set(
(2017..2018).random(), (2016..2018).random(),
(0..11).random(), (0..11).random(),
(0..28).random(), (0..28).random(),
(0..23).random(), (0..23).random(),
@ -117,8 +121,6 @@ class PokerAnalyticsApplication : Application() {
calendar.add(Calendar.MINUTE, (0..59).random()) calendar.add(Calendar.MINUTE, (0..59).random())
val endDate = calendar.time val endDate = calendar.time
// val timeFrame = TimeFrame()
session.startDate = startDate session.startDate = startDate
session.endDate = endDate session.endDate = endDate
session.creationDate = startDate session.creationDate = startDate
@ -147,7 +149,7 @@ class PokerAnalyticsApplication : Application() {
Timber.e(e) Timber.e(e)
} }
} }.start()
} }

@ -2,6 +2,7 @@ package net.pokeranalytics.android.calculus
import net.pokeranalytics.android.calculus.Stat.* import net.pokeranalytics.android.calculus.Stat.*
import net.pokeranalytics.android.model.realm.SessionSet import net.pokeranalytics.android.model.realm.SessionSet
import timber.log.Timber
/** /**
* The class performing stats computation * The class performing stats computation
@ -56,6 +57,7 @@ class Calculator {
companion object { companion object {
fun computePreAggregation(sets: List<SessionSet>, options: Options): List<ComputedResults> { fun computePreAggregation(sets: List<SessionSet>, options: Options): List<ComputedResults> {
Timber.d("sets = ${sets.size}")
return listOf() return listOf()
} }
@ -89,6 +91,8 @@ class Calculator {
*/ */
fun compute(sessionGroup: SessionGroup, options: Options) : ComputedResults { fun compute(sessionGroup: SessionGroup, options: Options) : ComputedResults {
Timber.d(">>>> Start computing group ${sessionGroup.name}, ${sessionGroup.sessions.size} sessions")
val sessions: List<SessionInterface> = sessionGroup.sessions val sessions: List<SessionInterface> = sessionGroup.sessions
val sessionSets = sessionGroup.sessions.mapNotNull { it.sessionSet }.toHashSet() val sessionSets = sessionGroup.sessions.mapNotNull { it.sessionSet }.toHashSet()
@ -193,7 +197,7 @@ class Calculator {
)) ))
} }
// Create stats // Create stats
results.addStats(setOf( results.addStats(setOf(
ComputedStat(NETRESULT, sum), ComputedStat(NETRESULT, sum),
ComputedStat(DURATION, duration), ComputedStat(DURATION, duration),

@ -162,7 +162,7 @@ class ComputedStat(stat: Stat, value: Double) {
* Returns a TextFormat instance for an evolution value located at the specified [index] * Returns a TextFormat instance for an evolution value located at the specified [index]
*/ */
fun evolutionValueFormat(index: Int) : TextFormat { fun evolutionValueFormat(index: Int) : TextFormat {
return TextFormat("undef") return TextFormat("undef ${index}")
} }
} }

@ -7,7 +7,3 @@ class ModelException(message: String) : Exception(message) {
class FormattingException(message: String) : Exception(message) { class FormattingException(message: String) : Exception(message) {
} }
class TypeException(message: String) : Exception(message) {
}

@ -40,10 +40,22 @@ enum class LiveData : Localizable {
fun setUseCount(realm: Realm, realmResults: RealmResults<*>) { fun setUseCount(realm: Realm, realmResults: RealmResults<*>) {
realm.executeTransaction { realm.executeTransaction {
realmResults.forEach { countableUsage -> realmResults.forEach { countableUsage ->
(countableUsage as CountableUsage).useCount = it.where<Session>().equalTo(
"${relatedEntity.simpleName.toLowerCase()}.id", when (this) {
countableUsage.uniqueIdentifier() TOURNAMENT_FEATURE -> {
).count().toInt() (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()
}
}
} }
} }
} }
@ -81,7 +93,7 @@ enum class LiveData : Localizable {
} }
fun deleteData(realm: Realm, data: Manageable) { fun deleteData(realm: Realm, data: Manageable) {
realm.where(this.relatedEntity).equalTo("id", data.uniqueIdentifier()).findAll().deleteAllFromRealm() realm.where(this.relatedEntity).equalTo("id", data.id).findAll().deleteAllFromRealm()
} }
fun updateOrCreate(realm: Realm, primaryKey: String?): RealmObject { fun updateOrCreate(realm: Realm, primaryKey: String?): RealmObject {

@ -43,4 +43,4 @@ fun Session.getState(): SessionState {
} }
} }
} }

@ -6,5 +6,5 @@ package net.pokeranalytics.android.model.interfaces
interface CountableUsage : Identifiable { interface CountableUsage : Identifiable {
var useCount: Int var useCount: Int
get() { return 0 } get() { return 0 }
set(newValue) {} set(_) {}
} }

@ -16,7 +16,7 @@ interface Identifiable {
/** /**
* A unique identifier getter * A unique identifier getter
*/ */
fun uniqueIdentifier(): String var id: String
} }
/** /**

@ -11,6 +11,7 @@ import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowRepresentableEditDescriptor import net.pokeranalytics.android.ui.view.RowRepresentableEditDescriptor
import net.pokeranalytics.android.ui.view.rowrepresentable.BankrollRow import net.pokeranalytics.android.ui.view.rowrepresentable.BankrollRow
import net.pokeranalytics.android.ui.view.rowrepresentable.SimpleRow import net.pokeranalytics.android.ui.view.rowrepresentable.SimpleRow
import timber.log.Timber
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
@ -22,10 +23,17 @@ open class Bankroll(name: String = "") : RealmObject(), Manageable,
var bankroll: Bankroll = Bankroll() var bankroll: Bankroll = Bankroll()
return bankroll return bankroll
} }
val rowRepresentation : List<RowRepresentable> by lazy {
val rows = ArrayList<RowRepresentable>()
rows.add(SimpleRow.NAME)
rows.addAll(BankrollRow.values())
rows
}
} }
@PrimaryKey @PrimaryKey
var id = UUID.randomUUID().toString() override var id = UUID.randomUUID().toString()
// the name of the bankroll // the name of the bankroll
var name: String = name var name: String = name
@ -43,17 +51,9 @@ open class Bankroll(name: String = "") : RealmObject(), Manageable,
return this.name return this.name
} }
override fun uniqueIdentifier(): String {
return this.id
}
// Row Representable Datasource // Row Representable Datasource
override fun adapterRows(): List<RowRepresentable>? { override fun adapterRows(): List<RowRepresentable>? {
val rows = ArrayList<RowRepresentable>() return Bankroll.rowRepresentation
rows.add(SimpleRow.NAME)
rows.addAll(BankrollRow.values())
return rows
} }
override fun stringForRow(row: RowRepresentable): String { override fun stringForRow(row: RowRepresentable): String {

@ -20,8 +20,17 @@ import kotlin.collections.ArrayList
open class Game : RealmObject(), Manageable, StaticRowRepresentableDataSource, RowRepresentable, CountableUsage { open class Game : RealmObject(), Manageable, StaticRowRepresentableDataSource, RowRepresentable, CountableUsage {
companion object {
val rowRepresentation : List<RowRepresentable> by lazy {
val rows = ArrayList<RowRepresentable>()
rows.add(SimpleRow.NAME)
rows.addAll(GameRow.values())
rows
}
}
@PrimaryKey @PrimaryKey
var id = UUID.randomUUID().toString() override var id = UUID.randomUUID().toString()
// The name of the game // The name of the game
var name: String = "" var name: String = ""
@ -36,15 +45,8 @@ open class Game : RealmObject(), Manageable, StaticRowRepresentableDataSource, R
return this.name return this.name
} }
override fun uniqueIdentifier(): String {
return this.id
}
override fun adapterRows(): List<RowRepresentable>? { override fun adapterRows(): List<RowRepresentable>? {
val rows = ArrayList<RowRepresentable>() return Game.rowRepresentation
rows.add(SimpleRow.NAME)
rows.addAll(GameRow.values())
return rows
} }
override fun stringForRow(row: RowRepresentable): String { override fun stringForRow(row: RowRepresentable): String {

@ -2,23 +2,18 @@ package net.pokeranalytics.android.model.realm
import com.google.android.libraries.places.api.model.Place import com.google.android.libraries.places.api.model.Place
import io.realm.RealmObject import io.realm.RealmObject
import io.realm.annotations.Ignore
import io.realm.annotations.PrimaryKey import io.realm.annotations.PrimaryKey
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.interfaces.Manageable import net.pokeranalytics.android.model.interfaces.Manageable
import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource
import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowRepresentableEditDescriptor
import net.pokeranalytics.android.ui.view.rowrepresentable.LocationRow
import net.pokeranalytics.android.ui.view.rowrepresentable.SimpleRow import net.pokeranalytics.android.ui.view.rowrepresentable.SimpleRow
import java.util.* import java.util.*
import kotlin.collections.ArrayList
open class Location : RealmObject(), Manageable, StaticRowRepresentableDataSource, RowRepresentable { open class Location : RealmObject(), Manageable, RowRepresentable {
@PrimaryKey @PrimaryKey
var id = UUID.randomUUID().toString() override var id = UUID.randomUUID().toString()
// The name of the location // The name of the location
var name: String = "" var name: String = ""
@ -32,69 +27,13 @@ open class Location : RealmObject(), Manageable, StaticRowRepresentableDataSourc
// the latitude of the location // the latitude of the location
var latitude: Double? = null var latitude: Double? = null
@Ignore
var isLookingForPlaces = false
override fun getDisplayName(): String { override fun getDisplayName(): String {
return this.name return this.name
} }
override fun uniqueIdentifier(): String {
return this.id
}
override fun adapterRows(): List<RowRepresentable>? {
val rows = ArrayList<RowRepresentable>()
rows.add(SimpleRow.NAME)
rows.addAll(LocationRow.values())
return rows
}
override fun stringForRow(row: RowRepresentable): String {
return when (row) {
SimpleRow.NAME -> this.name
LocationRow.ADDRESS -> this.address
else -> return super.stringForRow(row)
}
}
override fun boolForRow(row: RowRepresentable): Boolean {
return when(row) {
LocationRow.LOCATE_ME -> return isLookingForPlaces
else -> super.boolForRow(row)
}
}
override fun editDescriptors(row: RowRepresentable): ArrayList<RowRepresentableEditDescriptor> {
val data = java.util.ArrayList<RowRepresentableEditDescriptor>()
when (row) {
SimpleRow.NAME -> data.add(
RowRepresentableEditDescriptor(
this.name,
SimpleRow.NAME.resId
)
)
LocationRow.ADDRESS -> data.add(
RowRepresentableEditDescriptor(
this.address,
LocationRow.ADDRESS.resId
)
)
}
return data
}
override fun updateValue(value: Any?, row: RowRepresentable) { override fun updateValue(value: Any?, row: RowRepresentable) {
when (row) { when (row) {
SimpleRow.NAME -> this.name = value as String? ?: "" SimpleRow.NAME -> this.name = value as String? ?: ""
LocationRow.ADDRESS -> this.address = value as String? ?: ""
LocationRow.LOCATE_ME -> {
isLookingForPlaces = false
if (value is Place) {
setPlace(value)
}
}
} }
} }

@ -28,7 +28,7 @@ import net.pokeranalytics.android.ui.adapter.UnmanagedRowRepresentableException
import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowRepresentableEditDescriptor import net.pokeranalytics.android.ui.view.RowRepresentableEditDescriptor
import net.pokeranalytics.android.ui.view.RowViewType import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.ui.view.rowrepresentable.HeaderRowRepresentable import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable
import net.pokeranalytics.android.ui.view.rowrepresentable.SeparatorRowRepresentable import net.pokeranalytics.android.ui.view.rowrepresentable.SeparatorRowRepresentable
import net.pokeranalytics.android.ui.view.rowrepresentable.SessionRow import net.pokeranalytics.android.ui.view.rowrepresentable.SessionRow
import net.pokeranalytics.android.util.* import net.pokeranalytics.android.util.*
@ -36,6 +36,8 @@ import net.pokeranalytics.android.util.extensions.*
import java.util.* import java.util.*
import java.util.Currency import java.util.Currency
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
import kotlin.properties.Delegates
import kotlin.reflect.KProperty
open class Session : RealmObject(), SessionInterface, Manageable, StaticRowRepresentableDataSource, RowRepresentable, open class Session : RealmObject(), SessionInterface, Manageable, StaticRowRepresentableDataSource, RowRepresentable,
Timed { Timed {
@ -46,17 +48,21 @@ open class Session : RealmObject(), SessionInterface, Manageable, StaticRowRepre
} }
companion object { companion object {
fun newInstance(realm: Realm, isTournament: Boolean): Session { fun newInstance(realm: Realm, isTournament: Boolean, bankroll: Bankroll? = null): Session {
val session = Session() val session = Session()
session.result = Result() session.result = Result()
session.bankroll = realm.where<Bankroll>().findFirst() if (bankroll != null) {
session.bankroll = bankroll
} else {
session.bankroll = realm.where<Bankroll>().findFirst()
}
session.type = if (isTournament) Session.Type.TOURNAMENT.ordinal else Session.Type.CASH_GAME.ordinal session.type = if (isTournament) Session.Type.TOURNAMENT.ordinal else Session.Type.CASH_GAME.ordinal
return realm.copyToRealm(session) return realm.copyToRealm(session)
} }
} }
@PrimaryKey @PrimaryKey
var id = UUID.randomUUID().toString() override var id = UUID.randomUUID().toString()
/** /**
* Indicates the type of session, cash game or tournament * Indicates the type of session, cash game or tournament
@ -110,6 +116,11 @@ open class Session : RealmObject(), SessionInterface, Manageable, StaticRowRepre
* The start date of the break * The start date of the break
*/ */
override var pauseDate: Date? = null override var pauseDate: Date? = null
set(value) {
field = value
this.updateRowRepresentation()
}
// The time frame of the Session, i.e. the start & end date // The time frame of the Session, i.e. the start & end date
// var timeFrame: TimeFrame? = null // var timeFrame: TimeFrame? = null
@ -186,6 +197,7 @@ open class Session : RealmObject(), SessionInterface, Manageable, StaticRowRepre
} else if (this.sessionSet != null) { } else if (this.sessionSet != null) {
SessionSetManager.removeFromTimeline(this) SessionSetManager.removeFromTimeline(this)
} }
this.updateRowRepresentation()
} }
/** /**
@ -224,22 +236,6 @@ open class Session : RealmObject(), SessionInterface, Manageable, StaticRowRepre
return this.result?.net ?: 0.0 return this.result?.net ?: 0.0
} }
/*
public var numberOfHandsPerHour: Double {
var playersHandsPerHour: Int = Session.livePlayersHandsPerHour // default is live
if let bankroll = self.bankroll {
playersHandsPerHour = bankroll.isLive() ? Session.livePlayersHandsPerHour : Session.onlinePlayersHandsPerHour
}
var numberOfPlayers: Int = 9 // default is 9 players
if let playersAtTable = self.tableSize?.intValue {
numberOfPlayers = playersAtTable
}
return Double(playersHandsPerHour) / Double(numberOfPlayers)
}
*/
@Ignore @Ignore
val ONLINE_PLAYER_HANDS_PER_HOUR = 400.0 val ONLINE_PLAYER_HANDS_PER_HOUR = 400.0
@Ignore @Ignore
@ -250,7 +246,7 @@ open class Session : RealmObject(), SessionInterface, Manageable, StaticRowRepre
*/ */
val numberOfHandsPerHour: Double val numberOfHandsPerHour: Double
get() { get() {
val tableSize = this.tableSize ?: 9 val tableSize = this.tableSize ?: 9 // 9 is the default table size if null
val isLive = this.bankroll?.live ?: true val isLive = this.bankroll?.live ?: true
val playerHandsPerHour = if (isLive) LIVE_PLAYER_HANDS_PER_HOUR else ONLINE_PLAYER_HANDS_PER_HOUR val playerHandsPerHour = if (isLive) LIVE_PLAYER_HANDS_PER_HOUR else ONLINE_PLAYER_HANDS_PER_HOUR
return playerHandsPerHour / tableSize.toDouble() return playerHandsPerHour / tableSize.toDouble()
@ -461,22 +457,21 @@ open class Session : RealmObject(), SessionInterface, Manageable, StaticRowRepre
@Ignore @Ignore
override val viewType: Int = RowViewType.ROW_SESSION.ordinal override val viewType: Int = RowViewType.ROW_SESSION.ordinal
override fun uniqueIdentifier(): String {
return this.id
}
override fun getDisplayName(): String { override fun getDisplayName(): String {
return "Session ${this.creationDate}" return "Session ${this.creationDate}"
} }
override fun adapterRows(): List<RowRepresentable>? { @Ignore
private var rowRepresentationForCurrentState : List<RowRepresentable> = this.updatedRowRepresentationForCurrentState()
private fun updatedRowRepresentationForCurrentState(): List<RowRepresentable> {
val rows = ArrayList<RowRepresentable>() val rows = ArrayList<RowRepresentable>()
// Headers // Headers
when (getState()) { when (getState()) {
SessionState.STARTED -> { SessionState.STARTED -> {
rows.add( rows.add(
HeaderRowRepresentable( CustomizableRowRepresentable(
RowViewType.HEADER_TITLE_AMOUNT_BIG, RowViewType.HEADER_TITLE_AMOUNT_BIG,
title = getFormattedDuration(), title = getFormattedDuration(),
computedStat = ComputedStat(Stat.NETRESULT, result?.net ?: 0.0) computedStat = ComputedStat(Stat.NETRESULT, result?.net ?: 0.0)
@ -486,7 +481,7 @@ open class Session : RealmObject(), SessionInterface, Manageable, StaticRowRepre
} }
SessionState.PAUSED -> { SessionState.PAUSED -> {
rows.add( rows.add(
HeaderRowRepresentable( CustomizableRowRepresentable(
RowViewType.HEADER_TITLE_AMOUNT_BIG, RowViewType.HEADER_TITLE_AMOUNT_BIG,
resId = R.string.pause, resId = R.string.pause,
computedStat = ComputedStat(Stat.NETRESULT, result?.net ?: 0.0) computedStat = ComputedStat(Stat.NETRESULT, result?.net ?: 0.0)
@ -496,33 +491,29 @@ open class Session : RealmObject(), SessionInterface, Manageable, StaticRowRepre
} }
SessionState.FINISHED -> { SessionState.FINISHED -> {
rows.add( rows.add(
HeaderRowRepresentable( CustomizableRowRepresentable(
RowViewType.HEADER_TITLE_AMOUNT_BIG, RowViewType.HEADER_TITLE_AMOUNT_BIG,
title = getFormattedDuration(), title = getFormattedDuration(),
computedStat = ComputedStat(Stat.NETRESULT, result?.net ?: 0.0) computedStat = ComputedStat(Stat.NETRESULT, result?.net ?: 0.0)
) )
) )
rows.add( rows.add(
HeaderRowRepresentable( CustomizableRowRepresentable(
RowViewType.HEADER_TITLE_AMOUNT, RowViewType.HEADER_TITLE_AMOUNT,
resId = R.string.hour_rate_without_pauses, resId = R.string.hour_rate_without_pauses,
computedStat = ComputedStat(Stat.HOURLY_RATE, this.hourlyRate) computedStat = ComputedStat(Stat.HOURLY_RATE, this.hourlyRate)
) )
) )
//TODO V2: Add Bankroll variation
/*
if (!isTournament()) { if (!isTournament()) {
rows.add( rows.add(
HeaderRowRepresentable( CustomizableRowRepresentable(
RowViewType.HEADER_TITLE_VALUE, RowViewType.HEADER_TITLE_VALUE,
resId = R.string.bankroll_variation, resId = R.string.bankroll_variation,
computedStat = ComputedStat(Stat.HOURLY_RATE, 0.0) computedStat = ComputedStat(Stat.HOURLY_RATE, 0.0)
) )
) )
} }
*/
rows.add(SeparatorRowRepresentable()) rows.add(SeparatorRowRepresentable())
} }
else -> { else -> {
@ -534,6 +525,15 @@ open class Session : RealmObject(), SessionInterface, Manageable, StaticRowRepre
return rows return rows
} }
private fun updateRowRepresentation() {
this.rowRepresentationForCurrentState = this.updatedRowRepresentationForCurrentState()
}
override fun adapterRows(): List<RowRepresentable>? {
return this.rowRepresentationForCurrentState
}
override fun boolForRow(row: RowRepresentable): Boolean { override fun boolForRow(row: RowRepresentable): Boolean {
return false return false
} }

@ -16,8 +16,17 @@ import kotlin.collections.ArrayList
open class TournamentFeature : RealmObject(), Manageable, StaticRowRepresentableDataSource, RowRepresentable, open class TournamentFeature : RealmObject(), Manageable, StaticRowRepresentableDataSource, RowRepresentable,
CountableUsage { CountableUsage {
companion object {
val rowRepresentation : List<RowRepresentable> by lazy {
val rows = ArrayList<RowRepresentable>()
rows.add(SimpleRow.NAME)
rows.addAll(TournamentFeatureRow.values())
rows
}
}
@PrimaryKey @PrimaryKey
var id = UUID.randomUUID().toString() override var id = UUID.randomUUID().toString()
// The name of the feature // The name of the feature
var name: String = "" var name: String = ""
@ -29,15 +38,8 @@ open class TournamentFeature : RealmObject(), Manageable, StaticRowRepresentable
return this.name return this.name
} }
override fun uniqueIdentifier(): String {
return this.id
}
override fun adapterRows(): List<RowRepresentable>? { override fun adapterRows(): List<RowRepresentable>? {
val rows = ArrayList<RowRepresentable>() return TournamentFeature.rowRepresentation
rows.add(SimpleRow.NAME)
rows.addAll(TournamentFeatureRow.values())
return rows
} }
override fun stringForRow(row: RowRepresentable): String { override fun stringForRow(row: RowRepresentable): String {

@ -15,9 +15,17 @@ import kotlin.collections.ArrayList
open class TournamentName : RealmObject(), Manageable, StaticRowRepresentableDataSource, RowRepresentable { open class TournamentName : RealmObject(), Manageable, StaticRowRepresentableDataSource, RowRepresentable {
companion object {
val rowRepresentation : List<RowRepresentable> by lazy {
val rows = ArrayList<RowRepresentable>()
rows.add(SimpleRow.NAME)
rows.addAll(TournamentNameRow.values())
rows
}
}
@PrimaryKey @PrimaryKey
var id = UUID.randomUUID().toString() override var id = UUID.randomUUID().toString()
// The name of the tournament // The name of the tournament
var name: String = "" var name: String = ""
@ -26,10 +34,6 @@ open class TournamentName : RealmObject(), Manageable, StaticRowRepresentableDat
return this.name return this.name
} }
override fun uniqueIdentifier(): String {
return this.id
}
override fun updateValue(value: Any?, row: RowRepresentable) { override fun updateValue(value: Any?, row: RowRepresentable) {
when (row) { when (row) {
SimpleRow.NAME -> this.name = value as String? ?: "" SimpleRow.NAME -> this.name = value as String? ?: ""
@ -37,10 +41,7 @@ open class TournamentName : RealmObject(), Manageable, StaticRowRepresentableDat
} }
override fun adapterRows(): List<RowRepresentable>? { override fun adapterRows(): List<RowRepresentable>? {
val rows = ArrayList<RowRepresentable>() return TournamentName.rowRepresentation
rows.add(SimpleRow.NAME)
rows.addAll(TournamentNameRow.values())
return rows
} }
override fun stringForRow(row: RowRepresentable): String { override fun stringForRow(row: RowRepresentable): String {

@ -15,9 +15,17 @@ import kotlin.collections.ArrayList
open class TransactionType : RealmObject(), Manageable, StaticRowRepresentableDataSource, RowRepresentable { open class TransactionType : RealmObject(), Manageable, StaticRowRepresentableDataSource, RowRepresentable {
companion object {
val rowRepresentation : List<RowRepresentable> by lazy {
val rows = ArrayList<RowRepresentable>()
rows.add(SimpleRow.NAME)
rows.addAll(TransactionTypeRow.values())
rows
}
}
@PrimaryKey @PrimaryKey
var id = UUID.randomUUID().toString() override var id = UUID.randomUUID().toString()
// The name of the transaction type // The name of the transaction type
var name: String = "" var name: String = ""
@ -35,15 +43,8 @@ open class TransactionType : RealmObject(), Manageable, StaticRowRepresentableDa
return this.name return this.name
} }
override fun uniqueIdentifier(): String {
return this.id
}
override fun adapterRows(): List<RowRepresentable>? { override fun adapterRows(): List<RowRepresentable>? {
val rows = ArrayList<RowRepresentable>() return TransactionType.rowRepresentation
rows.add(SimpleRow.NAME)
rows.addAll(TransactionTypeRow.values())
return rows
} }
override fun stringForRow(row: RowRepresentable): String { override fun stringForRow(row: RowRepresentable): String {

@ -68,6 +68,13 @@ open class PokerAnalyticsActivity : AppCompatActivity() {
return realm return realm
} }
/**
* Return if the location permission has been granted by the user
*/
fun hasLocationPermissionGranted() : Boolean {
return ContextCompat.checkSelfPermission(this, ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
}
/** /**
* Ask for location permission * Ask for location permission
*/ */
@ -78,7 +85,6 @@ open class PokerAnalyticsActivity : AppCompatActivity() {
) )
} }
/** /**
* Ask for places request * Ask for places request
*/ */
@ -118,4 +124,25 @@ open class PokerAnalyticsActivity : AppCompatActivity() {
} }
} }
/**
* Find the current location
*/
fun findCurrentLocation(callback: ((location: Location?) -> Unit)?) {
if (LocationManager(this).databaseContainsLocationsWithCoordinates()) {
if (ContextCompat.checkSelfPermission(this, ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
LocationManager(this).findNearestLocationFromUser(callback)
} else {
askForLocationPermission { granted ->
if (granted) {
LocationManager(this).findNearestLocationFromUser(callback)
} else {
callback?.invoke(null)
}
}
}
} else {
callback?.invoke(null)
}
}
} }

@ -1,7 +1,6 @@
package net.pokeranalytics.android.ui.fragment package net.pokeranalytics.android.ui.fragment
import android.app.Activity import android.app.Activity
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
@ -16,30 +15,40 @@ import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource
import net.pokeranalytics.android.ui.fragment.components.PokerAnalyticsFragment import net.pokeranalytics.android.ui.fragment.components.PokerAnalyticsFragment
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.HeaderRowRepresentable
import net.pokeranalytics.android.ui.view.rowrepresentable.SeparatorRowRepresentable import net.pokeranalytics.android.ui.view.rowrepresentable.SeparatorRowRepresentable
import net.pokeranalytics.android.util.Preferences import net.pokeranalytics.android.util.Preferences
import java.util.* import java.util.*
class CurrenciesFragment : PokerAnalyticsFragment(), StaticRowRepresentableDataSource, RowRepresentableDelegate { class CurrenciesFragment : PokerAnalyticsFragment(), StaticRowRepresentableDataSource, RowRepresentableDelegate {
private val mostUsedCurrencyCodes = arrayListOf("EUR", "USD", "CAD", "GBP", "AUD", "CNY") companion object {
private val systemCurrencies = Currency.getAvailableCurrencies() val rowRepresentation : List<RowRepresentable> by lazy {
val rows = ArrayList<RowRepresentable>()
rows.addAll(mostUsedCurrencies)
rows.add(SeparatorRowRepresentable())
rows.addAll(availableCurrencies)
rows
}
val mostUsedCurrencyCodes = arrayListOf("EUR", "USD", "CAD", "GBP", "AUD", "CNY")
val systemCurrencies = Currency.getAvailableCurrencies()
private val mostUsedCurrencies = this.mostUsedCurrencyCodes.map { code ->
CurrencyRow(
this.systemCurrencies.filter {
it.currencyCode == code
}.first()
)
}
private val availableCurrencies = this.systemCurrencies.filter { private val mostUsedCurrencies = this.mostUsedCurrencyCodes.map { code ->
!mostUsedCurrencyCodes.contains(it.currencyCode) CurrencyRow(
}.sortedBy { this.systemCurrencies.filter {
it.displayName it.currencyCode == code
}.map { }.first()
CurrencyRow(it) )
}
private val availableCurrencies = this.systemCurrencies.filter {
!mostUsedCurrencyCodes.contains(it.currencyCode)
}.sortedBy {
it.displayName
}.map {
CurrencyRow(it)
}
} }
private class CurrencyRow(var currency:Currency) : RowRepresentable { private class CurrencyRow(var currency:Currency) : RowRepresentable {
@ -70,11 +79,7 @@ class CurrenciesFragment : PokerAnalyticsFragment(), StaticRowRepresentableDataS
// StaticRowRepresentableDataSource // StaticRowRepresentableDataSource
override fun adapterRows(): List<RowRepresentable>? { override fun adapterRows(): List<RowRepresentable>? {
val rows = ArrayList<RowRepresentable>() return CurrenciesFragment.rowRepresentation
rows.addAll(mostUsedCurrencies)
rows.add(SeparatorRowRepresentable())
rows.addAll(availableCurrencies)
return rows
} }

@ -70,7 +70,7 @@ class DataListFragment : PokerAnalyticsFragment(), LiveRowRepresentableDataSourc
EditableDataActivity.newInstance( EditableDataActivity.newInstance(
requireContext(), requireContext(),
it.ordinal, it.ordinal,
(row as Manageable).uniqueIdentifier() (row as Manageable).id
) )
} }
} }

@ -4,7 +4,6 @@ import android.app.Activity.RESULT_OK
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.* import android.view.*
import android.widget.Toast
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import io.realm.RealmObject import io.realm.RealmObject
@ -35,7 +34,9 @@ open class EditableDataFragment : PokerAnalyticsFragment(), RowRepresentableDele
private var editableMenu: Menu? = null private var editableMenu: Menu? = null
private var dataType: Int? = null private var dataType: Int? = null
private var primaryKey: String? = null private var primaryKey: String? = null
private var isUpdating = false
var isUpdating = false
var shouldOpenKeyboard = true
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_editable_data, container, false) return inflater.inflate(R.layout.fragment_editable_data, container, false)
@ -64,7 +65,7 @@ open class EditableDataFragment : PokerAnalyticsFragment(), RowRepresentableDele
} }
override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) { override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) {
BottomSheetFragment.create(fragmentManager, row, this, (this.item as RowRepresentableDataSource).editDescriptors(row)) BottomSheetFragment.create(fragmentManager, row, this, getDataSource().editDescriptors(row))
} }
override fun onRowValueChanged(value: Any?, row: RowRepresentable) { override fun onRowValueChanged(value: Any?, row: RowRepresentable) {
@ -91,6 +92,12 @@ open class EditableDataFragment : PokerAnalyticsFragment(), RowRepresentableDele
} }
} }
/**
* Return the data source
*/
open fun getDataSource(): RowRepresentableDataSource {
return this.item as RowRepresentableDataSource
}
/** /**
* Init data * Init data
@ -99,21 +106,22 @@ open class EditableDataFragment : PokerAnalyticsFragment(), RowRepresentableDele
if (this.dataType != null) { if (this.dataType != null) {
val proxyItem: RealmObject? = this.liveDataType.getData(this.getRealm(), primaryKey) val proxyItem: RealmObject? = this.liveDataType.getData(this.getRealm(), primaryKey)
proxyItem?.let { proxyItem?.let {
//TODO: Localize
this.appBar.toolbar.title = "Update ${this.liveDataType.localizedTitle(this.parentActivity).toLowerCase().capitalize()}" this.appBar.toolbar.title = "Update ${this.liveDataType.localizedTitle(this.parentActivity).toLowerCase().capitalize()}"
isUpdating = true isUpdating = true
} ?: run { } ?: run {
//TODO: Localize
this.appBar.toolbar.title = "New ${this.liveDataType.localizedTitle(this.parentActivity).toLowerCase().capitalize()}" this.appBar.toolbar.title = "New ${this.liveDataType.localizedTitle(this.parentActivity).toLowerCase().capitalize()}"
} }
this.item = this.liveDataType.updateOrCreate(this.getRealm(), primaryKey) this.item = this.liveDataType.updateOrCreate(this.getRealm(), primaryKey)
this.rowRepresentableAdapter = RowRepresentableAdapter(
(this.item as RowRepresentableDataSource), val dataSource = getDataSource()
this this.rowRepresentableAdapter = RowRepresentableAdapter(getDataSource(), this)
)
this.recyclerView.adapter = rowRepresentableAdapter this.recyclerView.adapter = rowRepresentableAdapter
// When creating an object, open automatically the keyboard for the first row // When creating an object, open automatically the keyboard for the first row
if (!isUpdating && this.item is RowRepresentableDataSource) { if (!isUpdating && shouldOpenKeyboard) {
val row = (this.item as RowRepresentableDataSource).adapterRows()?.firstOrNull() val row = dataSource.adapterRows()?.firstOrNull()
row?.let { row?.let {
onRowSelected(0, it) onRowSelected(0, it)
} }
@ -132,13 +140,13 @@ open class EditableDataFragment : PokerAnalyticsFragment(), RowRepresentableDele
/** /**
* Save data * Save data
*/ */
private fun saveData() { fun saveData() {
if ((this.item as Savable).isValidForSave()) { if ((this.item as Savable).isValidForSave()) {
this.getRealm().executeTransaction { this.getRealm().executeTransaction {
val item = it.copyToRealmOrUpdate(this.item) val item = it.copyToRealmOrUpdate(this.item)
val uniqueIdentifier = if (item is Identifiable) { val uniqueIdentifier = if (item is Identifiable) {
item.uniqueIdentifier() item.id
} else "" } else ""
finishActivityWithResult(uniqueIdentifier) finishActivityWithResult(uniqueIdentifier)

@ -24,11 +24,11 @@ import net.pokeranalytics.android.ui.view.HistorySessionDiffCallback
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.SmoothScrollLinearLayoutManager import net.pokeranalytics.android.ui.view.SmoothScrollLinearLayoutManager
import net.pokeranalytics.android.ui.view.rowrepresentable.HeaderRowRepresentable
import net.pokeranalytics.android.util.extensions.getMonthAndYear import net.pokeranalytics.android.util.extensions.getMonthAndYear
import net.pokeranalytics.android.util.extensions.isSameDay import net.pokeranalytics.android.util.extensions.isSameDay
import net.pokeranalytics.android.util.extensions.isSameMonth import net.pokeranalytics.android.util.extensions.isSameMonth
import net.pokeranalytics.android.util.extensions.longDate import net.pokeranalytics.android.util.extensions.longDate
import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable
import java.util.* import java.util.*
class HistoryFragment : PokerAnalyticsFragment(), LiveRowRepresentableDataSource, RowRepresentableDelegate { class HistoryFragment : PokerAnalyticsFragment(), LiveRowRepresentableDataSource, RowRepresentableDelegate {
@ -68,7 +68,7 @@ class HistoryFragment : PokerAnalyticsFragment(), LiveRowRepresentableDataSource
//rows.clear() //rows.clear()
//endedSessions.addAll(getRealm().copyFromRealm(realmSessions)) //endedSessions.addAll(getRealm().copyFromRealm(realmSessions))
createSessionsHeaders() // createSessionsHeaders()
} }
/** /**
@ -128,7 +128,7 @@ class HistoryFragment : PokerAnalyticsFragment(), LiveRowRepresentableDataSource
if (groupedByDay) { if (groupedByDay) {
if (!calendar.isSameDay(currentCalendar) || index == 0) { if (!calendar.isSameDay(currentCalendar) || index == 0) {
calendar.time = currentCalendar.time calendar.time = currentCalendar.time
val header = HeaderRowRepresentable( val header = CustomizableRowRepresentable(
customViewType = RowViewType.HEADER_TITLE, customViewType = RowViewType.HEADER_TITLE,
title = session.creationDate.longDate() title = session.creationDate.longDate()
) )
@ -137,7 +137,7 @@ class HistoryFragment : PokerAnalyticsFragment(), LiveRowRepresentableDataSource
} else { } else {
if (!calendar.isSameMonth(currentCalendar) || index == 0) { if (!calendar.isSameMonth(currentCalendar) || index == 0) {
calendar.time = currentCalendar.time calendar.time = currentCalendar.time
val header = HeaderRowRepresentable( val header = CustomizableRowRepresentable(
customViewType = RowViewType.HEADER_TITLE, customViewType = RowViewType.HEADER_TITLE,
title = session.creationDate.getMonthAndYear() title = session.creationDate.getMonthAndYear()
) )
@ -175,6 +175,6 @@ class HistoryFragment : PokerAnalyticsFragment(), LiveRowRepresentableDataSource
} }
override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) { override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) {
SessionActivity.newInstance(requireContext(), sessionId = (row as Manageable).uniqueIdentifier()) SessionActivity.newInstance(requireContext(), sessionId = (row as Manageable).id)
} }
} }

@ -1,36 +1,209 @@
package net.pokeranalytics.android.ui.fragment package net.pokeranalytics.android.ui.fragment
import net.pokeranalytics.android.exceptions.TypeException import android.os.Bundle
import android.view.View
import com.google.android.libraries.places.api.model.PlaceLikelihood
import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.realm.Location import net.pokeranalytics.android.model.realm.Location
import net.pokeranalytics.android.ui.helpers.PlacePickerManager import net.pokeranalytics.android.ui.adapter.RowRepresentableDataSource
import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource
import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowRepresentableEditDescriptor
import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable
import net.pokeranalytics.android.ui.view.rowrepresentable.LocationRow import net.pokeranalytics.android.ui.view.rowrepresentable.LocationRow
import net.pokeranalytics.android.ui.view.rowrepresentable.SimpleRow
import timber.log.Timber
/** /**
* Custom EditableDataFragment to manage the LOCATE_ME case * Custom EditableDataFragment to manage the LOCATE_ME case
*/ */
class LocationDataFragment: EditableDataFragment() { class LocationDataFragment : EditableDataFragment(), StaticRowRepresentableDataSource {
// Return the item as a Location object
private val location: Location
get() {
return this.item as Location
}
// Loader boolean
private var isLookingForPlaces: Boolean = false
private var placesForRows: HashMap<CustomizableRowRepresentable, PlaceLikelihood> = HashMap()
private var rowPlaces: ArrayList<CustomizableRowRepresentable> = ArrayList()
private var locationActivated = false
val rows = ArrayList<RowRepresentable>()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
shouldOpenKeyboard = false
locationActivated = parentActivity.hasLocationPermissionGranted()
if (isUpdating) {
// If we update a location, we set the switch to the correct value
locationActivated = location.latitude != null && location.longitude != null
} else if (locationActivated) {
// If we create a new location, we try to locate the user by default
isLookingForPlaces = true
getSuggestionsPlaces()
}
updateAdapterUI()
}
override fun getDataSource(): RowRepresentableDataSource {
return this
}
override fun adapterRows(): List<RowRepresentable>? {
return rows
}
override fun stringForRow(row: RowRepresentable): String {
return when (row) {
SimpleRow.NAME -> location.name
else -> return super.stringForRow(row)
}
}
override fun boolForRow(row: RowRepresentable): Boolean {
return when (row) {
LocationRow.LOCATION_PERMISSION_SWITCH -> return locationActivated
else -> super.boolForRow(row)
}
}
override fun editDescriptors(row: RowRepresentable): ArrayList<RowRepresentableEditDescriptor> {
val data = java.util.ArrayList<RowRepresentableEditDescriptor>()
when (row) {
SimpleRow.NAME -> data.add(
RowRepresentableEditDescriptor(
location.name,
SimpleRow.NAME.resId
)
)
}
return data
}
override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) { override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) {
when(row) { // If we click on a location row, save the location (and finish activity)
LocationRow.LOCATE_ME -> { placesForRows[row]?.place?.let { place ->
if (item is Location) { location.setPlace(place)
(item as Location).isLookingForPlaces = true saveData()
PlacePickerManager.create(parentActivity, row, this) return
rowRepresentableAdapter.refreshRow(row)
} else {
throw TypeException("Need to manage LocationRow.LOCATE_ME for ${item::class.java}")
}
}
else -> super.onRowSelected(position, row, fromAction)
} }
super.onRowSelected(position, row, fromAction)
} }
override fun onRowValueChanged(value: Any?, row: RowRepresentable) { override fun onRowValueChanged(value: Any?, row: RowRepresentable) {
super.onRowValueChanged(value, row)
when (row) { when (row) {
LocationRow.LOCATE_ME -> { LocationRow.LOCATION_PERMISSION_SWITCH -> {
rowRepresentableAdapter.notifyDataSetChanged() if (value is Boolean && value != locationActivated) {
rowPlaces.clear()
locationActivated = value
isLookingForPlaces = value
if (value) {
updateAdapterUI()
getSuggestionsPlaces()
} else {
clearCurrentLocation()
updateAdapterUI()
}
}
}
else -> super.onRowValueChanged(value, row)
}
}
/**
* Clear current location
*/
private fun clearCurrentLocation() {
location.latitude = null
location.longitude = null
}
/**
* Refresh rows
*/
private fun refreshRows() {
rows.clear()
rows.add(SimpleRow.NAME)
rows.add(LocationRow.LOCATION_PERMISSION_SWITCH)
if (isLookingForPlaces) {
rows.add(LocationRow.LOCATION_LOADER)
}
if (locationActivated && rowPlaces.size > 0) {
rows.add(CustomizableRowRepresentable(resId = R.string.suggestions))
for (row in rowPlaces) {
rows.add(row)
}
}
}
/**
* Update UI adapter
*/
private fun updateAdapterUI() {
val currentRowsSize = rows.size
refreshRows()
val newRowsSize = rows.size
if (currentRowsSize < newRowsSize) {
rowRepresentableAdapter.notifyItemRangeInserted(currentRowsSize, newRowsSize - currentRowsSize)
} else {
rowRepresentableAdapter.notifyItemRangeRemoved(newRowsSize, currentRowsSize - newRowsSize)
}
}
/**
* Return the places around the user
*/
private fun getSuggestionsPlaces() {
val maxResults = 5
parentActivity.askForPlacesRequest { success, places ->
if (success) {
// Try to get the location of the user
parentActivity.findCurrentLocation {currentLocation ->
currentLocation?.let {
Timber.d("Current location: ${it.latitude}, ${it.longitude}")
location.latitude = currentLocation.latitude
location.longitude = currentLocation.longitude
}
}
}
if (success && places.size > 0) {
rowPlaces.clear()
placesForRows.clear()
for ((index, place) in places.withIndex()) {
if (index < maxResults) {
val row = CustomizableRowRepresentable(customViewType = RowViewType.LOCATION_TITLE, title = place.place.name, isSelectable = true)
rowPlaces.add(row)
placesForRows[row] = place
}
}
locationActivated = true
isLookingForPlaces = false
updateAdapterUI()
} else {
isLookingForPlaces = false
locationActivated = false
updateAdapterUI()
} }
} }
} }

@ -215,7 +215,6 @@ class SessionFragment : PokerAnalyticsFragment(), RowRepresentableDelegate {
* Update adapter UI * Update adapter UI
*/ */
private fun updateAdapterUI(scrollToTop: Boolean) { private fun updateAdapterUI(scrollToTop: Boolean) {
currentSession.adapterRows()?.let { currentSession.adapterRows()?.let {
val diffResult = DiffUtil.calculateDiff(RowRepresentableDiffCallback(it, oldRows)) val diffResult = DiffUtil.calculateDiff(RowRepresentableDiffCallback(it, oldRows))
sessionAdapter.updateRows(diffResult) sessionAdapter.updateRows(diffResult)
@ -225,7 +224,6 @@ class SessionFragment : PokerAnalyticsFragment(), RowRepresentableDelegate {
if (scrollToTop) { if (scrollToTop) {
recyclerView.smoothScrollToPosition(0) recyclerView.smoothScrollToPosition(0)
} }
} }
} }

@ -43,6 +43,12 @@ class SettingsFragment : PokerAnalyticsFragment(), RowRepresentableDelegate, Sta
return fragment return fragment
} }
val rowRepresentation : List<RowRepresentable> by lazy {
val rows = ArrayList<RowRepresentable>()
rows.addAll(SettingRow.getRows())
rows
}
val REQUEST_CODE_CURRENCY : Int = 0 val REQUEST_CODE_CURRENCY : Int = 0
} }
@ -74,9 +80,7 @@ class SettingsFragment : PokerAnalyticsFragment(), RowRepresentableDelegate, Sta
} }
override fun adapterRows(): List<RowRepresentable>? { override fun adapterRows(): List<RowRepresentable>? {
val rows = ArrayList<RowRepresentable>() return SettingsFragment.rowRepresentation
rows.addAll(SettingRow.getRows())
return rows
} }
override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) { override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) {

@ -5,11 +5,9 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import io.realm.Realm
import kotlinx.android.synthetic.main.fragment_stats.* import kotlinx.android.synthetic.main.fragment_stats.*
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
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.StatRepresentable import net.pokeranalytics.android.model.StatRepresentable
@ -19,7 +17,7 @@ import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter
import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource
import net.pokeranalytics.android.ui.fragment.components.SessionObserverFragment import net.pokeranalytics.android.ui.fragment.components.SessionObserverFragment
import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.rowrepresentable.HeaderRowRepresentable import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable
import net.pokeranalytics.android.util.NULL_TEXT import net.pokeranalytics.android.util.NULL_TEXT
import timber.log.Timber import timber.log.Timber
import java.util.* import java.util.*
@ -111,26 +109,36 @@ class StatsFragment : SessionObserverFragment(), StaticRowRepresentableDataSourc
private fun launchStatComputation() { private fun launchStatComputation() {
val s = Date() GlobalScope.launch(coroutineContext) {
Timber.d(">>> start...")
GlobalScope.launch(context = this.coroutineContext) { var results = listOf<ComputedResults>()
val results = compute() val test = GlobalScope.async {
showResults(results) val s = Date()
} Timber.d(">>> start...")
results = createSessionGroupsAndStartCompute()
val e = Date()
val duration = (e.time - s.time) / 1000.0
Timber.d(">>> ended in ${duration} seconds")
val e = Date() }
val duration = (e.time - s.time) / 1000.0 test.await()
Timber.d(">>> ended in ${duration} seconds") showResults(results)
}
} }
private fun compute() : List<ComputedResults> { suspend private fun createSessionGroupsAndStartCompute() : List<ComputedResults> {
val cgSessions = mutableListOf<Session>() val cgSessions = mutableListOf<Session>()
val tSessions = mutableListOf<Session>() val tSessions = mutableListOf<Session>()
super.endedSessions.forEach { session -> val realm = Realm.getDefaultInstance()
val allSessions = realm.where(Session::class.java).isNotNull("endDate").findAll()
Timber.d(">>>>> number of sessions to compute = ${allSessions.size}")
allSessions.forEach { session ->
if (session.isCashGame()) { if (session.isCashGame()) {
cgSessions.add(session) cgSessions.add(session)
} else { } else {
@ -139,12 +147,14 @@ class StatsFragment : SessionObserverFragment(), StaticRowRepresentableDataSourc
} }
val allStats: List<Stat> = listOf(Stat.NETRESULT, Stat.HOURLY_RATE, Stat.AVERAGE, Stat.NUMBER_OF_SETS, Stat.AVERAGE_DURATION, Stat.DURATION) val allStats: List<Stat> = listOf(Stat.NETRESULT, Stat.HOURLY_RATE, Stat.AVERAGE, Stat.NUMBER_OF_SETS, Stat.AVERAGE_DURATION, Stat.DURATION)
val allSessionGroup = SessionGroup(getString(R.string.all), super.endedSessions, allStats) val allSessionGroup = SessionGroup(getString(R.string.all), allSessions, allStats)
val cgStats: List<Stat> = listOf(Stat.NETRESULT, Stat.HOURLY_RATE, Stat.NET_BB_PER_100_HANDS, Stat.HOURLY_RATE_BB, Stat.AVERAGE, Stat.STANDARD_DEVIATION_HOURLY, Stat.WIN_RATIO, Stat.NUMBER_OF_GAMES, Stat.AVERAGE_BUYIN) val cgStats: List<Stat> = listOf(Stat.NETRESULT, Stat.HOURLY_RATE, Stat.NET_BB_PER_100_HANDS, Stat.HOURLY_RATE_BB, Stat.AVERAGE, Stat.STANDARD_DEVIATION_HOURLY, Stat.WIN_RATIO, Stat.NUMBER_OF_GAMES, Stat.AVERAGE_BUYIN)
val cgSessionGroup = SessionGroup(getString(R.string.cash_game), cgSessions, cgStats) val cgSessionGroup = SessionGroup(getString(R.string.cash_game), cgSessions, cgStats)
val tStats: List<Stat> = listOf(Stat.NETRESULT, Stat.HOURLY_RATE, Stat.ROI, Stat.WIN_RATIO, Stat.NUMBER_OF_GAMES, Stat.AVERAGE_BUYIN) val tStats: List<Stat> = listOf(Stat.NETRESULT, Stat.HOURLY_RATE, Stat.ROI, Stat.WIN_RATIO, Stat.NUMBER_OF_GAMES, Stat.AVERAGE_BUYIN)
val tSessionGroup = SessionGroup(getString(R.string.tournament), tSessions, tStats) val tSessionGroup = SessionGroup(getString(R.string.tournament), tSessions, tStats)
Timber.d(">>>>> Start computations...")
return Calculator.computeGroups(listOf(allSessionGroup, cgSessionGroup, tSessionGroup), Calculator.Options()) return Calculator.computeGroups(listOf(allSessionGroup, cgSessionGroup, tSessionGroup), Calculator.Options())
} }
@ -166,10 +176,10 @@ class StatsFragment : SessionObserverFragment(), StaticRowRepresentableDataSourc
val rows: ArrayList<RowRepresentable> = ArrayList() val rows: ArrayList<RowRepresentable> = ArrayList()
results.forEach { results -> results.forEach { result ->
rows.add(HeaderRowRepresentable(title = results.group.name)) rows.add(CustomizableRowRepresentable(title = result.group.name))
results.group.stats?.forEach { stat -> result.group.stats?.forEach { stat ->
rows.add(StatRepresentable(stat, results.computedStat(stat))) rows.add(StatRepresentable(stat, result.computedStat(stat)))
} }
} }

@ -3,7 +3,7 @@ package net.pokeranalytics.android.ui.view
import androidx.annotation.Nullable import androidx.annotation.Nullable
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import net.pokeranalytics.android.model.realm.Session import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.ui.view.rowrepresentable.HeaderRowRepresentable import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable
class HistorySessionDiffCallback(var newRows: List<RowRepresentable>, var oldRows: List<RowRepresentable>) : class HistorySessionDiffCallback(var newRows: List<RowRepresentable>, var oldRows: List<RowRepresentable>) :
DiffUtil.Callback() { DiffUtil.Callback() {
@ -22,9 +22,9 @@ class HistorySessionDiffCallback(var newRows: List<RowRepresentable>, var oldRow
val session1 = oldRows[oldItemPosition] as Session val session1 = oldRows[oldItemPosition] as Session
val session2 = newRows[newItemPosition] as Session val session2 = newRows[newItemPosition] as Session
return session1.id == session2.id return session1.id == session2.id
} else if (oldRows[oldItemPosition] is HeaderRowRepresentable && newRows[newItemPosition] is HeaderRowRepresentable) { } else if (oldRows[oldItemPosition] is CustomizableRowRepresentable && newRows[newItemPosition] is CustomizableRowRepresentable) {
val header1 = oldRows[oldItemPosition] as HeaderRowRepresentable val header1 = oldRows[oldItemPosition] as CustomizableRowRepresentable
val header2 = newRows[newItemPosition] as HeaderRowRepresentable val header2 = newRows[newItemPosition] as CustomizableRowRepresentable
return header1.title == header2.title return header1.title == header2.title
} }
@ -40,9 +40,9 @@ class HistorySessionDiffCallback(var newRows: List<RowRepresentable>, var oldRow
return false //session1.id == session2.id return false //session1.id == session2.id
} else if (oldRows[oldItemPosition] is HeaderRowRepresentable && newRows[newItemPosition] is HeaderRowRepresentable) { } else if (oldRows[oldItemPosition] is CustomizableRowRepresentable && newRows[newItemPosition] is CustomizableRowRepresentable) {
val header1 = oldRows[oldItemPosition] as HeaderRowRepresentable val header1 = oldRows[oldItemPosition] as CustomizableRowRepresentable
val header2 = newRows[newItemPosition] as HeaderRowRepresentable val header2 = newRows[newItemPosition] as CustomizableRowRepresentable
return header1.title == header2.title return header1.title == header2.title
} }

@ -14,7 +14,7 @@ import kotlinx.android.synthetic.main.row_history_session.view.*
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.realm.Session import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter
import net.pokeranalytics.android.ui.view.rowrepresentable.HeaderRowRepresentable import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable
/** /**
* An interface used to factor the configuration of RecyclerView.ViewHolder * An interface used to factor the configuration of RecyclerView.ViewHolder
@ -35,6 +35,7 @@ enum class RowViewType(private var layoutRes: Int) {
HEADER_TITLE_VALUE(R.layout.row_header_title_value), HEADER_TITLE_VALUE(R.layout.row_header_title_value),
HEADER_TITLE_AMOUNT(R.layout.row_header_title_amount), HEADER_TITLE_AMOUNT(R.layout.row_header_title_amount),
HEADER_TITLE_AMOUNT_BIG(R.layout.row_header_title_amount_big), HEADER_TITLE_AMOUNT_BIG(R.layout.row_header_title_amount_big),
LOCATION_TITLE(R.layout.row_title),
// Row // Row
TITLE(R.layout.row_title), TITLE(R.layout.row_title),
@ -46,6 +47,7 @@ enum class RowViewType(private var layoutRes: Int) {
DATA(R.layout.row_title), DATA(R.layout.row_title),
BOTTOM_SHEET_DATA(R.layout.row_bottom_sheet_title), BOTTOM_SHEET_DATA(R.layout.row_bottom_sheet_title),
TITLE_CHECK(R.layout.row_title_check), TITLE_CHECK(R.layout.row_title_check),
LOADER(R.layout.row_loader),
// Custom row // Custom row
ROW_SESSION(R.layout.row_history_session), ROW_SESSION(R.layout.row_history_session),
@ -66,10 +68,10 @@ enum class RowViewType(private var layoutRes: Int) {
return when (this) { return when (this) {
// Header Row View Holder // Header Row View Holder
HEADER_TITLE, HEADER_TITLE_VALUE, HEADER_TITLE_AMOUNT, HEADER_TITLE_AMOUNT_BIG -> HeaderViewHolder(layout) HEADER_TITLE, HEADER_TITLE_VALUE, HEADER_TITLE_AMOUNT, HEADER_TITLE_AMOUNT_BIG, LOCATION_TITLE -> TitleViewHolder(layout)
// Row View Holder // Row View Holder
TITLE, TITLE_ARROW, TITLE_VALUE, TITLE_VALUE_ARROW, TITLE_GRID, TITLE_SWITCH, TITLE_CHECK, DATA, BOTTOM_SHEET_DATA -> RowViewHolder( TITLE, TITLE_ARROW, TITLE_VALUE, TITLE_VALUE_ARROW, TITLE_GRID, TITLE_SWITCH, TITLE_CHECK, DATA, BOTTOM_SHEET_DATA, LOADER -> RowViewHolder(
layout layout
) )
@ -96,10 +98,10 @@ enum class RowViewType(private var layoutRes: Int) {
/** /**
* Display a header * Display a header
*/ */
inner class HeaderViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), BindableHolder { inner class TitleViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), BindableHolder {
override fun bind(position: Int, row: RowRepresentable, adapter: RowRepresentableAdapter) { override fun bind(position: Int, row: RowRepresentable, adapter: RowRepresentableAdapter) {
if (row is HeaderRowRepresentable) { if (row is CustomizableRowRepresentable) {
// Title // Title
itemView.findViewById<AppCompatTextView>(R.id.title)?.let { itemView.findViewById<AppCompatTextView>(R.id.title)?.let {
@ -116,6 +118,18 @@ enum class RowViewType(private var layoutRes: Int) {
it.text = row.value it.text = row.value
} }
} }
// Listener
row.isSelectable?.let { isSelectable ->
if (isSelectable) {
val listener = View.OnClickListener {
adapter.delegate?.onRowSelected(position, row)
}
itemView.findViewById<View?>(R.id.container)?.let {
it.setOnClickListener(listener)
}
}
}
} }
} }
} }
@ -199,7 +213,7 @@ enum class RowViewType(private var layoutRes: Int) {
itemView.findViewById<AppCompatTextView?>(R.id.title)?.text = row.localizedTitle(itemView.context) itemView.findViewById<AppCompatTextView?>(R.id.title)?.text = row.localizedTitle(itemView.context)
// Value // Value
itemView.findViewById<AppCompatTextView?>(R.id.value)?.let {view -> itemView.findViewById<AppCompatTextView?>(R.id.value)?.let { view ->
adapter.dataSource.contentDescriptorForRow(row)?.textFormat?.let { adapter.dataSource.contentDescriptorForRow(row)?.textFormat?.let {
view.text = it.text view.text = it.text
view.setTextColor(it.getColor(itemView.context)) view.setTextColor(it.getColor(itemView.context))

@ -6,14 +6,15 @@ import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType import net.pokeranalytics.android.ui.view.RowViewType
/** /**
* A class to display headers as row representable * A class to display a title (and a value) as a Row Representable object
*/ */
class HeaderRowRepresentable( class CustomizableRowRepresentable(
var customViewType: RowViewType? = RowViewType.HEADER_TITLE, var customViewType: RowViewType? = RowViewType.HEADER_TITLE,
override var resId: Int? = null, override var resId: Int? = null,
var title: String? = null, var title: String? = null,
var value: String? = null, var value: String? = null,
var computedStat: ComputedStat? = null var computedStat: ComputedStat? = null,
var isSelectable: Boolean? = false
) : RowRepresentable { ) : RowRepresentable {
override fun localizedTitle(context: Context): String { override fun localizedTitle(context: Context): String {
@ -27,8 +28,6 @@ class HeaderRowRepresentable(
return "LOCALISATION NOT FOUND" return "LOCALISATION NOT FOUND"
} }
override val viewType: Int = customViewType?.ordinal ?: RowViewType.HEADER_TITLE.ordinal override val viewType: Int = customViewType?.ordinal ?: RowViewType.HEADER_TITLE.ordinal
} }

@ -7,30 +7,30 @@ import net.pokeranalytics.android.ui.view.RowViewType
enum class LocationRow : RowRepresentable { enum class LocationRow : RowRepresentable {
ADDRESS, LOCATION_PERMISSION_SWITCH,
LOCATE_ME; LOCATION_LOADER;
override val resId: Int? override val resId: Int?
get() { get() {
return when (this) { return when (this) {
ADDRESS -> R.string.address LOCATION_PERMISSION_SWITCH -> R.string.geo_locate
LOCATE_ME -> R.string.geo_locate LOCATION_LOADER -> null
} }
} }
override val viewType: Int override val viewType: Int
get() { get() {
return when (this) { return when (this) {
ADDRESS -> RowViewType.TITLE_VALUE.ordinal LOCATION_PERMISSION_SWITCH -> RowViewType.TITLE_SWITCH.ordinal
LOCATE_ME -> RowViewType.ROW_BUTTON.ordinal LOCATION_LOADER -> RowViewType.LOADER.ordinal
} }
} }
override val bottomSheetType: BottomSheetType override val bottomSheetType: BottomSheetType
get() { get() {
return when (this) { return when (this) {
ADDRESS -> BottomSheetType.EDIT_TEXT LOCATION_PERMISSION_SWITCH -> BottomSheetType.NONE
LOCATE_ME -> BottomSheetType.NONE LOCATION_LOADER -> BottomSheetType.NONE
} }
} }

@ -40,24 +40,24 @@ enum class SettingRow : RowRepresentable {
fun getRows(): ArrayList<RowRepresentable> { fun getRows(): ArrayList<RowRepresentable> {
val rows = ArrayList<RowRepresentable>() val rows = ArrayList<RowRepresentable>()
rows.add(HeaderRowRepresentable(customViewType = RowViewType.HEADER_TITLE, resId = R.string.information)) rows.add(CustomizableRowRepresentable(customViewType = RowViewType.HEADER_TITLE, resId = R.string.information))
rows.addAll(arrayListOf(VERSION, RATE_APP, CONTACT_US, BUG_REPORT)) rows.addAll(arrayListOf(VERSION, RATE_APP, CONTACT_US, BUG_REPORT))
rows.add(HeaderRowRepresentable(customViewType = RowViewType.HEADER_TITLE, resId = R.string.follow_us)) rows.add(CustomizableRowRepresentable(customViewType = RowViewType.HEADER_TITLE, resId = R.string.follow_us))
rows.addAll(arrayListOf(FOLLOW_US)) rows.addAll(arrayListOf(FOLLOW_US))
rows.add(HeaderRowRepresentable(customViewType = RowViewType.HEADER_TITLE, resId = R.string.preferences)) rows.add(CustomizableRowRepresentable(customViewType = RowViewType.HEADER_TITLE, resId = R.string.preferences))
rows.addAll(arrayListOf(CURRENCY)) rows.addAll(arrayListOf(CURRENCY))
rows.add( rows.add(
HeaderRowRepresentable( CustomizableRowRepresentable(
customViewType = RowViewType.HEADER_TITLE, customViewType = RowViewType.HEADER_TITLE,
resId = R.string.data_management resId = R.string.data_management
) )
) )
rows.addAll(arrayListOf(BANKROLL, GAME, LOCATION, TOURNAMENT_NAME, TOURNAMENT_FEATURE)) rows.addAll(arrayListOf(BANKROLL, GAME, LOCATION, TOURNAMENT_NAME, TOURNAMENT_FEATURE))
rows.add(HeaderRowRepresentable(customViewType = RowViewType.HEADER_TITLE, resId = R.string.terms)) rows.add(CustomizableRowRepresentable(customViewType = RowViewType.HEADER_TITLE, resId = R.string.terms))
rows.addAll(arrayListOf(PRIVACY_POLICY, TERMS_OF_USE, GDPR)) rows.addAll(arrayListOf(PRIVACY_POLICY, TERMS_OF_USE, GDPR))
return rows return rows

@ -44,6 +44,8 @@ class LocationManager(private var context: Context) {
// Call findCurrentPlace and handle the response (first check that the user has granted permission). // Call findCurrentPlace and handle the response (first check that the user has granted permission).
if (ContextCompat.checkSelfPermission(context, ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { if (ContextCompat.checkSelfPermission(context, ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
val placeResponse = placesClient.findCurrentPlace(request) val placeResponse = placesClient.findCurrentPlace(request)
placeResponse.addOnCompleteListener { task -> placeResponse.addOnCompleteListener { task ->
val places = ArrayList<PlaceLikelihood>() val places = ArrayList<PlaceLikelihood>()
if (task.isSuccessful) { if (task.isSuccessful) {
@ -142,7 +144,31 @@ class LocationManager(private var context: Context) {
// If we don't have the permission, return null // If we don't have the permission, return null
callback?.invoke(null) callback?.invoke(null)
} }
}
/**
* Return the current location of the user
*/
fun findCurrentLocation(callback: ((location: android.location.Location?) -> Unit)?) {
val fusedLocationClient: FusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(context)
if (ContextCompat.checkSelfPermission(context, ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
fusedLocationClient.lastLocation.addOnSuccessListener { location: android.location.Location? ->
// Got last known location. In some rare situations this can be null.
location?.let { currentLocation ->
callback?.invoke(currentLocation)
} ?: run {
// If the current location is null, return null
callback?.invoke(null)
}
}.addOnCanceledListener {
// If there was a problem during the call to last location, return null
callback?.invoke(null)
}
} else {
callback?.invoke(null)
}
} }
} }

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="56dp"
android:background="?selectableItemBackground">
<androidx.core.widget.ContentLoadingProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guidelineStart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_begin="0dp" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guidelineEnd"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_end="0dp" />
</androidx.constraintlayout.widget.ConstraintLayout>

@ -19,6 +19,7 @@
<string name="hands_played">Hands played</string> <string name="hands_played">Hands played</string>
<string name="address">Address</string> <string name="address">Address</string>
<string name="suggestions">Suggestions</string>
<string name="data_deleted" formatted="false">%s deleted</string> <string name="data_deleted" formatted="false">%s deleted</string>
<string name="end_date_not_possible">The end date should be after the start date</string> <string name="end_date_not_possible">The end date should be after the start date</string>

Loading…
Cancel
Save