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

feature/top10
Laurent 7 years ago
commit 914f320126
  1. 7
      app/build.gradle
  2. 3
      app/src/main/AndroidManifest.xml
  3. 3
      app/src/main/java/net/pokeranalytics/android/PokerAnalyticsApplication.kt
  4. 26
      app/src/main/java/net/pokeranalytics/android/model/Limit.kt
  5. 6
      app/src/main/java/net/pokeranalytics/android/model/LiveData.kt
  6. 18
      app/src/main/java/net/pokeranalytics/android/model/realm/Bankroll.kt
  7. 18
      app/src/main/java/net/pokeranalytics/android/model/realm/Game.kt
  8. 11
      app/src/main/java/net/pokeranalytics/android/model/realm/Location.kt
  9. 184
      app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt
  10. 11
      app/src/main/java/net/pokeranalytics/android/model/realm/TournamentFeature.kt
  11. 11
      app/src/main/java/net/pokeranalytics/android/model/realm/TransactionType.kt
  12. 3
      app/src/main/java/net/pokeranalytics/android/ui/activity/HomeActivity.kt
  13. 1
      app/src/main/java/net/pokeranalytics/android/ui/adapter/LimitTypesAdapter.kt
  14. 35
      app/src/main/java/net/pokeranalytics/android/ui/adapter/components/LiveDataAdapter.kt
  15. 72
      app/src/main/java/net/pokeranalytics/android/ui/adapter/components/RowRepresentableAdapter.kt
  16. 32
      app/src/main/java/net/pokeranalytics/android/ui/fragment/DataListFragment.kt
  17. 114
      app/src/main/java/net/pokeranalytics/android/ui/fragment/EditableDataFragment.kt
  18. 65
      app/src/main/java/net/pokeranalytics/android/ui/fragment/HistoryFragment.kt
  19. 58
      app/src/main/java/net/pokeranalytics/android/ui/fragment/SessionFragment.kt
  20. 45
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/bottomsheet/BottomSheetListFragment.kt
  21. 81
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/bottomsheet/BottomSheetListGameFragment.kt
  22. 121
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/bottomsheet/BottomSheetTableSizeGridFragment.kt
  23. 8
      app/src/main/java/net/pokeranalytics/android/ui/view/RowRepresentable.kt
  24. 6
      app/src/main/java/net/pokeranalytics/android/ui/view/RowRepresentableDiffCallback.kt
  25. 68
      app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt
  26. 15
      app/src/main/java/net/pokeranalytics/android/ui/view/SessionRowView.kt
  27. 17
      app/src/main/java/net/pokeranalytics/android/util/Extensions.kt
  28. 21
      app/src/main/java/net/pokeranalytics/android/util/data/LiveRealmData.kt
  29. 11
      app/src/main/java/net/pokeranalytics/android/util/data/Realm+Dao.kt
  30. 5
      app/src/main/res/color/chips_background_states.xml
  31. 7
      app/src/main/res/font/roboto_medium.xml
  32. 7
      app/src/main/res/font/roboto_mono_medium.xml
  33. 62
      app/src/main/res/layout/bottom_sheet_game_list.xml
  34. 87
      app/src/main/res/layout/fragment_editable_data.xml
  35. 92
      app/src/main/res/layout/fragment_session.xml
  36. 7
      app/src/main/res/layout/row_session_view.xml
  37. 17
      app/src/main/res/menu/editable_data.xml
  38. 2
      app/src/main/res/values/preloaded_fonts.xml
  39. 29
      app/src/main/res/values/styles.xml

