merge conflicts fix

feature/top10
Razmig Sarkissian 7 years ago
commit 40ac353039
  1. 4
      app/src/main/java/net/pokeranalytics/android/exceptions/Exceptions.kt
  2. 58
      app/src/main/java/net/pokeranalytics/android/model/realm/Location.kt
  3. 21
      app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt
  4. 29
      app/src/main/java/net/pokeranalytics/android/ui/activity/components/PokerAnalyticsActivity.kt
  5. 2
      app/src/main/java/net/pokeranalytics/android/ui/fragment/CurrenciesFragment.kt
  6. 28
      app/src/main/java/net/pokeranalytics/android/ui/fragment/EditableDataFragment.kt
  7. 6
      app/src/main/java/net/pokeranalytics/android/ui/fragment/HistoryFragment.kt
  8. 203
      app/src/main/java/net/pokeranalytics/android/ui/fragment/LocationDataFragment.kt
  9. 1
      app/src/main/java/net/pokeranalytics/android/ui/fragment/SessionFragment.kt
  10. 4
      app/src/main/java/net/pokeranalytics/android/ui/fragment/StatsFragment.kt
  11. 14
      app/src/main/java/net/pokeranalytics/android/ui/view/HistorySessionDiffCallback.kt
  12. 24
      app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt
  13. 9
      app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/CustomizableRowRepresentable.kt
  14. 16
      app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/LocationRow.kt
  15. 10
      app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/SettingRow.kt
  16. 26
      app/src/main/java/net/pokeranalytics/android/util/LocationManager.kt
  17. 37
      app/src/main/res/layout/row_loader.xml
  18. 1
      app/src/main/res/values/strings.xml

@ -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) {
}

@ -2,20 +2,15 @@ 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() var id = UUID.randomUUID().toString()
@ -32,9 +27,6 @@ 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
} }
@ -43,58 +35,10 @@ open class Location : RealmObject(), Manageable, StaticRowRepresentableDataSourc
return this.id 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,11 +28,10 @@ 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.*
import timber.log.Timber
import java.util.* import java.util.*
import java.util.Currency import java.util.Currency
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
@ -480,14 +479,14 @@ open class Session : RealmObject(), SessionInterface, Manageable, StaticRowRepre
@Ignore @Ignore
private var rowRepresentationForCurrentState : List<RowRepresentable> = this.updatedRowRepresentationForCurrentState() private var rowRepresentationForCurrentState : List<RowRepresentable> = this.updatedRowRepresentationForCurrentState()
fun updatedRowRepresentationForCurrentState(): List<RowRepresentable> { 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)
@ -497,7 +496,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)
@ -507,33 +506,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 -> {
@ -545,7 +540,7 @@ open class Session : RealmObject(), SessionInterface, Manageable, StaticRowRepre
return rows return rows
} }
fun updateRowRepresentation() { private fun updateRowRepresentation() {
this.rowRepresentationForCurrentState = this.updatedRowRepresentationForCurrentState() this.rowRepresentationForCurrentState = this.updatedRowRepresentationForCurrentState()
} }

@ -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,7 +15,6 @@ 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.*

@ -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,7 +140,7 @@ 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)

@ -24,7 +24,7 @@ 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.ui.view.rowrepresentable.CustomizableRowRepresentable
import net.pokeranalytics.android.util.getMonthAndYear import net.pokeranalytics.android.util.getMonthAndYear
import net.pokeranalytics.android.util.isSameDay import net.pokeranalytics.android.util.isSameDay
import net.pokeranalytics.android.util.isSameMonth import net.pokeranalytics.android.util.isSameMonth
@ -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()
) )

@ -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 {
override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) { // 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) { when (row) {
LocationRow.LOCATE_ME -> { SimpleRow.NAME -> data.add(
if (item is Location) { RowRepresentableEditDescriptor(
(item as Location).isLookingForPlaces = true location.name,
PlacePickerManager.create(parentActivity, row, this) SimpleRow.NAME.resId
rowRepresentableAdapter.refreshRow(row) )
} else { )
throw TypeException("Need to manage LocationRow.LOCATE_ME for ${item::class.java}")
} }
return data
} }
else -> super.onRowSelected(position, row, fromAction)
override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) {
// If we click on a location row, save the location (and finish activity)
placesForRows[row]?.place?.let { place ->
location.setPlace(place)
saveData()
return
} }
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()
} }
} }
} }

@ -224,7 +224,6 @@ class SessionFragment : PokerAnalyticsFragment(), RowRepresentableDelegate {
if (scrollToTop) { if (scrollToTop) {
recyclerView.smoothScrollToPosition(0) recyclerView.smoothScrollToPosition(0)
} }
} }
} }

@ -16,7 +16,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
class StatsFragment : SessionObserverFragment(), StaticRowRepresentableDataSource { class StatsFragment : SessionObserverFragment(), StaticRowRepresentableDataSource {
@ -145,7 +145,7 @@ class StatsFragment : SessionObserverFragment(), StaticRowRepresentableDataSourc
val rows: ArrayList<RowRepresentable> = ArrayList() val rows: ArrayList<RowRepresentable> = ArrayList()
results.forEach { results -> results.forEach { results ->
rows.add(HeaderRowRepresentable(title = results.group.name)) rows.add(CustomizableRowRepresentable(title = results.group.name))
results.group.stats?.forEach { stat -> results.group.stats?.forEach { stat ->
rows.add(StatRepresentable(stat, results.computedStat(stat))) rows.add(StatRepresentable(stat, results.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)
}
}
}
} }
} }
} }

@ -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