diff --git a/app/src/main/java/net/pokeranalytics/android/model/extensions/SessionExtensions.kt b/app/src/main/java/net/pokeranalytics/android/model/extensions/SessionExtensions.kt index 11c1ef4d..278ece74 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/extensions/SessionExtensions.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/extensions/SessionExtensions.kt @@ -16,17 +16,25 @@ enum class SessionState { * For example: STARTED, PAUSED, FINISHED */ fun Session.getState(): SessionState { + + if (timeFrame == null) { + return SessionState.PENDING + } + val endDate = timeFrame?.endDate - timeFrame?.startDate?.let {startDate -> - if (startDate > Date()) { - return SessionState.PENDING - } else if (endDate != null) { - return SessionState.FINISHED - } else if (isPaused) { - return SessionState.PAUSED - } else { - return SessionState.STARTED + timeFrame?.let {sessionTimeFrame -> + timeFrame?.startDate?.let {startDate -> + if (startDate > Date()) { + return SessionState.PENDING + } else if (endDate != null) { + return SessionState.FINISHED + } else if (sessionTimeFrame.paused) { + return SessionState.PAUSED + } else { + return SessionState.STARTED + } } } + return SessionState.INVALID } diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt index fc52bde3..c328cd12 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt @@ -7,6 +7,8 @@ import io.realm.annotations.PrimaryKey import net.pokeranalytics.android.R import net.pokeranalytics.android.calculus.SessionInterface import net.pokeranalytics.android.model.LiveData +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 @@ -88,17 +90,70 @@ open class Session : RealmObject(), SessionInterface, RowRepresentableDataSource // The features of the tournament, like Knockout, Shootout, Turbo... var tournamentFeatures: RealmList = RealmList() - var isPaused: Boolean = false + /** + * Start a session + */ + fun startSession() { + realm.executeTransaction { + when (getState()) { + SessionState.PENDING -> { + val sessionTimeFrame = this.timeFrame ?: realm.createObject(TimeFrame::class.java) + sessionTimeFrame.setDate(Date(), null) + this.timeFrame = sessionTimeFrame + } + SessionState.PAUSED -> { + this.timeFrame?.paused = false + } + else -> { + } + } + } + } - companion object { + /** + * Pause a session + */ + fun pauseSession() { + realm.executeTransaction { + when (getState()) { + SessionState.STARTED -> { + this.timeFrame?.paused = true + } + } + } + } + /** + * Stop a session + */ + fun stopSession() { + realm.executeTransaction { + when (getState()) { + SessionState.STARTED, SessionState.PAUSED -> { + this.timeFrame?.paused = false + this.timeFrame?.setDate(null, Date()) + } + } + } + } + + /** + * Delete the object from realm + * TODO: Cascade delete? + */ + fun delete() { + realm.executeTransaction { + deleteFromRealm() + } + } + + companion object { fun newInstance(): Session { var session: Session = Session() session.result = Result() session.timeFrame = TimeFrame() return session } - } @Ignore // SessionInterface value @@ -137,7 +192,7 @@ open class Session : RealmObject(), SessionInterface, RowRepresentableDataSource override fun adapterRows(): ArrayList { val rows = ArrayList() - rows.addAll(SessionRow.values()) + rows.addAll(SessionRow.getRowsForState(getState())) return rows } @@ -180,10 +235,22 @@ open class Session : RealmObject(), SessionInterface, RowRepresentableDataSource data.add(BottomSheetData(game, inputType = InputType.TYPE_NULL, data = LiveData.GAME.items(realm))) } SessionRow.LOCATION -> { - data.add(BottomSheetData(location, inputType = InputType.TYPE_NULL, data = LiveData.LOCATION.items(realm))) + data.add( + BottomSheetData( + location, + inputType = InputType.TYPE_NULL, + data = LiveData.LOCATION.items(realm) + ) + ) } SessionRow.BANKROLL -> { - data.add(BottomSheetData(bankroll, inputType = InputType.TYPE_NULL, data = LiveData.BANKROLL.items(realm))) + data.add( + BottomSheetData( + bankroll, + inputType = InputType.TYPE_NULL, + data = LiveData.BANKROLL.items(realm) + ) + ) } SessionRow.BLINDS -> { data.add(BottomSheetData(cgSmallBlind, R.string.smallblind, InputType.TYPE_CLASS_NUMBER)) @@ -199,14 +266,22 @@ open class Session : RealmObject(), SessionInterface, RowRepresentableDataSource override fun updateValue(value: Any?, row: RowRepresentable) { realm.beginTransaction() - when(row) { + when (row) { SessionRow.GAME -> game = value as Game? SessionRow.BANKROLL -> bankroll = value as Bankroll? SessionRow.LOCATION -> location = value as Location? SessionRow.COMMENT -> comment = value as String? ?: "" SessionRow.BLINDS -> if (value is ArrayList<*>) { - cgSmallBlind = try {(value[0] as String? ?: "0").toDouble()} catch (e:Exception) {null} - cgBigBlind = try {(value[1] as String? ?: "0").toDouble()} catch (e:Exception) {null} + cgSmallBlind = try { + (value[0] as String? ?: "0").toDouble() + } catch (e: Exception) { + null + } + cgBigBlind = try { + (value[1] as String? ?: "0").toDouble() + } catch (e: Exception) { + null + } } //TODO: Update SessionRow.START_DATE -> if (value is Date) { @@ -227,6 +302,7 @@ open class Session : RealmObject(), SessionInterface, RowRepresentableDataSource } realm.commitTransaction() } + } enum class TournamentKind { diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/TimeFrame.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/TimeFrame.kt index f443f44e..f5c55284 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/TimeFrame.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/TimeFrame.kt @@ -50,7 +50,7 @@ open class TimeFrame : RealmObject() { @Ignore var session: Session? = null - get() = this.sessions?.first() + //get() = this.sessions?.first() ?: null // Group @LinkingObjects("timeFrame") @@ -60,8 +60,12 @@ open class TimeFrame : RealmObject() { var set: SessionSet? = null get() = this.sets?.first() - fun setDate(startDate: Date, endDate: Date?) { - this.startDate = startDate + fun setDate(startDate: Date?, endDate: Date?) { + + startDate?.let { + this.startDate = startDate + } + this.endDate = endDate this.computeDuration() diff --git a/app/src/main/java/net/pokeranalytics/android/ui/activity/SessionActivity.kt b/app/src/main/java/net/pokeranalytics/android/ui/activity/SessionActivity.kt index e7d7810c..0017d653 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/activity/SessionActivity.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/activity/SessionActivity.kt @@ -10,16 +10,21 @@ import net.pokeranalytics.android.ui.fragment.SessionFragment class SessionActivity: PokerAnalyticsActivity() { + enum class IntentKey(val keyName : String) { + IS_TOURNAMENT("IS_TOURNAMENT"), + SESSION_ID("SESSION_ID"); + } + companion object { fun newInstance(context: Context, isTournament: Boolean? = false, sessionId: String? = "") { val intent = Intent(context, SessionActivity::class.java) isTournament?.let { - intent.putExtra("is_tournament", isTournament) + intent.putExtra(IntentKey.IS_TOURNAMENT.keyName, isTournament) } sessionId?.let { - intent.putExtra("session_id", sessionId) + intent.putExtra(IntentKey.SESSION_ID.keyName, sessionId) } context.startActivity(intent) @@ -38,8 +43,8 @@ class SessionActivity: PokerAnalyticsActivity() { * Init UI */ private fun initUI() { - val sessionId = intent.getStringExtra("session_id") - val isTournament = intent.getBooleanExtra("is_tournament", false) + val sessionId = intent.getStringExtra(IntentKey.SESSION_ID.keyName) + val isTournament = intent.getBooleanExtra(IntentKey.IS_TOURNAMENT.keyName, false) val fragment = newSessionFragment as SessionFragment fragment.setData(isTournament, sessionId) } diff --git a/app/src/main/java/net/pokeranalytics/android/ui/adapter/components/RowRepresentableAdapter.kt b/app/src/main/java/net/pokeranalytics/android/ui/adapter/components/RowRepresentableAdapter.kt index 429d6ae9..7106449e 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/adapter/components/RowRepresentableAdapter.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/adapter/components/RowRepresentableAdapter.kt @@ -85,4 +85,12 @@ class RowRepresentableAdapter(var rowRepresentableDataSource: RowRepresentableDa } } + /** + * Refresh all adapter rows + */ + fun refreshAllRows() { + this.rows = rowRepresentableDataSource.adapterRows() + notifyDataSetChanged() + } + } \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/SessionFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/SessionFragment.kt index 84fefb5f..a71988a4 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/SessionFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/SessionFragment.kt @@ -3,11 +3,14 @@ package net.pokeranalytics.android.ui.fragment import android.os.Bundle import android.view.* import android.widget.Toast +import androidx.interpolator.view.animation.FastOutSlowInInterpolator import androidx.recyclerview.widget.LinearLayoutManager import io.realm.kotlin.where import kotlinx.android.synthetic.main.fragment_session.* import net.pokeranalytics.android.R import net.pokeranalytics.android.model.LiveData +import net.pokeranalytics.android.model.extensions.SessionState +import net.pokeranalytics.android.model.extensions.getState import net.pokeranalytics.android.model.realm.Session import net.pokeranalytics.android.ui.activity.EditableDataActivity import net.pokeranalytics.android.ui.activity.components.PokerAnalyticsActivity @@ -20,12 +23,13 @@ import net.pokeranalytics.android.ui.fragment.components.bottomsheet.BottomSheet import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.SessionRow import net.pokeranalytics.android.util.toast +import timber.log.Timber import java.util.* class SessionFragment : PokerAnalyticsFragment(), RowRepresentableDelegate, BottomSheetDelegate { private lateinit var currentSession: Session - private lateinit var sessionAdapter : RowRepresentableAdapter + private lateinit var sessionAdapter: RowRepresentableAdapter override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater.inflate(R.layout.fragment_session, container, false) @@ -44,7 +48,10 @@ class SessionFragment : PokerAnalyticsFragment(), RowRepresentableDelegate, Bott override fun onOptionsItemSelected(item: MenuItem?): Boolean { when (item!!.itemId) { R.id.restart -> toast("Restard is clicked!") - R.id.delete -> toast("Delete is clicked!") + R.id.delete -> { + currentSession.delete() + activity?.finish() + } } return true } @@ -97,6 +104,10 @@ class SessionFragment : PokerAnalyticsFragment(), RowRepresentableDelegate, Bott private fun initUI() { val activity = activity as PokerAnalyticsActivity + + // Avoid a bug during setting the title + toolbar.title = "" + activity.setSupportActionBar(toolbar) activity.supportActionBar?.setDisplayHomeAsUpEnabled(true) setHasOptionsMenu(true) @@ -112,24 +123,79 @@ class SessionFragment : PokerAnalyticsFragment(), RowRepresentableDelegate, Bott bottomAppBar.menu.findItem(R.id.stop).isVisible = false bottomAppBar.setOnMenuItemClickListener { item -> - when(item.itemId) { - R.id.stop -> toast("Stop is clicked!") + when (item.itemId) { + R.id.stop -> { + currentSession.stopSession() + updateSessionUI() + } } false } + floatingActionButton.setExpanded(false) floatingActionButton.setOnClickListener { - floatingActionButton.setImageResource(R.drawable.ic_outline_pause) - bottomAppBar.menu.findItem(R.id.stop).isVisible = true + manageSessionState() + } + + } + + /** + * Update the UI with the session data + * Should be called after the initialization of the session + */ + private fun updateSessionUI() { + + when (currentSession.getState()) { + SessionState.PENDING -> { + floatingActionButton.setImageResource(R.drawable.ic_outline_play) + bottomAppBar.menu.findItem(R.id.stop).isVisible = false + } + SessionState.STARTED -> { + floatingActionButton.setImageResource(R.drawable.ic_outline_pause) + bottomAppBar.menu.findItem(R.id.stop).isVisible = true + } + SessionState.PAUSED -> { + floatingActionButton.setImageResource(R.drawable.ic_outline_play) + bottomAppBar.menu.findItem(R.id.stop).isVisible = true + } + SessionState.FINISHED -> { + bottomAppBar.menu.findItem(R.id.stop).isVisible = false + floatingActionButton.animate() + .scaleX(0f) + .scaleY(0f) + .alpha(0f) + .setInterpolator(FastOutSlowInInterpolator()) + .start() + } + } + + sessionAdapter.refreshAllRows() + } + + /** + * Update the state of the session (start / pause) + */ + private fun manageSessionState() { + when (currentSession.getState()) { + SessionState.PENDING -> { + currentSession.startSession() + } + SessionState.STARTED -> { + currentSession.pauseSession() + } + SessionState.PAUSED -> { + currentSession.startSession() + } } + updateSessionUI() } /** * Set fragment data */ fun setData(isTournament: Boolean, sessionId: String) { - toolbar.title = if (isTournament) "Tournament" else "Cash game" + toolbar.title = if (isTournament) getString(R.string.tournament) else getString(R.string.cash_game) val realm = getRealm() val sessionRealm = realm.where().equalTo("id", sessionId).findFirst() @@ -144,6 +210,8 @@ class SessionFragment : PokerAnalyticsFragment(), RowRepresentableDelegate, Bott sessionAdapter = RowRepresentableAdapter(currentSession, this) recyclerView.adapter = sessionAdapter + updateSessionUI() + Timber.d("Session state: ${currentSession.getState()}") } } \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/ui/view/RowRepresentable.kt b/app/src/main/java/net/pokeranalytics/android/ui/view/RowRepresentable.kt index 58157e5d..ea66210d 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/view/RowRepresentable.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/view/RowRepresentable.kt @@ -3,6 +3,7 @@ package net.pokeranalytics.android.ui.view import android.content.Context import net.pokeranalytics.android.R import net.pokeranalytics.android.model.LiveData +import net.pokeranalytics.android.model.extensions.SessionState import net.pokeranalytics.android.ui.fragment.components.bottomsheet.BottomSheetType /** @@ -33,7 +34,7 @@ interface RowRepresentable { * The type of view associated with the row */ val viewType: Int - get() { + get() { return 0 } @@ -52,32 +53,89 @@ interface RowRepresentable { } enum class SessionRow : RowRepresentable { + + DURATION, + NET_HOURLY_RATE, + BANKROLL_VARIATION, + + CASHED_OUT, + BUY_IN, + TIPS, + GAME, BLINDS, LOCATION, BANKROLL, + TABLE_SIZE, START_DATE, END_DATE, + + BREAK_TIME, COMMENT; + companion object { + /** + * Return the rows to display for the current session state + */ + fun getRowsForState(sessionState: SessionState): ArrayList { + return when (sessionState) { + SessionState.PENDING -> { + arrayListOf(GAME, BLINDS, LOCATION, BANKROLL, TABLE_SIZE, START_DATE, END_DATE) + } + SessionState.STARTED -> { + arrayListOf( + CASHED_OUT, BUY_IN, TIPS, + GAME, BLINDS, LOCATION, BANKROLL, TABLE_SIZE, START_DATE, END_DATE, BREAK_TIME, COMMENT + ) + } + SessionState.PAUSED -> { + arrayListOf( + CASHED_OUT, BUY_IN, TIPS, + GAME, BLINDS, LOCATION, BANKROLL, TABLE_SIZE, START_DATE, END_DATE, BREAK_TIME, COMMENT + ) + } + SessionState.FINISHED -> { + arrayListOf( + DURATION, NET_HOURLY_RATE, BANKROLL_VARIATION, + CASHED_OUT, BUY_IN, TIPS, + GAME, BLINDS, LOCATION, BANKROLL, TABLE_SIZE, START_DATE, END_DATE, BREAK_TIME, COMMENT + ) + } + else -> arrayListOf() + } + } + } + override val resId: Int? get() { return when (this) { - BLINDS -> R.string.blinds + + DURATION -> R.string.duration + NET_HOURLY_RATE -> R.string.hour_rate_without_pauses + BANKROLL_VARIATION -> R.string.bankroll_variation + + CASHED_OUT -> R.string.cashed_out + BUY_IN -> R.string.buyin + TIPS -> R.string.tips + GAME -> R.string.game + BLINDS -> R.string.blinds LOCATION -> R.string.location BANKROLL -> R.string.bankroll - COMMENT -> R.string.comment + TABLE_SIZE -> R.string.table_size START_DATE -> R.string.start_date END_DATE -> R.string.end_date + BREAK_TIME -> R.string.break_time + COMMENT -> R.string.comment } } override val viewType: Int get() { return when (this) { - BLINDS, GAME, BANKROLL, LOCATION, COMMENT -> RowViewType.TITLE_VALUE.ordinal - START_DATE, END_DATE -> RowViewType.TITLE_VALUE_ACTION.ordinal + DURATION, NET_HOURLY_RATE, BANKROLL_VARIATION, + CASHED_OUT, BUY_IN, TIPS, + GAME, BLINDS, LOCATION, BANKROLL, TABLE_SIZE, COMMENT, START_DATE, END_DATE, BREAK_TIME -> RowViewType.TITLE_VALUE.ordinal } } @@ -96,6 +154,7 @@ enum class SessionRow : RowRepresentable { enum class SimpleRow : RowRepresentable { NAME; + override val resId: Int? = R.string.name override val viewType: Int = RowViewType.TITLE_VALUE.ordinal override val bottomSheetType: BottomSheetType = BottomSheetType.EDIT_TEXT @@ -141,7 +200,7 @@ enum class TransactionTypeRow : RowRepresentable { enum class TournamentFeatureRow : RowRepresentable { } -enum class SettingRow: RowRepresentable { +enum class SettingRow : RowRepresentable { BANKROLL, GAME, LOCATION, @@ -161,7 +220,7 @@ enum class SettingRow: RowRepresentable { override val viewType: Int = RowViewType.TITLE.ordinal - override val relatedResultsRepresentable : LiveData? + override val relatedResultsRepresentable: LiveData? get() { return when (this) { BANKROLL -> LiveData.BANKROLL diff --git a/app/src/main/res/layout/fragment_session.xml b/app/src/main/res/layout/fragment_session.xml index 87e604d0..95e68273 100644 --- a/app/src/main/res/layout/fragment_session.xml +++ b/app/src/main/res/layout/fragment_session.xml @@ -19,6 +19,8 @@ android:id="@+id/recyclerView" android:layout_width="0dp" android:layout_height="0dp" + android:paddingBottom="96dp" + android:clipToPadding="false" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" @@ -57,11 +59,12 @@ - @@ -69,7 +72,9 @@ android:id="@+id/floatingActionButton" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:src="@drawable/ic_outline_play" - app:layout_anchor="@id/bottomAppBar" /> + android:layout_gravity="bottom|center" + android:layout_marginBottom="28dp" + android:elevation="16dp" + android:src="@drawable/ic_outline_play" /> \ No newline at end of file diff --git a/app/src/main/res/layout/row_title_value.xml b/app/src/main/res/layout/row_title_value.xml index 1aa21a3d..80517a70 100644 --- a/app/src/main/res/layout/row_title_value.xml +++ b/app/src/main/res/layout/row_title_value.xml @@ -6,8 +6,8 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?selectableItemBackground" - android:paddingStart="24dp" - android:paddingEnd="24dp"> + android:paddingStart="16dp" + android:paddingEnd="16dp"> 8dp - + + +