@ -34,6 +34,10 @@ android {
}
}
configurations {
all*.exclude group: 'com.google.guava', module: 'listenablefuture'
}
}
dependencies {
@ -47,6 +51,9 @@ dependencies {
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
// Places
implementation 'com.google.android.libraries.places:places:1.0.0'
// Firebase
implementation 'com.google.firebase:firebase-core:16.0.7'

@ -2,6 +2,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.pokeranalytics.android">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"

@ -5,6 +5,7 @@ import io.realm.Realm
import io.realm.RealmConfiguration
import io.realm.RealmResults
import io.realm.kotlin.where
import net.pokeranalytics.android.model.Limit
import net.pokeranalytics.android.model.realm.Game
import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.util.PokerAnalyticsLogs
@ -52,6 +53,8 @@ class PokerAnalyticsApplication : Application() {
*/
}
Limit.init(this)
if (BuildConfig.DEBUG) {
// Logs
Timber.plant(PokerAnalyticsLogs())

@ -0,0 +1,26 @@
package net.pokeranalytics.android.model
import android.content.Context
import net.pokeranalytics.android.R
class Limit {
companion object {
private val values = ArrayList<String>()
fun init(context: Context) {
values.clear()
values.addAll(context.resources.getStringArray(R.array.limit_short_name))
}
/**
* Get a limit name
*/
fun get(index: Int) : String? {
if (index >= 0 && index < values.size) {
return values[index]
}
return ""
}
}
}

@ -16,6 +16,8 @@ interface ObjectSavable {
fun isValidForSave(): Boolean {
return true
}
fun uniqueIdentifier(): String
}
/**
@ -64,8 +66,8 @@ enum class LiveData : Localizable {
}
}
fun deleteData(realm:Realm, data:LiveDataDataSource) {
realm.where(this.relatedEntity).equalTo("id", data.primaryKey).findAll().deleteAllFromRealm()
fun deleteData(realm:Realm, data:ObjectSavable) {
realm.where(this.relatedEntity).equalTo("id", data.uniqueIdentifier()).findAll().deleteAllFromRealm()
}
fun updateOrCreate(realm:Realm, primaryKey:String?): RealmObject {

@ -8,15 +8,12 @@ import net.pokeranalytics.android.model.ObjectSavable
import net.pokeranalytics.android.ui.adapter.components.LiveDataDataSource
import net.pokeranalytics.android.ui.adapter.components.RowRepresentableDataSource
import net.pokeranalytics.android.ui.fragment.components.bottomsheet.BottomSheetData
import net.pokeranalytics.android.ui.view.BankrollRow
import net.pokeranalytics.android.ui.view.RowEditable
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.SimpleRow
import net.pokeranalytics.android.ui.view.*
import java.util.*
import kotlin.collections.ArrayList
open class Bankroll(name: String = "") : RealmObject(), RowRepresentableDataSource, LiveDataDataSource,
RowEditable, ObjectSavable {
open class Bankroll(name: String = "") : RealmObject(), RowRepresentableDataSource,
RowEditable, ObjectSavable, RowRepresentable {
companion object {
fun newInstance() : Bankroll {
@ -42,8 +39,13 @@ open class Bankroll(name: String = "") : RealmObject(), RowRepresentableDataSour
// @todo rate management
override val title: String get() = this.name
override val primaryKey: String get() = this.id
override fun getDisplayName(): String {
return this.name
}
override fun uniqueIdentifier(): String {
return this.id
}
override fun adapterRows(): ArrayList<RowRepresentable> {
val rows = ArrayList<RowRepresentable>()

@ -4,16 +4,15 @@ import android.text.InputType
import io.realm.RealmObject
import io.realm.annotations.PrimaryKey
import net.pokeranalytics.android.model.ObjectSavable
import net.pokeranalytics.android.ui.adapter.components.DisplayableDelegate
import net.pokeranalytics.android.ui.adapter.components.LiveDataDataSource
import net.pokeranalytics.android.ui.adapter.components.RowRepresentableDataSource
import net.pokeranalytics.android.ui.fragment.components.bottomsheet.BottomSheetData
import net.pokeranalytics.android.ui.view.GameRow
import net.pokeranalytics.android.ui.view.RowEditable
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.SimpleRow
import net.pokeranalytics.android.ui.view.*
import java.util.*
open class Game : RealmObject(), RowRepresentableDataSource, LiveDataDataSource, RowEditable, ObjectSavable {
open class Game : RealmObject(), RowRepresentableDataSource, RowEditable, ObjectSavable,
RowRepresentable {
@PrimaryKey
var id = UUID.randomUUID().toString()
@ -24,8 +23,13 @@ open class Game : RealmObject(), RowRepresentableDataSource, LiveDataDataSource,
// A shorter name for the game
var shortName: String? = null
override val title: String get() = this.name
override val primaryKey: String get() = this.id
override fun getDisplayName(): String {
return this.name
}
override fun uniqueIdentifier(): String {
return this.id
}
override fun adapterRows(): ArrayList<RowRepresentable> {
val rows = ArrayList<RowRepresentable>()

@ -13,7 +13,7 @@ import net.pokeranalytics.android.ui.view.SimpleRow
import java.util.*
open class Location : RealmObject(), RowRepresentableDataSource, LiveDataDataSource, RowEditable, ObjectSavable {
open class Location : RealmObject(), RowRepresentableDataSource, RowEditable, ObjectSavable, RowRepresentable {
@PrimaryKey
var id = UUID.randomUUID().toString()
@ -27,8 +27,13 @@ open class Location : RealmObject(), RowRepresentableDataSource, LiveDataDataSou
// the latitude of the location
var latitude: Double? = null
override val title: String get() = this.name
override val primaryKey: String get() = this.id
override fun getDisplayName(): String {
return this.name
}
override fun uniqueIdentifier(): String {
return this.id
}
override fun adapterRows(): ArrayList<RowRepresentable> {
val rows = ArrayList<RowRepresentable>()

@ -1,28 +1,33 @@
package net.pokeranalytics.android.model.realm
import android.content.Context
import android.text.InputType
import io.realm.*
import io.realm.RealmList
import io.realm.RealmObject
import io.realm.annotations.Ignore
import io.realm.annotations.PrimaryKey
import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.SessionInterface
import net.pokeranalytics.android.model.Limit
import net.pokeranalytics.android.model.LiveData
import net.pokeranalytics.android.model.ObjectSavable
import net.pokeranalytics.android.model.extensions.SessionState
import net.pokeranalytics.android.model.extensions.getState
import net.pokeranalytics.android.ui.adapter.components.LiveDataDataSource
import net.pokeranalytics.android.ui.adapter.components.RowRepresentableDataSource
import net.pokeranalytics.android.ui.fragment.components.bottomsheet.BottomSheetData
import net.pokeranalytics.android.ui.view.RowEditable
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.SessionRow
import net.pokeranalytics.android.util.data.sessionDao
import net.pokeranalytics.android.util.getDuration
import net.pokeranalytics.android.util.round
import net.pokeranalytics.android.util.short
import net.pokeranalytics.android.util.toCurrency
import timber.log.Timber
import java.util.*
import kotlin.collections.ArrayList
open class Session : RealmObject(), SessionInterface, RowRepresentableDataSource, LiveDataDataSource,
RowEditable {
open class Session : RealmObject(), SessionInterface, RowRepresentableDataSource,
RowEditable, RowRepresentable, ObjectSavable {
@PrimaryKey
var id = UUID.randomUUID().toString()
@ -138,6 +143,22 @@ open class Session : RealmObject(), SessionInterface, RowRepresentableDataSource
}
}
/**
* Return the duration of the current session
*/
fun getDuration(context: Context): String {
val startDate = timeFrame?.startDate ?: Date()
val enDate = timeFrame?.endDate ?: Date()
return startDate.getDuration(context, enDate)
}
/**
* Return the formatted blinds
*/
fun getBlinds(): String {
return if (cgSmallBlind == null) "--" else "$${cgSmallBlind?.round()}/${cgBigBlind?.round()}"
}
/**
* Delete the object from realm
* TODO: Cascade delete?
@ -179,13 +200,13 @@ open class Session : RealmObject(), SessionInterface, RowRepresentableDataSource
*/
fun getGameTitle(): String {
var gameTitle = ""
if (limit != null) {
gameTitle += limit
limit?.let {
gameTitle += Limit.get(it) + " "
}
if (game != null) {
gameTitle += game?.title
gameTitle += game?.name
}
return gameTitle
return if (gameTitle.isNotBlank()) gameTitle else "--"
}
companion object {
@ -237,6 +258,14 @@ open class Session : RealmObject(), SessionInterface, RowRepresentableDataSource
return 0.0
}
override fun uniqueIdentifier(): String {
return this.id
}
override fun getDisplayName(): String {
return "session ${this.creationDate}"
}
override fun adapterRows(): ArrayList<RowRepresentable> {
val rows = ArrayList<RowRepresentable>()
rows.addAll(SessionRow.getRowsForState(getState()))
@ -251,9 +280,9 @@ open class Session : RealmObject(), SessionInterface, RowRepresentableDataSource
return when (row) {
SessionRow.BUY_IN -> buyin.toCurrency()
SessionRow.BLINDS -> if (cgSmallBlind != null && cgBigBlind != null) "$cgSmallBlind / $cgBigBlind" else "--"
SessionRow.GAME -> game?.title ?: "--"
SessionRow.LOCATION -> location?.title ?: "--"
SessionRow.BANKROLL -> bankroll?.title ?: "--"
SessionRow.GAME -> getGameTitle()
SessionRow.LOCATION -> location?.name ?: "--"
SessionRow.BANKROLL -> bankroll?.name ?: "--"
SessionRow.TABLE_SIZE -> tableSize?.toString() ?: "--"
SessionRow.START_DATE -> if (timeFrame != null) timeFrame?.startDate?.short() ?: "--" else "--"
SessionRow.END_DATE -> if (timeFrame != null) timeFrame?.endDate?.short() ?: "--" else "--"
@ -271,12 +300,6 @@ open class Session : RealmObject(), SessionInterface, RowRepresentableDataSource
}
}
override var title: String = "Change that: $creationDate"
override val primaryKey: String get() = this.id
override fun getBottomSheetData(row: RowRepresentable): ArrayList<BottomSheetData> {
val data = ArrayList<BottomSheetData>()
@ -288,8 +311,21 @@ open class Session : RealmObject(), SessionInterface, RowRepresentableDataSource
data.add(BottomSheetData(100.0 * (cgBigBlind ?: 0.0)))
data.add(BottomSheetData(200.0 * (cgBigBlind ?: 0.0)))
data.add(BottomSheetData(buyin))
data.add(BottomSheetData("",inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL))
data.add(BottomSheetData("",inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL))
data.add(
BottomSheetData(
"",
inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL
)
)
data.add(
BottomSheetData(
"",
inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL
)
)
}
SessionRow.CASHED_OUT -> {
data.add(BottomSheetData(result?.cashout, inputType = InputType.TYPE_CLASS_NUMBER))
}
SessionRow.TIPS -> {
// Disable the buttons with value = 0, add current value & set the 2 edit texts
@ -300,7 +336,9 @@ open class Session : RealmObject(), SessionInterface, RowRepresentableDataSource
data.add(BottomSheetData("", inputType = InputType.TYPE_CLASS_NUMBER))
data.add(BottomSheetData("", inputType = InputType.TYPE_CLASS_NUMBER))
}
SessionRow.TABLE_SIZE -> {data.add(BottomSheetData(tableSize))}
SessionRow.TABLE_SIZE -> {
data.add(BottomSheetData(tableSize))
}
SessionRow.GAME -> {
// Add current game & games list
data.add(BottomSheetData(limit))
@ -329,14 +367,33 @@ open class Session : RealmObject(), SessionInterface, RowRepresentableDataSource
override fun updateValue(value: Any?, row: RowRepresentable) {
realm.beginTransaction()
when (row) {
SessionRow.BUY_IN -> {
val localResult = if (result != null) result as Result else realm.createObject(Result::class.java)
localResult.buyin = value as Double
localResult.buyin = value as Double?
result = localResult
}
SessionRow.CASHED_OUT -> {
val localResult = if (result != null) result as Result else realm.createObject(Result::class.java)
localResult.cashout = if (value == null) null else (value as String).toDouble()
result = localResult
}
SessionRow.TABLE_SIZE -> tableSize = value as Int?
SessionRow.GAME -> game = value as Game?
SessionRow.GAME -> {
if (value is ArrayList<*>) {
Timber.d("${value[0]}")
Timber.d("${value[1]}")
limit = try {
(value[0] as Int?)
} catch (e: Exception) {
null
}
game = try {
(value[1] as Game?)
} catch (e: Exception) {
null
}
}
}
SessionRow.BANKROLL -> bankroll = value as Bankroll?
SessionRow.LOCATION -> location = value as Location?
SessionRow.COMMENT -> comment = value as String? ?: ""
@ -351,20 +408,28 @@ open class Session : RealmObject(), SessionInterface, RowRepresentableDataSource
} catch (e: Exception) {
null
}
cgBigBlind?.let {
if (cgSmallBlind == null || cgSmallBlind == 0.0) {
cgSmallBlind = it / 2.0
}
}
}
//TODO: Update
SessionRow.START_DATE -> if (value is Date?) {
if (value == null) {
timeFrame = null
} else {
val timeFrameToUpdate = if (timeFrame != null) timeFrame as TimeFrame else realm.createObject(TimeFrame::class.java)
val timeFrameToUpdate =
if (timeFrame != null) timeFrame as TimeFrame else realm.createObject(TimeFrame::class.java)
timeFrameToUpdate.setDate(value, null)
timeFrame = timeFrameToUpdate
}
}
//TODO: Update
SessionRow.END_DATE -> if (value is Date?) {
val timeFrameToUpdate = if (timeFrame != null) timeFrame as TimeFrame else realm.createObject(TimeFrame::class.java)
val timeFrameToUpdate =
if (timeFrame != null) timeFrame as TimeFrame else realm.createObject(TimeFrame::class.java)
timeFrameToUpdate.setDate(null, value)
timeFrame = timeFrameToUpdate
}
@ -378,70 +443,3 @@ enum class TournamentKind {
MTT,
SNG
}
/**
* Session Dao
*/
class SessionDao(realmDb: Realm) {
var realm: Realm = realmDb
/**
* Create or update session
*/
fun createOrUpdateSession(session: Session): Session {
realm.beginTransaction()
val sessionToSave = realm.copyToRealmOrUpdate(session)
realm.commitTransaction()
return realm.copyFromRealm(sessionToSave)
}
/**
* Create or update sessions
*/
fun createOrUpdateSessions(sessions: List<Session>): List<Session> {
realm.beginTransaction()
// Update
val sessionsToSave = realm.copyToRealmOrUpdate(sessions)
realm.commitTransaction()
return realm.copyFromRealm(sessionsToSave)
}
/**
* Find all sessions
*/
fun findAllSessions(): RealmResults<Session> {
return realm.where(Session::class.java).findAll().sort("creationDate", Sort.DESCENDING)
}
/**
* Find session by id
*/
fun findSessionById(sessionId: Int): Session? {
return realm.copyFromRealm(realm.where(Session::class.java).equalTo("id", sessionId).findFirst())
}
/**
* Delete session
*/
fun deleteSession(sessionId: Int) {
realm.beginTransaction()
realm.sessionDao().findSessionById(sessionId)?.deleteFromRealm()
realm.commitTransaction()
}
/**
* Delete all sessions
*/
fun deleteAllSessions() {
realm.beginTransaction()
realm.sessionDao().findAllSessions().deleteAllFromRealm()
realm.commitTransaction()
}
}

@ -12,7 +12,7 @@ import net.pokeranalytics.android.ui.view.SimpleRow
import net.pokeranalytics.android.ui.view.TournamentFeatureRow
import java.util.*
open class TournamentFeature : RealmObject(), RowRepresentableDataSource, LiveDataDataSource, RowEditable, ObjectSavable {
open class TournamentFeature : RealmObject(), RowRepresentableDataSource, RowEditable, ObjectSavable, RowRepresentable {
@PrimaryKey
var id = UUID.randomUUID().toString()
@ -20,8 +20,13 @@ open class TournamentFeature : RealmObject(), RowRepresentableDataSource, LiveDa
// The name of the feature
var name: String = ""
override val title: String get() = this.name
override val primaryKey: String get() = this.id
override fun getDisplayName(): String {
return this.name
}
override fun uniqueIdentifier(): String {
return this.id
}
override fun adapterRows(): ArrayList<RowRepresentable> {
val rows = ArrayList<RowRepresentable>()

@ -13,7 +13,7 @@ import net.pokeranalytics.android.ui.view.TransactionTypeRow
import java.util.*
open class TransactionType : RealmObject(), RowRepresentableDataSource, LiveDataDataSource, RowEditable, ObjectSavable {
open class TransactionType : RealmObject(), RowRepresentableDataSource, RowEditable, ObjectSavable, RowRepresentable {
@PrimaryKey
var id = UUID.randomUUID().toString()
@ -30,8 +30,13 @@ open class TransactionType : RealmObject(), RowRepresentableDataSource, LiveData
// The predefined kind, if necessary, like: Withdrawal, deposit, or tips
var kind: Int? = null
override val title: String get() = this.name
override val primaryKey: String get() = this.id
override fun getDisplayName(): String {
return this.name
}
override fun uniqueIdentifier(): String {
return this.id
}
override fun adapterRows(): ArrayList<RowRepresentable> {
val rows = ArrayList<RowRepresentable>()

@ -3,6 +3,7 @@ package net.pokeranalytics.android.ui.activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.fragment.app.Fragment
import com.google.android.material.bottomnavigation.BottomNavigationView
import kotlinx.android.synthetic.main.activity_home.*
import net.pokeranalytics.android.R
@ -68,7 +69,7 @@ class HomeActivity : PokerAnalyticsActivity() {
else -> ""
}
val fragment = when(index) {
val fragment: Fragment = when(index) {
0 -> HistoryFragment()
1 -> StatsFragment()
else -> SettingsFragment()

@ -21,6 +21,7 @@ class LimitTypesAdapter(private var tableSizes: ArrayList<String>) : RecyclerVie
itemView.title.text = tableSize
itemView.container.setOnClickListener {
onClickOnItem?.invoke(adapterPosition)
}
}
}

@ -9,9 +9,13 @@ import androidx.recyclerview.widget.RecyclerView
import net.pokeranalytics.android.R
import net.pokeranalytics.android.ui.view.RowViewType
enum class LiveDataViewType {
DATA,
BOTTOM_SHEET_DATA
}
interface LiveDataDataSource {
val title: String
val primaryKey: String
}
interface LiveDataDelegate {
@ -20,17 +24,20 @@ interface LiveDataDelegate {
fun size() : Int
}
class LiveDataAdapter(var adapterDelegate: LiveDataDelegate, var layout: Int? = null) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
class LiveDataAdapter(var adapterDelegate: LiveDataDelegate, var liveDataViewType: LiveDataViewType? = LiveDataViewType.DATA) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
inner class DataViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(row: LiveDataDataSource, listener: View.OnClickListener) {
try {
itemView.findViewById<AppCompatTextView>(R.id.title).text = row.title
itemView.findViewById<ConstraintLayout>(R.id.container).setOnClickListener(listener)
} catch (e: Exception) {
e.printStackTrace()
when(liveDataViewType) {
LiveDataViewType.DATA -> {
itemView.findViewById<AppCompatTextView>(R.id.rowTitle_title).text = row.title
itemView.findViewById<ConstraintLayout>(R.id.rowTitle_container).setOnClickListener(listener)
}
LiveDataViewType.BOTTOM_SHEET_DATA -> {
itemView.findViewById<AppCompatTextView>(R.id.title).text = row.title
itemView.findViewById<ConstraintLayout>(R.id.container).setOnClickListener(listener)
}
}
}
}
@ -39,8 +46,16 @@ class LiveDataAdapter(var adapterDelegate: LiveDataDelegate, var layout: Int? =
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val layoutToInflate = layout ?: R.layout.row_title
return DataViewHolder(LayoutInflater.from(parent.context).inflate(layoutToInflate, parent, false))
val layoutToInflate = when(liveDataViewType) {
LiveDataViewType.DATA -> {
R.layout.row_title
}
LiveDataViewType.BOTTOM_SHEET_DATA -> {
R.layout.row_bottom_sheet_title
}
else -> R.layout.row_title
}
return DataViewHolder(LayoutInflater.from(parent.context).inflate(layoutToInflate, parent, false))
}
override fun getItemCount(): Int {

@ -6,18 +6,45 @@ import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import net.pokeranalytics.android.ui.view.BindableHolder
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowRepresentableDiffCallback
import net.pokeranalytics.android.ui.view.RowViewType
interface RowRepresentableDataSource : DisplayableDataSource {
fun rowRepresentableForPosition(position:Int): RowRepresentable {
if (this.numberOfRows() > position) {
return this.adapterRows()[position]
} else {
throw IllegalStateException("Need to implement Data Source")
}
}
fun numberOfRows(): Int {
return this.adapterRows().size
}
fun viewTypeForPosition(position:Int): Int {
return this.rowRepresentableForPosition(position).viewType
}
fun indexForRow(row:RowRepresentable): Int {
return this.adapterRows().indexOf(row)
}
}
interface RowRepresentableDelegate : DisplayableDelegate {
fun onIndexSelected(position: Int) {}
}
/**
* An interface used to provide RowRepresentableAdapter content and value in the form of rows
*/
interface RowRepresentableDataSource {
interface DisplayableDataSource {
/**
* Returns a list of rows
*/
fun adapterRows(): ArrayList<RowRepresentable>
fun adapterRows(): ArrayList<RowRepresentable> {
return ArrayList()
}
/**
* Returns a boolean for a specific row
@ -48,39 +75,29 @@ interface RowRepresentableDataSource {
* - switch (bool)
* - static content
* */
}
/**
* A delegate used to propagate UI actions
*/
interface RowRepresentableDelegate {
interface DisplayableDelegate {
fun onRowSelected(row: RowRepresentable) {}
fun onActionSelected(row: RowRepresentable) {}
}
/**
* An adapter capable of displaying a list of RowRepresentables
* @param rowRepresentableDataSource the datasource providing rows
* @param rowRepresentableDelegate the delegate, notified of UI actions
* @param dataSource the datasource providing rows
* @param delegate the delegate, notified of UI actions
*/
class RowRepresentableAdapter(
var rowRepresentableDataSource: RowRepresentableDataSource,
var rowRepresentableDelegate: RowRepresentableDelegate? = null
var dataSource: RowRepresentableDataSource,
var delegate: RowRepresentableDelegate? = null
) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
/**
* The list of rows to display
*/
private var rows: ArrayList<RowRepresentable> = ArrayList()
init {
this.rows = rowRepresentableDataSource.adapterRows()
}
override fun getItemViewType(position: Int): Int {
return this.rows[position].viewType
return this.dataSource.viewTypeForPosition(position)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
@ -89,28 +106,29 @@ class RowRepresentableAdapter(
}
override fun getItemCount(): Int {
return this.rows.size
return this.dataSource.numberOfRows()
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val dynamicRow = this.rows[position]
val dynamicRow = this.dataSource.rowRepresentableForPosition(position)
val listener = View.OnClickListener {
rowRepresentableDelegate?.onRowSelected(dynamicRow)
delegate?.onRowSelected(dynamicRow)
delegate?.onIndexSelected(position)
}
val actionListener = View.OnClickListener {
rowRepresentableDelegate?.onActionSelected(dynamicRow)
delegate?.onActionSelected(dynamicRow)
}
(holder as BindableHolder).bind(dynamicRow, this.rowRepresentableDataSource, listener, actionListener)
(holder as BindableHolder).bind(dynamicRow, this.dataSource, listener, actionListener)
}
/**
* Refresh the row in the adapter
*/
fun refreshRow(row: RowRepresentable) {
val index = rows.indexOf(row)
val index = this.dataSource.indexForRow(row)
if (index >= 0) {
notifyItemChanged(index)
}
@ -119,9 +137,7 @@ class RowRepresentableAdapter(
/**
* Update UI
*/
fun updateRows(newRows: ArrayList<RowRepresentable>) {
val diffResult = DiffUtil.calculateDiff(RowRepresentableDiffCallback(newRows, rows, rowRepresentableDataSource))
this.rows = newRows
fun updateRows(diffResult: DiffUtil.DiffResult) {
diffResult.dispatchUpdatesTo(this)
}

@ -5,17 +5,21 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import io.realm.ObjectChangeSet
import io.realm.Realm
import io.realm.RealmResults
import kotlinx.android.synthetic.main.fragment_data_list.*
import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.ObjectSavable
import net.pokeranalytics.android.ui.activity.EditableDataActivity
import net.pokeranalytics.android.ui.adapter.components.*
import net.pokeranalytics.android.ui.fragment.components.PokerAnalyticsFragment
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.ui.view.SettingRow
import timber.log.Timber
class DataListFragment : PokerAnalyticsFragment(), LiveDataDelegate {
class DataListFragment : PokerAnalyticsFragment(), RowRepresentableDataSource, RowRepresentableDelegate{
private lateinit var dataType: SettingRow
@ -31,20 +35,28 @@ class DataListFragment : PokerAnalyticsFragment(), LiveDataDelegate {
initUI()
}
override fun data(position: Int): LiveDataDataSource {
return (items[position] as LiveDataDataSource)
override fun rowRepresentableForPosition(position: Int): RowRepresentable {
return this.items[position] as RowRepresentable
}
override fun onRowSelected(position: Int) {
override fun numberOfRows(): Int {
return this.items.size
}
override fun viewTypeForPosition(position: Int): Int {
return RowViewType.DATA.ordinal
}
override fun indexForRow(row: RowRepresentable): Int {
return this.items.indexOf(row)
}
override fun onIndexSelected(position: Int) {
this.dataType.relatedResultsRepresentable?.let {
EditableDataActivity.newInstance(requireContext(), it.ordinal, this.data(position).primaryKey)
EditableDataActivity.newInstance(requireContext(), it.ordinal, (this.items[position] as ObjectSavable).uniqueIdentifier())
}
}
override fun size(): Int {
return items.size
}
private fun initData() {
}
@ -54,7 +66,7 @@ class DataListFragment : PokerAnalyticsFragment(), LiveDataDelegate {
private fun initUI() {
val viewManager = LinearLayoutManager(requireContext())
val dataListAdapter = LiveDataAdapter(this)
val dataListAdapter = RowRepresentableAdapter(this, this)
recyclerView.apply {
setHasFixedSize(true)

@ -1,10 +1,7 @@
package net.pokeranalytics.android.ui.fragment
import android.content.DialogInterface
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.*
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.recyclerview.widget.LinearLayoutManager
@ -25,9 +22,10 @@ import timber.log.Timber
class EditableDataFragment : PokerAnalyticsFragment(), RowRepresentableDelegate, BottomSheetDelegate {
private lateinit var item: RealmObject
private lateinit var item: RealmObject
private lateinit var liveDataType: LiveData
private lateinit var rowRepresentableAdapter: RowRepresentableAdapter
private var editableMenu: Menu? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_editable_data, container, false)
@ -39,6 +37,22 @@ class EditableDataFragment : PokerAnalyticsFragment(), RowRepresentableDelegate,
initUI()
}
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
inflater?.inflate(R.menu.editable_data, menu)
this.editableMenu = menu
updateMenuUI()
super.onCreateOptionsMenu(menu, inflater)
}
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
when (item!!.itemId) {
R.id.save -> saveData()
R.id.delete -> deleteData()
}
return true
}
override fun onRowSelected(row: RowRepresentable) {
BottomSheetFragment.create(fragmentManager, row, this, (this.item as RowEditable).getBottomSheetData(row))
}
@ -49,16 +63,16 @@ class EditableDataFragment : PokerAnalyticsFragment(), RowRepresentableDelegate,
override fun clickOnClear(row: RowRepresentable) {
Toast.makeText(requireContext(), "Clear: $row", Toast.LENGTH_SHORT).show()
(this.item as RowEditable).updateValue(null, row)
this.getRealm().executeTransaction {
(this.item as RowEditable).updateValue(null, row)
it.copyToRealmOrUpdate(this.item)
}
rowRepresentableAdapter.refreshRow(row)
}
override fun setValue(value: Any?, row: RowRepresentable) {
(this.item as RowEditable).updateValue(value, row)
this.getRealm().executeTransaction {
(this.item as RowEditable).updateValue(value, row)
it.copyToRealmOrUpdate(this.item)
}
rowRepresentableAdapter.refreshRow(row)
@ -74,6 +88,7 @@ class EditableDataFragment : PokerAnalyticsFragment(), RowRepresentableDelegate,
val activity = activity as PokerAnalyticsActivity
activity.setSupportActionBar(toolbar)
activity.supportActionBar?.setDisplayHomeAsUpEnabled(true)
setHasOptionsMenu(true)
val viewManager = LinearLayoutManager(requireContext())
@ -81,49 +96,60 @@ class EditableDataFragment : PokerAnalyticsFragment(), RowRepresentableDelegate,
setHasFixedSize(true)
layoutManager = viewManager
}
}
/**
* Update menu UI
*/
private fun updateMenuUI() {
editableMenu?.findItem(R.id.delete)?.isVisible = item.isManaged
editableMenu?.findItem(R.id.save)?.isVisible = true
}
/**
* Save data
*/
private fun saveData() {
if ((this.item as ObjectSavable).isValidForSave()) {
this.getRealm().executeTransaction {
it.copyToRealmOrUpdate(this.item)
}
activity?.finish()
} else {
val builder = AlertDialog.Builder(requireContext())
.setMessage(R.string.empty_name_for_br_error)
.setNegativeButton(R.string.ok, null)
builder.show()
}
this.saveButton.text = this.saveButton.context.getString(R.string.save)
this.saveButton.setOnClickListener {
}
if ((this.item as ObjectSavable).isValidForSave()) {
this.getRealm().executeTransaction {
it.copyToRealmOrUpdate(this.item)
/**
* Delete data
*/
private fun deleteData() {
val builder = AlertDialog.Builder(requireContext())
builder.setTitle(R.string.warning)
.setMessage(R.string.are_you_sure_you_want_to_do_that_)
.setNegativeButton(R.string.no, null)
.setPositiveButton(R.string.yes) { dialog, id ->
if (this.item.isManaged) {
Toast.makeText(requireContext(), "isManaged", Toast.LENGTH_SHORT).show()
Timber.d("is managed")
this.getRealm().executeTransaction {
this.liveDataType.deleteData(it, (this.item as ObjectSavable))
}
} else {
Toast.makeText(requireContext(), "isNotManaged", Toast.LENGTH_SHORT).show()
Timber.d("is not managed")
}
this.activity?.let {
it.finish()
}
} else {
val builder = AlertDialog.Builder(it.context)
.setMessage(R.string.empty_name_for_br_error)
.setNegativeButton(R.string.ok, null)
builder.show()
}
}
builder.show()
this.deleteButton.text = this.deleteButton.context.getString(R.string.delete)
this.deleteButton.setOnClickListener {
val builder = AlertDialog.Builder(it.context)
builder.setTitle(R.string.warning)
.setMessage(R.string.are_you_sure_you_want_to_do_that_)
.setNeutralButton(R.string.no, null)
.setNegativeButton(R.string.yes, DialogInterface.OnClickListener { dialog, id ->
if (this.item.isManaged) {
Toast.makeText(requireContext(), "isManaged", Toast.LENGTH_SHORT).show()
Timber.d("is managed")
this.getRealm().executeTransaction {
this.liveDataType.deleteData(it, (this.item as LiveDataDataSource))
}
} else {
Toast.makeText(requireContext(), "isNotManaged", Toast.LENGTH_SHORT).show()
Timber.d("is not managed")
}
this.activity?.let {
it.finish()
}
})
builder.show()
}
}
/**
@ -131,8 +157,8 @@ class EditableDataFragment : PokerAnalyticsFragment(), RowRepresentableDelegate,
*/
fun setData(dataType: Int, primaryKey: String?) {
this.liveDataType = LiveData.values()[dataType]
var proxyItem : RealmObject? = this.liveDataType.getData(this.getRealm(), primaryKey)
proxyItem?.let {
var proxyItem: RealmObject? = this.liveDataType.getData(this.getRealm(), primaryKey)
proxyItem?.let {
this.appBar.toolbar.title = "Update ${this.liveDataType.name.toLowerCase().capitalize()}"
} ?: run {
this.appBar.toolbar.title = "New ${this.liveDataType.name.toLowerCase().capitalize()}"

@ -7,15 +7,21 @@ import android.view.ViewGroup
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import io.realm.RealmResults
import io.realm.Sort
import io.realm.kotlin.where
import kotlinx.android.synthetic.main.fragment_history.*
import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.ObjectSavable
import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.ui.activity.SessionActivity
import net.pokeranalytics.android.ui.adapter.HistoryAdapter
import net.pokeranalytics.android.ui.adapter.components.RowRepresentableAdapter
import net.pokeranalytics.android.ui.adapter.components.RowRepresentableDataSource
import net.pokeranalytics.android.ui.adapter.components.RowRepresentableDelegate
import net.pokeranalytics.android.ui.fragment.components.PokerAnalyticsFragment
import net.pokeranalytics.android.util.data.sessionDao
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType
class HistoryFragment : PokerAnalyticsFragment() {
class HistoryFragment : PokerAnalyticsFragment(), RowRepresentableDataSource, RowRepresentableDelegate {
companion object {
fun newInstance(): HistoryFragment {
@ -26,8 +32,8 @@ class HistoryFragment : PokerAnalyticsFragment() {
}
}
private lateinit var historyAdapter: HistoryAdapter
private var realmSessions: RealmResults<Session>? = null
private lateinit var historyAdapter: RowRepresentableAdapter
private lateinit var realmSessions: RealmResults<Session>
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_history, container, false)
@ -41,7 +47,12 @@ class HistoryFragment : PokerAnalyticsFragment() {
override fun onDestroyView() {
super.onDestroyView()
realmSessions?.removeAllChangeListeners()
realmSessions.removeAllChangeListeners()
}
override fun onResume() {
super.onResume()
historyAdapter.notifyDataSetChanged()
}
/**
@ -70,28 +81,34 @@ class HistoryFragment : PokerAnalyticsFragment() {
* Init data
*/
private fun initData() {
realmSessions = getRealm().where<Session>().findAll().sort("timeFrame.startDate", Sort.DESCENDING)
val viewManager = LinearLayoutManager(requireContext())
historyAdapter = RowRepresentableAdapter(this, this)
recyclerView.apply {
setHasFixedSize(true)
layoutManager = viewManager
adapter = historyAdapter
}
}
realmSessions = getRealm().sessionDao().findAllSessions()
realmSessions?.let {
val viewManager = LinearLayoutManager(requireContext())
historyAdapter = HistoryAdapter(it)
recyclerView.apply {
setHasFixedSize(true)
layoutManager = viewManager
adapter = historyAdapter
}
override fun rowRepresentableForPosition(position: Int): RowRepresentable {
return this.realmSessions[position] as RowRepresentable
}
historyAdapter.onClickOnSession = {position, session ->
SessionActivity.newInstance(requireContext(), sessionId = session.id)
}
override fun numberOfRows(): Int {
return this.realmSessions.size
}
it.addChangeListener { newSessions ->
historyAdapter.notifyDataSetChanged()
}
}
override fun viewTypeForPosition(position: Int): Int {
return RowViewType.ROW_SESSION.ordinal
}
override fun indexForRow(row: RowRepresentable): Int {
return this.realmSessions.indexOf(row)
}
override fun onRowSelected(row: RowRepresentable) {
SessionActivity.newInstance(requireContext(), sessionId = (row as ObjectSavable).uniqueIdentifier())
}
}

@ -4,7 +4,9 @@ import android.os.Bundle
import android.view.*
import android.view.animation.OvershootInterpolator
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.interpolator.view.animation.FastOutSlowInInterpolator
import androidx.recyclerview.widget.DiffUtil
import io.realm.kotlin.where
import kotlinx.android.synthetic.main.fragment_session.*
import net.pokeranalytics.android.R
@ -21,8 +23,10 @@ import net.pokeranalytics.android.ui.fragment.components.PokerAnalyticsFragment
import net.pokeranalytics.android.ui.fragment.components.bottomsheet.BottomSheetDelegate
import net.pokeranalytics.android.ui.fragment.components.bottomsheet.BottomSheetFragment
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowRepresentableDiffCallback
import net.pokeranalytics.android.ui.view.SessionRow
import net.pokeranalytics.android.ui.view.SmoothScrollLinearLayoutManager
import net.pokeranalytics.android.util.px
import net.pokeranalytics.android.util.toast
import timber.log.Timber
import java.util.*
@ -32,7 +36,7 @@ class SessionFragment : PokerAnalyticsFragment(), RowRepresentableDelegate, Bott
private lateinit var currentSession: Session
private lateinit var sessionAdapter: RowRepresentableAdapter
private var sessionMenu: Menu? = null
private val oldRows: ArrayList<RowRepresentable> = ArrayList()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_session, container, false)
@ -145,7 +149,6 @@ class SessionFragment : PokerAnalyticsFragment(), RowRepresentableDelegate, Bott
floatingActionButton.setOnClickListener {
manageSessionState()
}
}
/**
@ -156,42 +159,89 @@ class SessionFragment : PokerAnalyticsFragment(), RowRepresentableDelegate, Bott
when (currentSession.getState()) {
SessionState.PENDING -> {
state.text = "WAITING"
state.setTextColor(ContextCompat.getColor(requireContext(), R.color.white))
sessionMenu?.findItem(R.id.restart)?.isVisible = false
floatingActionButton.setImageResource(R.drawable.ic_outline_play)
bottomAppBar.menu.findItem(R.id.stop).isVisible = false
floatingActionButton.animate().scaleX(1f).scaleY(1f).alpha(1f)
.setInterpolator(OvershootInterpolator()).start()
sessionData.animate().translationY(-(72f.px)).alpha(1f)
.setInterpolator(FastOutSlowInInterpolator()).setDuration(500).start()
recyclerView.animate().translationY(-(72f.px))
.setInterpolator(FastOutSlowInInterpolator()).setDuration(500).start()
}
SessionState.STARTED -> {
state.text = "PLAYING"
state.setTextColor(ContextCompat.getColor(requireContext(), R.color.green))
sessionMenu?.findItem(R.id.restart)?.isVisible = true
floatingActionButton.setImageResource(R.drawable.ic_outline_pause)
bottomAppBar.menu.findItem(R.id.stop).isVisible = true
floatingActionButton.animate().scaleX(1f).scaleY(1f).alpha(1f)
.setInterpolator(OvershootInterpolator()).start()
sessionData.animate().translationY(0f).alpha(1f)
.setInterpolator(FastOutSlowInInterpolator()).setDuration(500).start()
recyclerView.animate().translationY(0f.px)
.setInterpolator(FastOutSlowInInterpolator()).setDuration(500).start()
}
SessionState.PAUSED -> {
state.text = "BREAK"
state.setTextColor(ContextCompat.getColor(requireContext(), R.color.blue))
sessionMenu?.findItem(R.id.restart)?.isVisible = true
floatingActionButton.setImageResource(R.drawable.ic_outline_play)
bottomAppBar.menu.findItem(R.id.stop).isVisible = true
floatingActionButton.animate().scaleX(1f).scaleY(1f).alpha(1f)
.setInterpolator(OvershootInterpolator()).start()
sessionData.animate().translationY(0f).alpha(1f)
.setInterpolator(FastOutSlowInInterpolator()).setDuration(500).start()
recyclerView.animate().translationY(0f.px)
.setInterpolator(FastOutSlowInInterpolator()).setDuration(500).start()
}
SessionState.FINISHED -> {
state.text = "FINISHED"
state.setTextColor(ContextCompat.getColor(requireContext(), R.color.white))
sessionMenu?.findItem(R.id.restart)?.isVisible = true
bottomAppBar.menu.findItem(R.id.stop).isVisible = false
floatingActionButton.animate().scaleX(0f).scaleY(0f).alpha(0f)
.setInterpolator(FastOutSlowInInterpolator()).start()
sessionData.animate().translationY(0f).alpha(1f)
.setInterpolator(FastOutSlowInInterpolator()).setDuration(500).start()
recyclerView.animate().translationY(0f.px)
.setInterpolator(FastOutSlowInInterpolator()).setDuration(500).start()
}
}
sessionAdapter.updateRows(currentSession.adapterRows())
recyclerView.smoothScrollToPosition(0)
updateAdapterUI(true)
}
/**
* Update adapter UI
*/
private fun updateAdapterUI(scrollToTop: Boolean) {
val diffResult = DiffUtil.calculateDiff(RowRepresentableDiffCallback(currentSession.adapterRows(), oldRows))
sessionAdapter.updateRows(diffResult)
oldRows.clear()
oldRows.addAll(currentSession.adapterRows())
if (scrollToTop) {
recyclerView.smoothScrollToPosition(0)
}
}
/**
* Update the state of the session (start / pause)
*/
private fun manageSessionState() {
Timber.d("oldRows: $oldRows")
when (currentSession.getState()) {
SessionState.PENDING -> {
currentSession.startSession()

@ -8,15 +8,14 @@ import io.realm.RealmResults
import kotlinx.android.synthetic.main.bottom_sheet_list.*
import kotlinx.android.synthetic.main.fragment_bottom_sheet.view.*
import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.realm.Game
import net.pokeranalytics.android.ui.adapter.components.LiveDataAdapter
import net.pokeranalytics.android.ui.adapter.components.LiveDataDataSource
import net.pokeranalytics.android.ui.adapter.components.LiveDataDelegate
import net.pokeranalytics.android.ui.adapter.components.*
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType
class BottomSheetListFragment : BottomSheetFragment(), LiveDataDelegate {
class BottomSheetListFragment : BottomSheetFragment(), RowRepresentableDataSource, RowRepresentableDelegate {
private var realmData: RealmResults<*>? = null
private lateinit var dataAdapter: LiveDataAdapter
private lateinit var dataAdapter: RowRepresentableAdapter
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@ -29,15 +28,32 @@ class BottomSheetListFragment : BottomSheetFragment(), LiveDataDelegate {
dataAdapter.notifyDataSetChanged()
}
override fun data(position: Int): LiveDataDataSource {
override fun rowRepresentableForPosition(position: Int): RowRepresentable {
realmData?.let {
return it[position] as LiveDataDataSource
return it[position] as RowRepresentable
}
//TODO: Change that
return Game()
return super.rowRepresentableForPosition(position)
}
override fun onRowSelected(position: Int) {
override fun numberOfRows(): Int {
realmData?.let {
return it.size
}
return super.numberOfRows()
}
override fun viewTypeForPosition(position: Int): Int {
return RowViewType.BOTTOM_SHEET_DATA.ordinal
}
override fun indexForRow(row: RowRepresentable): Int {
realmData?.let {
return it.indexOf(row)
}
return super.indexForRow(row)
}
override fun onIndexSelected(position: Int) {
realmData?.let {
val selectedData = it[position]
selectedData?.let {data ->
@ -45,10 +61,7 @@ class BottomSheetListFragment : BottomSheetFragment(), LiveDataDelegate {
dismiss()
}
}
}
override fun size(): Int {
return realmData?.size ?: 0
super.onIndexSelected(position)
}
/**
@ -69,7 +82,7 @@ class BottomSheetListFragment : BottomSheetFragment(), LiveDataDelegate {
LayoutInflater.from(requireContext()).inflate(R.layout.bottom_sheet_list, view?.bottomSheetContainer, true)
val viewManager = LinearLayoutManager(requireContext())
dataAdapter = LiveDataAdapter(this, R.layout.row_bottom_sheet_title)
dataAdapter = RowRepresentableAdapter(this, this)
reyclerView.apply {
setHasFixedSize(true)

@ -4,22 +4,24 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.chip.Chip
import io.realm.RealmResults
import kotlinx.android.synthetic.main.bottom_sheet_game_list.*
import kotlinx.android.synthetic.main.fragment_bottom_sheet.view.*
import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.realm.Game
import net.pokeranalytics.android.ui.adapter.LimitTypesAdapter
import net.pokeranalytics.android.ui.adapter.components.LiveDataAdapter
import net.pokeranalytics.android.ui.adapter.components.LiveDataDataSource
import net.pokeranalytics.android.ui.adapter.components.LiveDataDelegate
import net.pokeranalytics.android.ui.adapter.components.RowRepresentableAdapter
import net.pokeranalytics.android.ui.adapter.components.RowRepresentableDataSource
import net.pokeranalytics.android.ui.adapter.components.RowRepresentableDelegate
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType
import timber.log.Timber
class BottomSheetListGameFragment : BottomSheetFragment(), LiveDataDelegate {
class BottomSheetListGameFragment : BottomSheetFragment(), RowRepresentableDataSource, RowRepresentableDelegate {
private var realmData: RealmResults<*>? = null
private lateinit var dataAdapter: LiveDataAdapter
private lateinit var dataAdapter: RowRepresentableAdapter
private val values = ArrayList<Any?>()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@ -32,29 +34,45 @@ class BottomSheetListGameFragment : BottomSheetFragment(), LiveDataDelegate {
dataAdapter.notifyDataSetChanged()
}
override fun data(position: Int): LiveDataDataSource {
override fun getValue(): Any? {
return values
}
override fun rowRepresentableForPosition(position: Int): RowRepresentable {
realmData?.let {
return it[position] as LiveDataDataSource
return it[position] as RowRepresentable
}
//TODO: Change that
return Game()
return super.rowRepresentableForPosition(position)
}
override fun onRowSelected(position: Int) {
override fun numberOfRows(): Int {
realmData?.let {
val selectedData = it[position]
selectedData?.let {data ->
bottomSheetDelegate.setValue(data, row)
dismiss()
}
return it.size
}
return super.numberOfRows()
}
override fun size(): Int {
override fun viewTypeForPosition(position: Int): Int {
return RowViewType.BOTTOM_SHEET_DATA.ordinal
}
Timber.d("Games: ${realmData?.size}")
override fun indexForRow(row: RowRepresentable): Int {
realmData?.let {
return it.indexOf(row)
}
return super.indexForRow(row)
}
return realmData?.size ?: 0
override fun onIndexSelected(position: Int) {
realmData?.let {
val selectedData = it[position]
selectedData?.let { data ->
values[1] = data
bottomSheetDelegate.setValue(values, row)
dismiss()
}
}
super.onIndexSelected(position)
}
/**
@ -74,23 +92,28 @@ class BottomSheetListGameFragment : BottomSheetFragment(), LiveDataDelegate {
LayoutInflater.from(requireContext()).inflate(R.layout.bottom_sheet_game_list, view?.bottomSheetContainer, true)
values.add(0, null)
values.add(1, null)
val limits = ArrayList<String>()
limits.addAll(resources.getStringArray(R.array.limit_short_name))
chipGroup.removeAllViews()
for ((index, limit) in limits.withIndex()) {
val chip = Chip(requireContext())
chip.text = limit
chip.id = index
chipGroup.addView(chip)
}
val viewManager1 = LinearLayoutManager(requireContext())
val gameDataAdapter1 = LimitTypesAdapter(limits)
recyclerView1.apply {
setHasFixedSize(true)
layoutManager = viewManager1
adapter = gameDataAdapter1
isNestedScrollingEnabled = false
chipGroup.setOnCheckedChangeListener { chipGroup, i ->
Timber.d("Chip selected: $i")
values[0] = i
}
val viewManager2 = LinearLayoutManager(requireContext())
dataAdapter = LiveDataAdapter(this, R.layout.row_bottom_sheet_title)
dataAdapter = RowRepresentableAdapter(this, this)
recyclerView2.apply {
setHasFixedSize(true)

@ -1,16 +1,131 @@
package net.pokeranalytics.android.ui.fragment.components.bottomsheet
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import androidx.recyclerview.widget.GridLayoutManager
import kotlinx.android.synthetic.main.bottom_sheet_grid.*
import kotlinx.android.synthetic.main.fragment_bottom_sheet.view.*
import net.pokeranalytics.android.ui.adapter.TableSizeGridAdapter
import net.pokeranalytics.android.R
import net.pokeranalytics.android.ui.adapter.components.*
import net.pokeranalytics.android.ui.view.GridSpacingItemDecoration
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.util.px
class TableSize(var numberOfPlayer:Int): RowRepresentable {
companion object {
val all = Array(8, init =
{ index -> TableSize(index+2)})
}
override val resId: Int?
get() {
return if (this.numberOfPlayer == 2) {
R.string.heads_up
} else {
R.string.max
}
}
override fun localizedTitle(context: Context): String {
this.resId?.let {
return if (this.numberOfPlayer == 2) {
context.getString(it)
} else {
"$this.numberOfPlayer$context.getString(it)"
}
}
return super.localizedTitle(context)
}
override val viewType: Int
get() = RowViewType.TITLE_GRID.ordinal
}
class BottomSheetTableSizeGridFragment : BottomSheetFragment(), RowRepresentableDataSource, RowRepresentableDelegate {
private lateinit var dataAdapter: RowRepresentableAdapter
private var defaultSize: Int? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initData()
initUI()
}
override fun onResume() {
super.onResume()
dataAdapter.notifyDataSetChanged()
}
override fun getValue(): Any? {
return defaultSize
}
/**
* Init data
*/
private fun initData() {
val bottomSheetData = getData()
if (bottomSheetData.isNotEmpty() && bottomSheetData.first().defaultValue != null) {
defaultSize = bottomSheetData.first().defaultValue as Int?
}
}
/**
* Init UI
*/
private fun initUI() {
setAddButtonVisible(false)
LayoutInflater.from(requireContext())
.inflate(net.pokeranalytics.android.R.layout.bottom_sheet_grid, view?.bottomSheetContainer, true)
val viewManager = GridLayoutManager(requireContext(), 3)
dataAdapter = RowRepresentableAdapter(this, this)
val spanCount = 3
val spacing = 2.px
val includeEdge = false
reyclerView.apply {
setHasFixedSize(true)
layoutManager = viewManager
adapter = dataAdapter
addItemDecoration(GridSpacingItemDecoration(spanCount, spacing, includeEdge))
}
}
override fun rowRepresentableForPosition(position: Int): RowRepresentable {
return TableSize.all[position]
}
override fun indexForRow(row: RowRepresentable): Int {
return TableSize.all.indexOf(row)
}
override fun numberOfRows(): Int {
return TableSize.all.size
}
override fun onRowSelected(row: RowRepresentable) {
bottomSheetDelegate.setValue((this.row as TableSize).numberOfPlayer, this.row)
dismiss()
}
override fun stringForRow(row: RowRepresentable): String {
this.context?.let {
return row.localizedTitle(it)
}
return "UNKNOWN CONTEXT FOR ROW $row"
}
}
/*
class BottomSheetTableSizeGridFragment : BottomSheetFragment() {
private var dataList: ArrayList<String> = ArrayList()
@ -79,4 +194,6 @@ class BottomSheetTableSizeGridFragment : BottomSheetFragment() {
}
}
}
*/

@ -29,11 +29,17 @@ interface Localizable {
}
}
interface RowRepresentable : Displayable {
fun getDisplayName(): String {
return "UNKNOWN NAME"
}
}
/**
* An interface used so that enums values can be represented visually
* as rows in RecyclerViews
*/
interface RowRepresentable : Localizable {
interface Displayable: Localizable {
/**
* The type of view associated with the row
*/

@ -2,12 +2,8 @@ package net.pokeranalytics.android.ui.view
import androidx.annotation.Nullable
import androidx.recyclerview.widget.DiffUtil
import net.pokeranalytics.android.ui.adapter.components.RowRepresentableDataSource
class RowRepresentableDiffCallback(
var newRows: List<RowRepresentable>, var oldRows: List<RowRepresentable>,
var rowRepresentableDataSource: RowRepresentableDataSource
) :
class RowRepresentableDiffCallback(var newRows: List<RowRepresentable>, var oldRows: List<RowRepresentable>) :
DiffUtil.Callback() {
override fun getOldListSize(): Int {

@ -3,12 +3,18 @@ package net.pokeranalytics.android.ui.view
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.AppCompatTextView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.row_bottom_sheet_grid_title.view.*
import kotlinx.android.synthetic.main.row_header_title_value.view.*
import kotlinx.android.synthetic.main.row_history_session.view.*
import kotlinx.android.synthetic.main.row_title.view.*
import kotlinx.android.synthetic.main.row_title_value.view.*
import kotlinx.android.synthetic.main.row_title_value_action.view.*
import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.ui.adapter.TableSizeGridAdapter
import net.pokeranalytics.android.ui.adapter.components.RowRepresentableDataSource
/**
@ -26,7 +32,11 @@ enum class RowViewType {
EDIT_TEXT,
TITLE,
TITLE_VALUE,
TITLE_VALUE_ACTION;
TITLE_VALUE_ACTION,
DATA,
BOTTOM_SHEET_DATA,
TITLE_GRID,
ROW_SESSION;
inner class FakeViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView),
BindableHolder {
@ -34,6 +44,35 @@ enum class RowViewType {
}
}
inner class RowSessionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView),
BindableHolder {
override fun bind(row: RowRepresentable, rowRepresentableDataSource: RowRepresentableDataSource?, listener: View.OnClickListener, actionListener: View.OnClickListener?) {
itemView.sessionRow.setData(row as Session)
itemView.sessionRow.setOnClickListener(listener)
}
}
inner class CellSessionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), BindableHolder {
override fun bind(row: RowRepresentable, rowRepresentableDataSource: RowRepresentableDataSource?, listener: View.OnClickListener, actionListener: View.OnClickListener?) {
itemView.title.text = row.localizedTitle(itemView.context)
itemView.container.setOnClickListener(listener)
}
}
inner class DataViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), BindableHolder {
override fun bind(row: RowRepresentable, rowRepresentableDataSource: RowRepresentableDataSource?, listener: View.OnClickListener, actionListener: View.OnClickListener?) {
itemView.findViewById<AppCompatTextView>(R.id.rowTitle_title).text = row.getDisplayName()
itemView.findViewById<ConstraintLayout>(R.id.rowTitle_container).setOnClickListener(listener)
}
}
inner class BottomSheetDataViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), BindableHolder {
override fun bind(row: RowRepresentable, rowRepresentableDataSource: RowRepresentableDataSource?, listener: View.OnClickListener, actionListener: View.OnClickListener?) {
itemView.findViewById<AppCompatTextView>(R.id.title).text = row.getDisplayName()
itemView.findViewById<ConstraintLayout>(R.id.container).setOnClickListener(listener)
}
}
inner class TitleViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView),
BindableHolder {
override fun bind(row: RowRepresentable, rowRepresentableDataSource: RowRepresentableDataSource?, listener: View.OnClickListener, actionListener: View.OnClickListener?) {
@ -115,7 +154,32 @@ enum class RowViewType {
false
)
)
DATA -> DataViewHolder(
LayoutInflater.from(parent.context).inflate(
R.layout.row_title,
parent,
false
)
)
BOTTOM_SHEET_DATA -> BottomSheetDataViewHolder(
LayoutInflater.from(parent.context).inflate(
R.layout.row_bottom_sheet_title,
parent,
false
)
)
TITLE_GRID -> CellSessionViewHolder(
LayoutInflater.from(parent.context).inflate(
R.layout.row_bottom_sheet_grid_title,
parent,
false)
)
ROW_SESSION -> RowSessionViewHolder(
LayoutInflater.from(parent.context).inflate(
R.layout.row_history_session,
parent,
false)
)
else -> FakeViewHolder(parent)
}
}

@ -10,6 +10,7 @@ import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.util.getDayNumber
import net.pokeranalytics.android.util.getShortDayName
import net.pokeranalytics.android.util.toCurrency
class SessionRowView : FrameLayout {
@ -55,17 +56,9 @@ class SessionRowView : FrameLayout {
rowHistorySession.dateDay.text = session.creationDate.getShortDayName()
rowHistorySession.dateNumber.text = session.creationDate.getDayNumber()
// TODO
var gameTitle = session.getGameTitle()
if (gameTitle.isEmpty()) {
gameTitle = "Game title here"
}
rowHistorySession.gameType.text = gameTitle
rowHistorySession.gameInfo.text = "Game info: duration, table size, ..."
rowHistorySession.gameResult.text = "$ 0"
rowHistorySession.gameType.text = session.getGameTitle()
rowHistorySession.gameInfo.text = session.getDuration(context)
rowHistorySession.gameResult.text = session.result?.netResult?.toCurrency() ?: "$0"
}

@ -1,5 +1,6 @@
package net.pokeranalytics.android.util
import android.content.Context
import android.content.res.Resources
import android.widget.Toast
import net.pokeranalytics.android.ui.activity.components.PokerAnalyticsActivity
@ -9,6 +10,8 @@ import java.text.DecimalFormat
import java.text.SimpleDateFormat
import java.util.*
// Sizes
val Int.dp: Int
get() = (this / Resources.getSystem().displayMetrics.density).toInt()
@ -55,6 +58,20 @@ fun Date.getDayNumber() : String {
fun Date.getShortDayName() : String {
return SimpleDateFormat("EE", Locale.getDefault()).format(this)
}
// Return the duration between two dates
fun Date.getDuration(context: Context, toDate: Date) : String {
val difference = (toDate.time - this.time).toInt()
val numOfDays = (difference / (1000 * 60 * 60 * 24))
val hours = (difference / (1000 * 60 * 60))
val minutes = (difference / (1000 * 60)) % 60
val seconds = (difference / 1000) % 60
val hoursStr = if (hours < 10) "0$hours" else "$hours"
val minutesStr = if (minutes < 10) "0$minutes" else "$minutes"
val secondsStr = if (seconds < 10) "0$seconds" else "$seconds"
return "$hoursStr:$minutesStr:$secondsStr"
}
// Toast

@ -1,21 +0,0 @@
package net.pokeranalytics.android.util.data
import androidx.lifecycle.LiveData
import io.realm.RealmChangeListener
import io.realm.RealmModel
import io.realm.RealmResults
class LiveRealmData<T : RealmModel>(private val results: RealmResults<T>) : LiveData<RealmResults<T>>() {
private val listener = RealmChangeListener<RealmResults<T>> { results -> value = results }
override fun onActive() {
results.addChangeListener(listener)
value = results
}
override fun onInactive() {
results.removeChangeListener(listener)
}
}

@ -1,11 +0,0 @@
@file:JvmName("RealmUtils") // pretty name for utils class if called from
package net.pokeranalytics.android.util.data
import io.realm.Realm
import io.realm.RealmModel
import io.realm.RealmResults
import net.pokeranalytics.android.model.realm.SessionDao
fun Realm.sessionDao(): SessionDao = SessionDao(this)
fun <T:RealmModel> RealmResults<T>.asLiveData() = LiveRealmData(this)

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/colorAccent" android:state_checked="true" />
<item android:color="@android:color/transparent" />
</selector>

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:app="http://schemas.android.com/apk/res-auto"
app:fontProviderAuthority="com.google.android.gms.fonts"
app:fontProviderPackage="com.google.android.gms"
app:fontProviderQuery="name=Roboto&amp;weight=500"
app:fontProviderCerts="@array/com_google_android_gms_fonts_certs">
</font-family>

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:app="http://schemas.android.com/apk/res-auto"
app:fontProviderAuthority="com.google.android.gms.fonts"
app:fontProviderPackage="com.google.android.gms"
app:fontProviderQuery="name=Roboto Mono&amp;weight=500"
app:fontProviderCerts="@array/com_google_android_gms_fonts_certs">
</font-family>

@ -2,16 +2,62 @@
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_height="240dp"
android:orientation="horizontal">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView1"
android:layout_width="80dp"
<FrameLayout
android:id="@+id/chips"
android:layout_width="0dp"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.chip.ChipGroup
android:id="@+id/chipGroup"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="horizontal"
app:chipSpacingHorizontal="16dp"
app:singleSelection="true">
<!--
<com.google.android.material.chip.Chip
android:id="@+id/chip1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="NL" />
<com.google.android.material.chip.Chip
android:id="@+id/chip2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="PL" />
<com.google.android.material.chip.Chip
android:id="@+id/chip3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="FL" />
<com.google.android.material.chip.Chip
android:id="@+id/chip4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="SL" />
<com.google.android.material.chip.Chip
android:id="@+id/chip5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="ML" />
-->
</com.google.android.material.chip.ChipGroup>
</FrameLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView2"
@ -19,7 +65,7 @@
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/recyclerView1"
app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/chips" />
</androidx.constraintlayout.widget.ConstraintLayout>

@ -1,74 +1,61 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
app:layout_behavior="@string/appbar_scrolling_view_behavior" android:id="@+id/nestedScrollView">
android:id="@+id/nestedScrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
android:id="@+id/recyclerView"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBar"
android:layout_width="match_parent"
android:layout_height="128dp"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
android:id="@+id/appBar"
android:layout_width="match_parent"
android:layout_height="128dp"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/collapsingToolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:contentScrim="?attr/colorPrimary"
app:expandedTitleGravity="bottom"
app:expandedTitleMarginStart="72dp"
app:expandedTitleTextAppearance="@style/PokerAnalyticsTheme.Toolbar.ExpandedTitleAppearance"
app:collapsedTitleTextAppearance="@style/PokerAnalyticsTheme.Toolbar.CollapsedTitleAppearance"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
android:id="@+id/collapsingToolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:collapsedTitleTextAppearance="@style/PokerAnalyticsTheme.Toolbar.CollapsedTitleAppearance"
app:contentScrim="?attr/colorPrimary"
app:expandedTitleGravity="bottom"
app:expandedTitleMarginStart="72dp"
app:expandedTitleTextAppearance="@style/PokerAnalyticsTheme.Toolbar.ExpandedTitleAppearance"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:title="Poker Analytics"
app:titleTextColor="@color/white"
app:layout_collapseMode="pin"/>
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin"
app:title="Poker Analytics"
app:titleTextColor="@color/white" />
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<Button
tools:text="Save"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/saveButton"
app:layout_anchorGravity="right|top"
app:layout_anchor="@+id/appBar"/>
<Button
tools:text="Delete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/deleteButton"
app:layout_anchorGravity="bottom|right"
app:layout_anchor="@+id/nestedScrollView"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

@ -15,16 +15,99 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/sessionData"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/gray_darker"
android:elevation="4dp"
android:translationY="-72dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:translationY="0dp">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/sinceTitle"
style="@style/PokerAnalyticsTheme.TextView.RowTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginBottom="16dp"
android:text="Since"
android:textAllCaps="true"
android:textSize="12sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/sinceValue"
style="@style/PokerAnalyticsTheme.TextView.RowTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginBottom="16dp"
android:text="-"
android:textSize="24sp"
app:fontFamily="@font/roboto_bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/sinceTitle" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/resultTitle"
style="@style/PokerAnalyticsTheme.TextView.RowTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:text="Result"
android:textAllCaps="true"
android:textSize="12sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/appCompatTextView"
style="@style/PokerAnalyticsTheme.TextView.RowTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:text="-"
android:textColor="@color/green"
android:textSize="28sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/resultTitle" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/state"
style="@style/PokerAnalyticsTheme.TextView.RowTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="PLAYING"
android:textColor="@color/colorAccent"
android:textSize="12sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="0dp"
android:layout_height="0dp"
android:paddingBottom="96dp"
android:clipToPadding="false"
android:paddingBottom="96dp"
android:translationY="-72dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintTop_toBottomOf="@+id/sessionData"
tools:translationY="0dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
@ -55,6 +138,7 @@
app:titleTextColor="@color/white"
tools:title="Poker Analytics" />
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
@ -63,10 +147,10 @@
android:id="@+id/bottomAppBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:fabAlignmentMode="center"
android:layout_gravity="bottom"
android:background="@color/red"
android:theme="@style/PokerAnalyticsTheme.BottomAppBar"
android:layout_gravity="bottom" />
app:fabAlignmentMode="center" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/floatingActionButton"

@ -9,10 +9,12 @@
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/dateDay"
style="@style/PokerAnalyticsTheme.TextView.SessionRow.Date"
android:layout_width="wrap_content"
android:layout_width="32dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:gravity="center"
android:textAllCaps="true"
app:fontFamily="@font/roboto_mono_medium"
app:layout_constraintBottom_toTopOf="@+id/dateNumber"
app:layout_constraintStart_toStartOf="@+id/guidelineStart"
app:layout_constraintTop_toTopOf="parent"
@ -22,9 +24,10 @@
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/dateNumber"
style="@style/PokerAnalyticsTheme.TextView.SessionRow.DateNumber"
android:layout_width="wrap_content"
android:layout_width="32dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@+id/dateDay"
app:layout_constraintHorizontal_bias="0.5"

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/delete"
android:icon="@drawable/ic_outline_delete"
android:title="@string/delete"
app:showAsAction="ifRoom" />
<item
android:id="@+id/save"
android:title="@string/save"
android:icon="@drawable/ic_check"
app:showAsAction="ifRoom" />
</menu>

@ -5,5 +5,7 @@
<item>@font/roboto_black</item>
<item>@font/roboto_bold</item>
<item>@font/roboto_light</item>
<item>@font/roboto_medium</item>
<item>@font/roboto_mono_medium</item>
</array>
</resources>

@ -2,7 +2,7 @@
<!-- PokerAnalytics application theme -->
<style name="PokerAnalyticsTheme" parent="Theme.MaterialComponents.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
@ -14,7 +14,8 @@
<item name="bottomAppBarStyle">@style/PokerAnalyticsTheme.BottomAppBar</item>
<item name="editTextStyle">@style/PokerAnalyticsTheme.EditText</item>
<item name="android:textViewStyle">@style/PokerAnalyticsTheme.TextView</item>
<item name="alertDialogTheme">@style/PokerAnalyticsTheme.AlertDialog</item>
<item name="chipStyle">@style/PokerAnalyticsTheme.Chip</item>
</style>
@ -105,24 +106,29 @@
<item name="android:maxLines">1</item>
<item name="android:ellipsize">end</item>
</style>
<style name="PokerAnalyticsTheme.TextView.SessionRow.Date">
<item name="android:textSize">12sp</item>
<item name="android:fontFamily">@font/roboto</item>r
<item name="android:fontFamily">@font/roboto</item>
</style>
<style name="PokerAnalyticsTheme.TextView.SessionRow.DateNumber">
<item name="android:textSize">18sp</item>
<item name="android:fontFamily">@font/roboto_black</item>
<item name="android:letterSpacing">0.05</item>
</style>
<style name="PokerAnalyticsTheme.TextView.SessionRow.Title">
<item name="android:textSize">16sp</item>
<item name="android:fontFamily">@font/roboto</item>
</style>
<style name="PokerAnalyticsTheme.TextView.SessionRow.Subtitle">
<item name="android:textSize">12sp</item>
<item name="android:fontFamily">@font/roboto</item>
<item name="android:textColor">@color/kaki_lighter</item>
</style>
<style name="PokerAnalyticsTheme.TextView.SessionRow.Result">
<item name="android:textSize">20sp</item>
<item name="android:fontFamily">@font/roboto_light</item>
@ -130,8 +136,15 @@
</style>
<!-- EditText -->
<!-- Alert Dialog -->
<style name="PokerAnalyticsTheme.AlertDialog" parent="Theme.MaterialComponents.Dialog">
<item name="colorAccent">@color/green</item>
<item name="android:textColorPrimary">@color/white</item>
<item name="android:background">@color/gray_dark</item>
</style>
<!-- EditText -->
<style name="PokerAnalyticsTheme.EditText" parent="Widget.AppCompat.EditText">
<item name="android:textColor">@color/white</item>
<item name="android:fontFamily">@font/roboto</item>
@ -147,5 +160,13 @@
<item name="android:fontFamily">@font/roboto_bold</item>
</style>
<!-- Chip -->
<style name="PokerAnalyticsTheme.Chip" parent="Widget.MaterialComponents.Chip.Choice">
<item name="chipBackgroundColor">@color/chips_background_states</item>
<item name="android:textColor">@color/white</item>
<item name="chipStrokeColor">@color/colorAccent</item>
<item name="chipStrokeWidth">1dp</item>
</style>
</resources>

Loading…
Cancel
Save