From 1c2a4d0a09189e2d7a785894fa374cd01f994b9b Mon Sep 17 00:00:00 2001 From: Laurent Date: Tue, 7 Jan 2020 13:37:27 +0100 Subject: [PATCH 001/399] First hand history commit --- .../android/model/handhistory/Builder.kt | 42 ++++++++++ .../model/handhistory/ComputedAction.kt | 18 +++++ .../android/model/realm/HandHistory.kt | 16 ---- .../android/model/realm/Session.kt | 1 + .../android/model/realm/handhistory/Action.kt | 32 ++++++++ .../android/model/realm/handhistory/Card.kt | 25 ++++++ .../model/realm/handhistory/HandHistory.kt | 76 +++++++++++++++++++ .../model/realm/handhistory/PlayerSetup.kt | 29 +++++++ 8 files changed, 223 insertions(+), 16 deletions(-) create mode 100644 app/src/main/java/net/pokeranalytics/android/model/handhistory/Builder.kt create mode 100644 app/src/main/java/net/pokeranalytics/android/model/handhistory/ComputedAction.kt delete mode 100644 app/src/main/java/net/pokeranalytics/android/model/realm/HandHistory.kt create mode 100644 app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/Action.kt create mode 100644 app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/Card.kt create mode 100644 app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt create mode 100644 app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/PlayerSetup.kt diff --git a/app/src/main/java/net/pokeranalytics/android/model/handhistory/Builder.kt b/app/src/main/java/net/pokeranalytics/android/model/handhistory/Builder.kt new file mode 100644 index 00000000..a0e11180 --- /dev/null +++ b/app/src/main/java/net/pokeranalytics/android/model/handhistory/Builder.kt @@ -0,0 +1,42 @@ +package net.pokeranalytics.android.model.handhistory + +import net.pokeranalytics.android.model.realm.handhistory.HandHistory + +class Builder { + + /*** + * The hand history + */ + private var handHistory: HandHistory + set(value) { + field = value + load() + } + + val actions: List = listOf() + + constructor() { + this.handHistory = HandHistory() + } + + constructor(handHistory: HandHistory) { + this.handHistory = handHistory + } + + private fun load() { + + var potSize = 0.0 + + // sorted actions + this.handHistory.actions.forEach { + + val computedAction = ComputedAction(it, potSize, 0.0) + } + + } + + private fun save() { + + } + +} \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/model/handhistory/ComputedAction.kt b/app/src/main/java/net/pokeranalytics/android/model/handhistory/ComputedAction.kt new file mode 100644 index 00000000..b68dfc4e --- /dev/null +++ b/app/src/main/java/net/pokeranalytics/android/model/handhistory/ComputedAction.kt @@ -0,0 +1,18 @@ +package net.pokeranalytics.android.model.handhistory + +import net.pokeranalytics.android.model.realm.handhistory.Action + +class ComputedAction(var action: Action, + var potSize: Double = 0.0, + var playerRemainingStack: Double = 0.0) { + + /*** + * The potsize is used: + * - in the replayer / video + * - in the list of actions by street + */ +// var potSize = 0.0 +// +// var playerRemainingStack = 0.0 + +} \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/HandHistory.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/HandHistory.kt deleted file mode 100644 index c65264f9..00000000 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/HandHistory.kt +++ /dev/null @@ -1,16 +0,0 @@ -package net.pokeranalytics.android.model.realm - -import io.realm.RealmObject -import io.realm.annotations.PrimaryKey -import java.util.* - - -open class HandHistory : RealmObject() { - - @PrimaryKey - var id = UUID.randomUUID().toString() - - // the date of the hand history - var date: Date = Date() - -} \ No newline at end of file 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 79bfee91..d342387a 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 @@ -26,6 +26,7 @@ import net.pokeranalytics.android.model.filter.Filterable import net.pokeranalytics.android.model.filter.QueryCondition import net.pokeranalytics.android.model.filter.QueryCondition.* import net.pokeranalytics.android.model.interfaces.* +import net.pokeranalytics.android.model.realm.handhistory.HandHistory import net.pokeranalytics.android.model.utils.SessionSetManager import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource import net.pokeranalytics.android.ui.adapter.UnmanagedRowRepresentableException diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/Action.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/Action.kt new file mode 100644 index 00000000..a948a258 --- /dev/null +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/Action.kt @@ -0,0 +1,32 @@ +package net.pokeranalytics.android.model.realm.handhistory + +import io.realm.RealmObject + +open class Action : RealmObject() { + + /*** + * The street of the action + */ + var street: Int = 0 + + /*** + * The index of the action + */ + var index: Int = 0 + + /*** + * The position of the user making the action + */ + var position: Int = 0 + + /*** + * The type of action: check, fold, raise... + */ + var type: Int = 0 + + /*** + * The amount linked for a bet, raise... + */ + var amount: Double? = null + +} \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/Card.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/Card.kt new file mode 100644 index 00000000..68637116 --- /dev/null +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/Card.kt @@ -0,0 +1,25 @@ +package net.pokeranalytics.android.model.realm.handhistory + +import io.realm.RealmObject + + +open class Card : RealmObject() { + + enum class Suit { + SPADES, + HEART, + DIAMOND, + CLOVER + } + + /*** + * The card value: 2..A + */ + var value: Int = 0 + + /*** + * The card suit: heart, spades... + */ + var suit: Int = 0 + +} \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt new file mode 100644 index 00000000..608783cc --- /dev/null +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt @@ -0,0 +1,76 @@ +package net.pokeranalytics.android.model.realm.handhistory + +import io.realm.RealmList +import io.realm.RealmObject +import io.realm.annotations.PrimaryKey +import net.pokeranalytics.android.model.realm.Session +import java.util.* + + +open class HandHistory : RealmObject() { + + @PrimaryKey + var id = UUID.randomUUID().toString() + + /*** + * The date of the hand history + */ + var date: Date = Date() + + /*** + * The session whose hand was played + */ + var session: Session? = null + + /*** + * The small blind + */ + var smallBlind: Double = 0.0 + + /*** + * The big blind + */ + var bigBlind: Double = 0.0 + + /*** + * The ante + */ + var ante: Double = 0.0 + + /*** + * Big blind ante + */ + var bigBlindAnte: Boolean = false + + /*** + * Number of players in the hand + */ + var numberOfPlayers: Int = 9 + + /*** + * Number of players in the hand + */ + var comment: String? = null + + /*** + * The position index of the hero + */ + var heroIndex: Int? = null + + /*** + * The board + */ + var board: RealmList = RealmList() + + /*** + * The players actions + */ + var actions: RealmList = RealmList() + + /*** + * A list of players initial data: user, position, hand, stack + */ + var playerSetups: RealmList = RealmList() + + +} \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/PlayerSetup.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/PlayerSetup.kt new file mode 100644 index 00000000..b29d772d --- /dev/null +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/PlayerSetup.kt @@ -0,0 +1,29 @@ +package net.pokeranalytics.android.model.realm.handhistory + +import io.realm.RealmList +import io.realm.RealmObject +import net.pokeranalytics.android.model.realm.Player + +open class PlayerSetup : RealmObject() { + + /*** + * The player + */ + var player: Player? = null + + /*** + * The position at the table: SB..BUTTON + */ + var position: Int = 0 + + /*** + * The initial stack of the player + */ + var stack: Double? = null + + /*** + * The cards of the player + */ + var cards: RealmList = RealmList() + +} From ef362bfa283f292cab4392e33b953966eeab5595 Mon Sep 17 00:00:00 2001 From: Laurent Date: Wed, 8 Jan 2020 16:38:59 +0100 Subject: [PATCH 002/399] Added creation of new hand, building of actions per street --- .../android/model/handhistory/Builder.kt | 27 ++- .../model/handhistory/ComputedAction.kt | 2 +- .../android/model/handhistory/HandSetup.kt | 15 ++ .../android/model/handhistory/Street.kt | 9 + .../android/model/realm/handhistory/Action.kt | 19 ++- .../model/realm/handhistory/HandHistory.kt | 154 +++++++++++------- 6 files changed, 155 insertions(+), 71 deletions(-) create mode 100644 app/src/main/java/net/pokeranalytics/android/model/handhistory/HandSetup.kt create mode 100644 app/src/main/java/net/pokeranalytics/android/model/handhistory/Street.kt diff --git a/app/src/main/java/net/pokeranalytics/android/model/handhistory/Builder.kt b/app/src/main/java/net/pokeranalytics/android/model/handhistory/Builder.kt index a0e11180..2d5fc8c0 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/handhistory/Builder.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/handhistory/Builder.kt @@ -13,10 +13,13 @@ class Builder { load() } - val actions: List = listOf() + val actionsPerStreet = hashMapOf>() + + constructor(handSetup: HandSetup) { + val handHistory = HandHistory() + handHistory.configure(handSetup) + this.handHistory = handHistory - constructor() { - this.handHistory = HandHistory() } constructor(handHistory: HandHistory) { @@ -26,11 +29,23 @@ class Builder { private fun load() { var potSize = 0.0 + var totalPotSize = 0.0 - // sorted actions - this.handHistory.actions.forEach { + // on veut sortir: + // une liste d'action pour générer des images / des animations => ImageRepresentable? + // une liste de lignes à afficher dans un tableau => RowRepresentable + // => ne pas oublier flop / turn / river / résumé - val computedAction = ComputedAction(it, potSize, 0.0) + // sorted actions + val sortedActions = this.handHistory.actions.sortedBy { it.index } + Street.values().forEach { street -> + val filteredActions = sortedActions.filter { it.street == street.ordinal } + val computedActions = filteredActions.map { action -> + totalPotSize += action.effectiveAmount + ComputedAction(action, potSize, action.positionRemainingStack) + } + this.actionsPerStreet[street] = computedActions + potSize = totalPotSize } } diff --git a/app/src/main/java/net/pokeranalytics/android/model/handhistory/ComputedAction.kt b/app/src/main/java/net/pokeranalytics/android/model/handhistory/ComputedAction.kt index b68dfc4e..9c8cdb69 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/handhistory/ComputedAction.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/handhistory/ComputedAction.kt @@ -4,7 +4,7 @@ import net.pokeranalytics.android.model.realm.handhistory.Action class ComputedAction(var action: Action, var potSize: Double = 0.0, - var playerRemainingStack: Double = 0.0) { + var playerRemainingStack: Double? = null) { /*** * The potsize is used: diff --git a/app/src/main/java/net/pokeranalytics/android/model/handhistory/HandSetup.kt b/app/src/main/java/net/pokeranalytics/android/model/handhistory/HandSetup.kt new file mode 100644 index 00000000..a5e09487 --- /dev/null +++ b/app/src/main/java/net/pokeranalytics/android/model/handhistory/HandSetup.kt @@ -0,0 +1,15 @@ +package net.pokeranalytics.android.model.handhistory + +class HandSetup { + + var smallBlind: Double? = null + + var bigBlind: Double? = null + + var ante: Double? = null + + var tableSize: Int? = null + + var bigBlindAnte: Boolean = false + +} \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/model/handhistory/Street.kt b/app/src/main/java/net/pokeranalytics/android/model/handhistory/Street.kt new file mode 100644 index 00000000..b4120df6 --- /dev/null +++ b/app/src/main/java/net/pokeranalytics/android/model/handhistory/Street.kt @@ -0,0 +1,9 @@ +package net.pokeranalytics.android.model.handhistory + +enum class Street { + PREFLOP, + FLOP, + TURN, + RIVER +} + diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/Action.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/Action.kt index a948a258..9e91cead 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/Action.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/Action.kt @@ -4,6 +4,18 @@ import io.realm.RealmObject open class Action : RealmObject() { + enum class Type { + POST_SB, + POST_BB, + CHECK, + CALL, + BET, + RAISE, + CALL_ALLIN, + BET_ALLIN, + RAISE_ALLIN + } + /*** * The street of the action */ @@ -22,11 +34,16 @@ open class Action : RealmObject() { /*** * The type of action: check, fold, raise... */ - var type: Int = 0 + var type: Int? = null /*** * The amount linked for a bet, raise... */ var amount: Double? = null + var effectiveAmount: Double = 0.0 + private set + + var positionRemainingStack: Double? = null + } \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt index 608783cc..fe060d2e 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt @@ -3,74 +3,102 @@ package net.pokeranalytics.android.model.realm.handhistory import io.realm.RealmList import io.realm.RealmObject import io.realm.annotations.PrimaryKey +import net.pokeranalytics.android.model.handhistory.HandSetup import net.pokeranalytics.android.model.realm.Session import java.util.* open class HandHistory : RealmObject() { - @PrimaryKey - var id = UUID.randomUUID().toString() - - /*** - * The date of the hand history - */ - var date: Date = Date() - - /*** - * The session whose hand was played - */ - var session: Session? = null - - /*** - * The small blind - */ - var smallBlind: Double = 0.0 - - /*** - * The big blind - */ - var bigBlind: Double = 0.0 - - /*** - * The ante - */ - var ante: Double = 0.0 - - /*** - * Big blind ante - */ - var bigBlindAnte: Boolean = false - - /*** - * Number of players in the hand - */ - var numberOfPlayers: Int = 9 - - /*** - * Number of players in the hand - */ - var comment: String? = null - - /*** - * The position index of the hero - */ - var heroIndex: Int? = null - - /*** - * The board - */ - var board: RealmList = RealmList() - - /*** - * The players actions - */ - var actions: RealmList = RealmList() - - /*** - * A list of players initial data: user, position, hand, stack - */ - var playerSetups: RealmList = RealmList() - + @PrimaryKey + var id = UUID.randomUUID().toString() + + /*** + * The date of the hand history + */ + var date: Date = Date() + + /*** + * The session whose hand was played + */ + var session: Session? = null + + /*** + * The small blind + */ + var smallBlind: Double = 0.0 + + /*** + * The big blind + */ + var bigBlind: Double = 0.0 + + /*** + * The ante + */ + var ante: Double = 0.0 + + /*** + * Big blind ante + */ + var bigBlindAnte: Boolean = false + + /*** + * Number of players in the hand + */ + var numberOfPlayers: Int = 9 + + /*** + * Number of players in the hand + */ + var comment: String? = null + + /*** + * The position index of the hero + */ + var heroIndex: Int? = null + + /*** + * The board + */ + var board: RealmList = RealmList() + + /*** + * The players actions + */ + var actions: RealmList = RealmList() + + /*** + * A list of players initial data: user, position, hand, stack + */ + var playerSetups: RealmList = RealmList() + + fun configure(handSetup: HandSetup) { + + handSetup.tableSize?.let { this.numberOfPlayers = it } + handSetup.smallBlind?.let { this.smallBlind = it } + handSetup.bigBlind?.let { this.bigBlind = it } + + for (i in 0..this.numberOfPlayers) { + val action = Action() + action.index = i + action.position = i + when (i) { + 0 -> { + action.type = Action.Type.POST_SB.ordinal + action.amount = this.smallBlind + } + 1 -> { + action.type = Action.Type.POST_BB.ordinal + action.amount = this.bigBlind + } + else -> { + + } + } + this.actions.add(action) + } + + } } \ No newline at end of file From cf3a8b2f1643c262b786218e6b2a2e3023697f9d Mon Sep 17 00:00:00 2001 From: Laurent Date: Wed, 8 Jan 2020 17:46:43 +0100 Subject: [PATCH 003/399] Adds MMediaMuxer to assemble images into video --- .../android/model/handhistory/MMediaMuxer.kt | 395 ++++++++++++++++++ 1 file changed, 395 insertions(+) create mode 100644 app/src/main/java/net/pokeranalytics/android/model/handhistory/MMediaMuxer.kt diff --git a/app/src/main/java/net/pokeranalytics/android/model/handhistory/MMediaMuxer.kt b/app/src/main/java/net/pokeranalytics/android/model/handhistory/MMediaMuxer.kt new file mode 100644 index 00000000..f7de2365 --- /dev/null +++ b/app/src/main/java/net/pokeranalytics/android/model/handhistory/MMediaMuxer.kt @@ -0,0 +1,395 @@ +package net.pokeranalytics.android.model.handhistory + +import android.app.Activity +import android.app.ProgressDialog +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.media.* +import android.media.MediaCodecInfo.CodecCapabilities +import android.os.Environment +import android.os.Handler +import android.util.Log +import java.io.File +import java.io.IOException +import java.text.DateFormat +import java.util.* + + +class MMediaMuxer { + + private var mediaCodec: MediaCodec? = null + private var mediaMuxer: MediaMuxer? = null + private var mRunning = false + private var generateIndex = 0 + private var mTrackIndex = 0 + private var MAX_FRAME_VIDEO = 0 + private var bitList: MutableList? = null + private var bitFirst: MutableList? = null + private var bitLast: MutableList? = null + private var current_index_frame = 0 + private var outputPath: String? = null + private var _activity: Activity? = null + private var pd: ProgressDialog? = null + private var _title: String? = null + private var _mess: String? = null + + fun Init( + activity: Activity?, + width: Int, + height: Int, + title: String?, + mess: String? + ) { + _title = title + _mess = mess + _activity = activity + _width = width + _height = height + Logd("MMediaMuxer Init") + ShowProgressBar() + } + + private val aHandler = Handler() + + fun AddFrame(byteFrame: ByteArray) { + CheckDataListState() + Thread(Runnable { + Logd("Android get Frame") + val bit = BitmapFactory.decodeByteArray(byteFrame, 0, byteFrame.size) + Logd("Android convert Bitmap") + val byteConvertFrame = + getNV21(bit.width, bit.height, bit) + Logd("Android convert getNV21") + bitList!!.add(byteConvertFrame) + }).start() + } + + fun AddFrame(byteFrame: ByteArray, count: Int, isLast: Boolean) { + var byteFrame = byteFrame + CheckDataListState() + Logd("Android get Frames = $count") + val bit = BitmapFactory.decodeByteArray(byteFrame, 0, byteFrame.size) + Logd("Android convert Bitmap") + byteFrame = getNV21(bit.width, bit.height, bit) + Logd("Android convert getNV21") + for (i in 0 until count) { + if (isLast) { + bitLast!!.add(byteFrame) + } else { + bitFirst!!.add(byteFrame) + } + } + } + + fun CreateVideo() { + current_index_frame = 0 + Logd("Prepare Frames Data") + bitFirst!!.addAll(bitList!!) + bitFirst!!.addAll(bitLast!!) + MAX_FRAME_VIDEO = bitFirst!!.size + Logd("CreateVideo") + mRunning = true + bufferEncoder() + } + + fun GetStateEncoder(): Boolean { + return mRunning + } + + fun GetPath(): String? { + return outputPath + } + + fun onBackPressed() { + mRunning = false + } + + fun ShowProgressBar() { + _activity!!.runOnUiThread { + pd = ProgressDialog(_activity) + pd!!.setTitle(_title) + pd!!.setCancelable(false) + pd!!.setMessage(_mess) + pd!!.setCanceledOnTouchOutside(false) + pd!!.show() + } + } + + fun HideProgressBar() { + Thread(Runnable { _activity!!.runOnUiThread { pd!!.dismiss() } }).start() + } + + private fun bufferEncoder() { + val runnable = Runnable { + try { + Logd("PrepareEncoder start") + PrepareEncoder() + Logd("PrepareEncoder end") + } catch (e: IOException) { + Loge(e.message) + } + try { + while (mRunning) { + Encode() + } + } finally { + Logd("release") + Release() + HideProgressBar() + bitFirst = null + bitLast = null + } + } + val thread = Thread(runnable) + thread.start() + } + + fun ClearTask() { + bitList = null + bitFirst = null + bitLast = null + } + + @Throws(IOException::class) + private fun PrepareEncoder() { + val codecInfo = + selectCodec(MIME_TYPE) + if (codecInfo == null) { + Loge("Unable to find an appropriate codec for $MIME_TYPE") + } + Logd("found codec: " + codecInfo!!.name) + val colorFormat: Int + colorFormat = try { + selectColorFormat(codecInfo, MIME_TYPE) + } catch (e: Exception) { + CodecCapabilities.COLOR_FormatYUV420SemiPlanar + } + mediaCodec = MediaCodec.createByCodecName(codecInfo.name) + val mediaFormat = MediaFormat.createVideoFormat( + MIME_TYPE, + _width, + _height + ) + mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE) + mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE) + mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat) + mediaFormat.setInteger( + MediaFormat.KEY_I_FRAME_INTERVAL, + INFLAME_INTERVAL + ) + mediaCodec?.let { + it.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE) + it.start() + } + try { + val currentDateTimeString = + DateFormat.getDateTimeInstance().format(Date()) + outputPath = File( + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES), + "pixel$currentDateTimeString.mp4" + ).toString() + mediaMuxer = MediaMuxer(outputPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4) + } catch (ioe: IOException) { + Loge("MediaMuxer creation failed") + } + } + + private fun Encode() { + while (true) { + if (!mRunning) { + break + } + Logd("Encode start") + val TIMEOUT_USEC: Long = 5000 + val inputBufIndex = mediaCodec!!.dequeueInputBuffer(TIMEOUT_USEC) + val ptsUsec = + computePresentationTime(generateIndex.toLong(), FRAME_RATE) + if (inputBufIndex >= 0) { + val input = bitFirst!![current_index_frame] + val inputBuffer = mediaCodec!!.getInputBuffer(inputBufIndex) + inputBuffer.clear() + inputBuffer.put(input) + mediaCodec!!.queueInputBuffer(inputBufIndex, 0, input.size, ptsUsec, 0) + generateIndex++ + } + val mBufferInfo = MediaCodec.BufferInfo() + val encoderStatus = mediaCodec!!.dequeueOutputBuffer(mBufferInfo, TIMEOUT_USEC) + if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) { // no output available yet + Loge("No output from encoder available") + } else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { // not expected for an encoder + val newFormat = mediaCodec!!.outputFormat + mTrackIndex = mediaMuxer!!.addTrack(newFormat) + mediaMuxer!!.start() + } else if (encoderStatus < 0) { + Loge("unexpected result from encoder.dequeueOutputBuffer: $encoderStatus") + } else if (mBufferInfo.size != 0) { + val encodedData = mediaCodec!!.getOutputBuffer(encoderStatus) + if (encodedData == null) { + Loge("encoderOutputBuffer $encoderStatus was null") + } else { + encodedData.position(mBufferInfo.offset) + encodedData.limit(mBufferInfo.offset + mBufferInfo.size) + mediaMuxer!!.writeSampleData(mTrackIndex, encodedData, mBufferInfo) + mediaCodec!!.releaseOutputBuffer(encoderStatus, false) + } + } + current_index_frame++ + if (current_index_frame > MAX_FRAME_VIDEO - 1) { + Log.d(TAG, "mRunning = false;") + mRunning = false + } + Logd("Encode end") + } + } + + private fun Release() { + if (mediaCodec != null) { + mediaCodec!!.stop() + mediaCodec!!.release() + mediaCodec = null + Logd("RELEASE CODEC") + } + if (mediaMuxer != null) { + mediaMuxer!!.stop() + mediaMuxer!!.release() + mediaMuxer = null + Logd("RELEASE MUXER") + } + } + + private fun getNV21(inputWidth: Int, inputHeight: Int, scaled: Bitmap): ByteArray { + val argb = IntArray(inputWidth * inputHeight) + scaled.getPixels(argb, 0, inputWidth, 0, 0, inputWidth, inputHeight) + val yuv = ByteArray(inputWidth * inputHeight * 3 / 2) + encodeYUV420SP(yuv, argb, inputWidth, inputHeight) + scaled.recycle() + return yuv + } + + private fun encodeYUV420SP( + yuv420sp: ByteArray, + argb: IntArray, + width: Int, + height: Int + ) { + val frameSize = width * height + var yIndex = 0 + var uvIndex = frameSize + var a: Int + var R: Int + var G: Int + var B: Int + var Y: Int + var U: Int + var V: Int + var index = 0 + for (j in 0 until height) { + for (i in 0 until width) { + a = argb[index] and -0x1000000 shr 24 // a is not used obviously + R = argb[index] and 0xff0000 shr 16 + G = argb[index] and 0xff00 shr 8 + B = argb[index] and 0xff shr 0 + Y = (66 * R + 129 * G + 25 * B + 128 shr 8) + 16 + U = (-38 * R - 74 * G + 112 * B + 128 shr 8) + 128 + V = (112 * R - 94 * G - 18 * B + 128 shr 8) + 128 + yuv420sp[yIndex++] = + (if (Y < 0) 0 else if (Y > 255) 255 else Y).toByte() + if (j % 2 == 0 && index % 2 == 0) { + yuv420sp[uvIndex++] = + (if (U < 0) 0 else if (U > 255) 255 else U).toByte() + yuv420sp[uvIndex++] = + (if (V < 0) 0 else if (V > 255) 255 else V).toByte() + } + index++ + } + } + } + + private fun CheckDataListState() { + if (bitList == null) { + bitList = ArrayList() + } + if (bitFirst == null) { + bitFirst = ArrayList() + } + if (bitLast == null) { + bitLast = ArrayList() + } + } + + private fun computePresentationTime(frameIndex: Long, framerate: Int): Long { + return 132 + frameIndex * 1000000 / framerate + } + + companion object { + private const val MIME_TYPE = "video/avc" // H.264 Advanced Video Coding + private var _width = 512 + private var _height = 512 + private const val BIT_RATE = 800000 + private const val INFLAME_INTERVAL = 1 + private const val FRAME_RATE = 10 + private const val DEBUG = false + private const val TAG = "CODEC" + /** + * Returns the first codec capable of encoding the specified MIME type, or + * null if no match was found. + */ + private fun selectCodec(mimeType: String): MediaCodecInfo? { + val numCodecs = MediaCodecList.getCodecCount() + for (i in 0 until numCodecs) { + val codecInfo = MediaCodecList.getCodecInfoAt(i) + if (!codecInfo.isEncoder) { + continue + } + val types = codecInfo.supportedTypes + for (j in types.indices) { + if (types[j].equals(mimeType, ignoreCase = true)) { + return codecInfo + } + } + } + return null + } + + /** + * Returns a color format that is supported by the codec and by this test + * code. If no match is found, this throws a test failure -- the set of + * formats known to the test should be expanded for new platforms. + */ + private fun selectColorFormat( + codecInfo: MediaCodecInfo, + mimeType: String + ): Int { + val capabilities = codecInfo + .getCapabilitiesForType(mimeType) + for (i in capabilities.colorFormats.indices) { + val colorFormat = capabilities.colorFormats[i] + if (isRecognizedFormat(colorFormat)) { + return colorFormat + } + } + return 0 // not reached + } + + /** + * Returns true if this is a color format that this test code understands + * (i.e. we know how to read and generate frames in this format). + */ + private fun isRecognizedFormat(colorFormat: Int): Boolean { + return when (colorFormat) { + CodecCapabilities.COLOR_FormatYUV420Planar, CodecCapabilities.COLOR_FormatYUV420PackedPlanar, CodecCapabilities.COLOR_FormatYUV420SemiPlanar, CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar, CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar -> true + else -> false + } + } + + private fun Logd(Mess: String) { + if (DEBUG) { + Log.d(TAG, Mess) + } + } + + private fun Loge(Mess: String?) { + Log.e(TAG, Mess) + } + } +} \ No newline at end of file From fb56773f643bb1775f9aea6e546d2e7b0673bec1 Mon Sep 17 00:00:00 2001 From: Laurent Date: Thu, 9 Jan 2020 10:00:49 +0100 Subject: [PATCH 004/399] Moved MediaMuxer class --- .../handhistory => util/video}/MMediaMuxer.kt | 56 ++++++++++++++----- 1 file changed, 42 insertions(+), 14 deletions(-) rename app/src/main/java/net/pokeranalytics/android/{model/handhistory => util/video}/MMediaMuxer.kt (92%) diff --git a/app/src/main/java/net/pokeranalytics/android/model/handhistory/MMediaMuxer.kt b/app/src/main/java/net/pokeranalytics/android/util/video/MMediaMuxer.kt similarity index 92% rename from app/src/main/java/net/pokeranalytics/android/model/handhistory/MMediaMuxer.kt rename to app/src/main/java/net/pokeranalytics/android/util/video/MMediaMuxer.kt index f7de2365..d131a478 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/handhistory/MMediaMuxer.kt +++ b/app/src/main/java/net/pokeranalytics/android/util/video/MMediaMuxer.kt @@ -1,4 +1,4 @@ -package net.pokeranalytics.android.model.handhistory +package net.pokeranalytics.android.util.video import android.app.Activity import android.app.ProgressDialog @@ -122,18 +122,26 @@ class MMediaMuxer { private fun bufferEncoder() { val runnable = Runnable { try { - Logd("PrepareEncoder start") + Logd( + "PrepareEncoder start" + ) PrepareEncoder() - Logd("PrepareEncoder end") + Logd( + "PrepareEncoder end" + ) } catch (e: IOException) { - Loge(e.message) + Loge( + e.message + ) } try { while (mRunning) { Encode() } } finally { - Logd("release") + Logd( + "release" + ) Release() HideProgressBar() bitFirst = null @@ -153,14 +161,19 @@ class MMediaMuxer { @Throws(IOException::class) private fun PrepareEncoder() { val codecInfo = - selectCodec(MIME_TYPE) + selectCodec( + MIME_TYPE + ) if (codecInfo == null) { Loge("Unable to find an appropriate codec for $MIME_TYPE") } Logd("found codec: " + codecInfo!!.name) val colorFormat: Int colorFormat = try { - selectColorFormat(codecInfo, MIME_TYPE) + selectColorFormat( + codecInfo, + MIME_TYPE + ) } catch (e: Exception) { CodecCapabilities.COLOR_FormatYUV420SemiPlanar } @@ -170,8 +183,12 @@ class MMediaMuxer { _width, _height ) - mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE) - mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE) + mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, + BIT_RATE + ) + mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, + FRAME_RATE + ) mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat) mediaFormat.setInteger( MediaFormat.KEY_I_FRAME_INTERVAL, @@ -203,7 +220,9 @@ class MMediaMuxer { val TIMEOUT_USEC: Long = 5000 val inputBufIndex = mediaCodec!!.dequeueInputBuffer(TIMEOUT_USEC) val ptsUsec = - computePresentationTime(generateIndex.toLong(), FRAME_RATE) + computePresentationTime(generateIndex.toLong(), + FRAME_RATE + ) if (inputBufIndex >= 0) { val input = bitFirst!![current_index_frame] val inputBuffer = mediaCodec!!.getInputBuffer(inputBufIndex) @@ -215,17 +234,23 @@ class MMediaMuxer { val mBufferInfo = MediaCodec.BufferInfo() val encoderStatus = mediaCodec!!.dequeueOutputBuffer(mBufferInfo, TIMEOUT_USEC) if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) { // no output available yet - Loge("No output from encoder available") + Loge( + "No output from encoder available" + ) } else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { // not expected for an encoder val newFormat = mediaCodec!!.outputFormat mTrackIndex = mediaMuxer!!.addTrack(newFormat) mediaMuxer!!.start() } else if (encoderStatus < 0) { - Loge("unexpected result from encoder.dequeueOutputBuffer: $encoderStatus") + Loge( + "unexpected result from encoder.dequeueOutputBuffer: $encoderStatus" + ) } else if (mBufferInfo.size != 0) { val encodedData = mediaCodec!!.getOutputBuffer(encoderStatus) if (encodedData == null) { - Loge("encoderOutputBuffer $encoderStatus was null") + Loge( + "encoderOutputBuffer $encoderStatus was null" + ) } else { encodedData.position(mBufferInfo.offset) encodedData.limit(mBufferInfo.offset + mBufferInfo.size) @@ -364,7 +389,10 @@ class MMediaMuxer { .getCapabilitiesForType(mimeType) for (i in capabilities.colorFormats.indices) { val colorFormat = capabilities.colorFormats[i] - if (isRecognizedFormat(colorFormat)) { + if (isRecognizedFormat( + colorFormat + ) + ) { return colorFormat } } From b942c12d123c25b3a3d53e1e8584344db0fc1414 Mon Sep 17 00:00:00 2001 From: Laurent Date: Thu, 9 Jan 2020 12:17:11 +0100 Subject: [PATCH 005/399] Adds algorithm to determine the user available actions --- .../android/model/handhistory/Builder.kt | 57 ++++++++++++++++++- .../model/handhistory/ComputedAction.kt | 1 + .../android/model/realm/handhistory/Action.kt | 24 +++++++- .../model/realm/handhistory/HandHistory.kt | 4 +- 4 files changed, 80 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/net/pokeranalytics/android/model/handhistory/Builder.kt b/app/src/main/java/net/pokeranalytics/android/model/handhistory/Builder.kt index 2d5fc8c0..ca2f941e 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/handhistory/Builder.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/handhistory/Builder.kt @@ -1,5 +1,7 @@ package net.pokeranalytics.android.model.handhistory +import net.pokeranalytics.android.exceptions.PAIllegalStateException +import net.pokeranalytics.android.model.realm.handhistory.Action import net.pokeranalytics.android.model.realm.handhistory.HandHistory class Builder { @@ -13,7 +15,11 @@ class Builder { load() } - val actionsPerStreet = hashMapOf>() + private val actionsPerStreet = hashMapOf>() + private val sortedActions: List + get() { + return actionsPerStreet.flatMap { it.value } + } constructor(handSetup: HandSetup) { val handHistory = HandHistory() @@ -42,12 +48,59 @@ class Builder { val filteredActions = sortedActions.filter { it.street == street.ordinal } val computedActions = filteredActions.map { action -> totalPotSize += action.effectiveAmount - ComputedAction(action, potSize, action.positionRemainingStack) + ComputedAction(action, potSize, totalPotSize, action.positionRemainingStack) } this.actionsPerStreet[street] = computedActions potSize = totalPotSize } + } + + private fun availableActions(index: Int) : Set { + + val lastSignificantAction: ComputedAction? = getLastSignificantAction(index) + val lastUserAction: ComputedAction? = getLastUserAction(index) + + return if (lastSignificantAction != null) { + + when (lastSignificantAction.action.type) { + Action.Type.POST_BB, Action.Type.STRADDLE -> setOf(Action.Type.STRADDLE, Action.Type.FOLD, Action.Type.CALL, Action.Type.BET, Action.Type.BET_ALLIN) + Action.Type.BET, Action.Type.RAISE, Action.Type.RAISE_ALLIN, Action.Type.BET_ALLIN -> { + + val remainingStack = lastUserAction?.playerRemainingStack + val actionAmount = lastSignificantAction.action.amount + + if (remainingStack != null && actionAmount != null && remainingStack <= actionAmount) { + setOf(Action.Type.FOLD, Action.Type.CALL_ALLIN) + } else { + setOf(Action.Type.FOLD, Action.Type.CALL, Action.Type.RAISE, Action.Type.RAISE_ALLIN) + } + } + else -> { + throw PAIllegalStateException("We should not handle this action: ${lastSignificantAction.action.type}") + } + } + + } else { + setOf(Action.Type.CHECK, Action.Type.BET, Action.Type.BET_ALLIN) + } + + } + + private fun selectAction(index: Int, actionType: Action.Type) { + // remove everything after + } + + private fun getLastUserAction(index: Int): ComputedAction? { + val computedAction = this.sortedActions.first { it.action.index == index } + val action = computedAction.action + + val previousActions = this.sortedActions.drop(index) + return previousActions.lastOrNull { it.action.position == action.position } + } + private fun getLastSignificantAction(index: Int): ComputedAction? { + val previousActions = this.sortedActions.drop(index) + return previousActions.lastOrNull { it.action.isActionSignificant } } private fun save() { diff --git a/app/src/main/java/net/pokeranalytics/android/model/handhistory/ComputedAction.kt b/app/src/main/java/net/pokeranalytics/android/model/handhistory/ComputedAction.kt index 9c8cdb69..4620e052 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/handhistory/ComputedAction.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/handhistory/ComputedAction.kt @@ -4,6 +4,7 @@ import net.pokeranalytics.android.model.realm.handhistory.Action class ComputedAction(var action: Action, var potSize: Double = 0.0, + var totalPotSize: Double = 0.0, var playerRemainingStack: Double? = null) { /*** diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/Action.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/Action.kt index 9e91cead..5f9d4ebc 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/Action.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/Action.kt @@ -7,13 +7,23 @@ open class Action : RealmObject() { enum class Type { POST_SB, POST_BB, + STRADDLE, + FOLD, CHECK, CALL, BET, RAISE, CALL_ALLIN, BET_ALLIN, - RAISE_ALLIN + RAISE_ALLIN; + + val isSignificant: Boolean + get() { + return when (this) { + POST_BB, STRADDLE, BET, RAISE, BET_ALLIN, RAISE_ALLIN -> true + else -> false + } + } } /*** @@ -34,7 +44,12 @@ open class Action : RealmObject() { /*** * The type of action: check, fold, raise... */ - var type: Int? = null + var typeIdentifier: Int? = null + + val type: Type? + get() { + return typeIdentifier?.let { Type.values()[it] } + } /*** * The amount linked for a bet, raise... @@ -46,4 +61,9 @@ open class Action : RealmObject() { var positionRemainingStack: Double? = null + val isActionSignificant: Boolean + get() { + return this.type?.isSignificant ?: false + } + } \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt index fe060d2e..c1a79d74 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt @@ -85,11 +85,11 @@ open class HandHistory : RealmObject() { action.position = i when (i) { 0 -> { - action.type = Action.Type.POST_SB.ordinal + action.typeIdentifier = Action.Type.POST_SB.ordinal action.amount = this.smallBlind } 1 -> { - action.type = Action.Type.POST_BB.ordinal + action.typeIdentifier = Action.Type.POST_BB.ordinal action.amount = this.bigBlind } else -> { From e321014973ac2f831a125f4ace9a6947f2fa9148 Mon Sep 17 00:00:00 2001 From: Laurent Date: Thu, 9 Jan 2020 17:06:13 +0100 Subject: [PATCH 006/399] Adds View extension to convert View into ByteArray --- .../android/ui/extensions/UIExtensions.kt | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/app/src/main/java/net/pokeranalytics/android/ui/extensions/UIExtensions.kt b/app/src/main/java/net/pokeranalytics/android/ui/extensions/UIExtensions.kt index d59709fd..897bfc97 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/extensions/UIExtensions.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/extensions/UIExtensions.kt @@ -5,6 +5,9 @@ import android.content.ActivityNotFoundException import android.content.Context import android.content.Intent import android.content.res.Resources +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.Color import android.net.Uri import android.util.TypedValue import android.view.View @@ -25,6 +28,7 @@ import net.pokeranalytics.android.util.DeviceUtils import net.pokeranalytics.android.util.TextFormat import net.pokeranalytics.android.util.URL import net.pokeranalytics.android.util.billing.AppGuard +import java.io.ByteArrayOutputStream import java.io.File @@ -171,4 +175,23 @@ fun SearchView.removeMargins() { layoutParams?.leftMargin = 0 layoutParams?.rightMargin = 0 searchEditFrame?.layoutParams = layoutParams +} + +fun View.toByteArray() : ByteArray { + + val bitmap = this.convertToBitmap() + val stream = ByteArrayOutputStream() + bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream) + return stream.toByteArray() +} + +fun View.convertToBitmap(): Bitmap { + val measureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) + measure(measureSpec, measureSpec) + layout(0, 0, measuredWidth, measuredHeight) + val r = Bitmap.createBitmap(measuredWidth, measuredHeight, Bitmap.Config.ARGB_8888) + r.eraseColor(Color.TRANSPARENT) + val canvas = Canvas(r) + draw(canvas) + return r } \ No newline at end of file From 6cee11edbe591235dce784009b3db550413f4f85 Mon Sep 17 00:00:00 2001 From: Laurent Date: Thu, 9 Jan 2020 17:26:40 +0100 Subject: [PATCH 007/399] Manage forgotten case to determine possible HH player action --- .../android/model/handhistory/Builder.kt | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/net/pokeranalytics/android/model/handhistory/Builder.kt b/app/src/main/java/net/pokeranalytics/android/model/handhistory/Builder.kt index ca2f941e..338a58ff 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/handhistory/Builder.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/handhistory/Builder.kt @@ -62,14 +62,23 @@ class Builder { return if (lastSignificantAction != null) { - when (lastSignificantAction.action.type) { + when (val actionType = lastSignificantAction.action.type) { Action.Type.POST_BB, Action.Type.STRADDLE -> setOf(Action.Type.STRADDLE, Action.Type.FOLD, Action.Type.CALL, Action.Type.BET, Action.Type.BET_ALLIN) Action.Type.BET, Action.Type.RAISE, Action.Type.RAISE_ALLIN, Action.Type.BET_ALLIN -> { val remainingStack = lastUserAction?.playerRemainingStack val actionAmount = lastSignificantAction.action.amount - if (remainingStack != null && actionAmount != null && remainingStack <= actionAmount) { + if ((actionType == Action.Type.RAISE_ALLIN || actionType == Action.Type.BET_ALLIN) + && this.remainingPlayerCount == 2) { + + if (remainingStack != null && actionAmount != null && remainingStack <= actionAmount) { + setOf(Action.Type.FOLD, Action.Type.CALL_ALLIN) + } else { + setOf(Action.Type.FOLD, Action.Type.CALL) + } + + } else if (remainingStack != null && actionAmount != null && remainingStack <= actionAmount) { setOf(Action.Type.FOLD, Action.Type.CALL_ALLIN) } else { setOf(Action.Type.FOLD, Action.Type.CALL, Action.Type.RAISE, Action.Type.RAISE_ALLIN) @@ -86,6 +95,11 @@ class Builder { } + private val remainingPlayerCount: Int + get() { + return 0 // TODO + } + private fun selectAction(index: Int, actionType: Action.Type) { // remove everything after } From 884ae51f3c58b409ba9274bb4616a9cd40c54248 Mon Sep 17 00:00:00 2001 From: Laurent Date: Fri, 10 Jan 2020 10:21:55 +0100 Subject: [PATCH 008/399] Improved lisibility --- .../android/model/handhistory/Builder.kt | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/net/pokeranalytics/android/model/handhistory/Builder.kt b/app/src/main/java/net/pokeranalytics/android/model/handhistory/Builder.kt index 338a58ff..24ad6a0f 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/handhistory/Builder.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/handhistory/Builder.kt @@ -62,24 +62,25 @@ class Builder { return if (lastSignificantAction != null) { - when (val actionType = lastSignificantAction.action.type) { - Action.Type.POST_BB, Action.Type.STRADDLE -> setOf(Action.Type.STRADDLE, Action.Type.FOLD, Action.Type.CALL, Action.Type.BET, Action.Type.BET_ALLIN) - Action.Type.BET, Action.Type.RAISE, Action.Type.RAISE_ALLIN, Action.Type.BET_ALLIN -> { + val remainingStack = lastUserAction?.playerRemainingStack + val actionAmount = lastSignificantAction.action.amount - val remainingStack = lastUserAction?.playerRemainingStack - val actionAmount = lastSignificantAction.action.amount - - if ((actionType == Action.Type.RAISE_ALLIN || actionType == Action.Type.BET_ALLIN) - && this.remainingPlayerCount == 2) { - - if (remainingStack != null && actionAmount != null && remainingStack <= actionAmount) { - setOf(Action.Type.FOLD, Action.Type.CALL_ALLIN) - } else { - setOf(Action.Type.FOLD, Action.Type.CALL) - } - - } else if (remainingStack != null && actionAmount != null && remainingStack <= actionAmount) { + when (lastSignificantAction.action.type) { + Action.Type.POST_BB, Action.Type.STRADDLE -> { + setOf(Action.Type.STRADDLE, Action.Type.FOLD, Action.Type.CALL, Action.Type.BET, Action.Type.BET_ALLIN) + } + Action.Type.BET, Action.Type.RAISE -> { + if (remainingStack != null && actionAmount != null && remainingStack <= actionAmount) { + setOf(Action.Type.FOLD, Action.Type.CALL_ALLIN) + } else { + setOf(Action.Type.FOLD, Action.Type.CALL, Action.Type.RAISE, Action.Type.RAISE_ALLIN) + } + } + Action.Type.RAISE_ALLIN, Action.Type.BET_ALLIN -> { + if (remainingStack != null && actionAmount != null && remainingStack <= actionAmount) { setOf(Action.Type.FOLD, Action.Type.CALL_ALLIN) + } else if (this.remainingPlayerCount == 2 && remainingStack != null && actionAmount != null && remainingStack > actionAmount) { + setOf(Action.Type.FOLD, Action.Type.CALL) } else { setOf(Action.Type.FOLD, Action.Type.CALL, Action.Type.RAISE, Action.Type.RAISE_ALLIN) } @@ -104,6 +105,10 @@ class Builder { // remove everything after } + private fun setAmount(index: Int, amount: Double) { + + } + private fun getLastUserAction(index: Int): ComputedAction? { val computedAction = this.sortedActions.first { it.action.index == index } val action = computedAction.action From ad1c11bcfe644d4c265239d2d71e1e3f8b51bc4b Mon Sep 17 00:00:00 2001 From: Laurent Date: Fri, 10 Jan 2020 10:37:11 +0100 Subject: [PATCH 009/399] Adds documentation --- .../android/model/handhistory/Builder.kt | 52 ++++++++++++++++--- 1 file changed, 46 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/net/pokeranalytics/android/model/handhistory/Builder.kt b/app/src/main/java/net/pokeranalytics/android/model/handhistory/Builder.kt index 24ad6a0f..f3397d02 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/handhistory/Builder.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/handhistory/Builder.kt @@ -15,12 +15,23 @@ class Builder { load() } + /*** + * The map containing all actions present in a street + */ private val actionsPerStreet = hashMapOf>() + + /*** + * All actions sorted by index + */ private val sortedActions: List get() { return actionsPerStreet.flatMap { it.value } } + /*** + * Creates a builder using a [handSetup] + * Creates a new Hand History and configures it according to the [handSetup] + */ constructor(handSetup: HandSetup) { val handHistory = HandHistory() handHistory.configure(handSetup) @@ -28,10 +39,17 @@ class Builder { } + /*** + * Creates a builder using the parameter [handHistory] + */ constructor(handHistory: HandHistory) { this.handHistory = handHistory } + /*** + * Fills the [actionsPerStreet] variable with the proper actions + * Pre-computes the potsizes for the video export + */ private fun load() { var potSize = 0.0 @@ -55,6 +73,9 @@ class Builder { } } + /*** + * Returns the list of available user actions at [index] + */ private fun availableActions(index: Int) : Set { val lastSignificantAction: ComputedAction? = getLastSignificantAction(index) @@ -79,7 +100,7 @@ class Builder { Action.Type.RAISE_ALLIN, Action.Type.BET_ALLIN -> { if (remainingStack != null && actionAmount != null && remainingStack <= actionAmount) { setOf(Action.Type.FOLD, Action.Type.CALL_ALLIN) - } else if (this.remainingPlayerCount == 2 && remainingStack != null && actionAmount != null && remainingStack > actionAmount) { + } else if (this.remainingPlayerCount(index) == 2 && remainingStack != null && actionAmount != null && remainingStack > actionAmount) { setOf(Action.Type.FOLD, Action.Type.CALL) } else { setOf(Action.Type.FOLD, Action.Type.CALL, Action.Type.RAISE, Action.Type.RAISE_ALLIN) @@ -96,19 +117,32 @@ class Builder { } - private val remainingPlayerCount: Int - get() { - return 0 // TODO - } + /*** + * Returns the remaining player count at the provided [index] + */ + private fun remainingPlayerCount(index: Int): Int { + return 0 + } + /*** + * Selects an action type for the action at the provided [index] + * If the user changes the current action, + * for convenience we remove all the following actions to avoid managing complex cases + */ private fun selectAction(index: Int, actionType: Action.Type) { - // remove everything after + // if the selected action is different from current, remove any action after the index } + /*** + * Sets the amount for the action at the provided [index] + */ private fun setAmount(index: Int, amount: Double) { } + /*** + * Returns the last user action, if any, for the action at the provided [index] + */ private fun getLastUserAction(index: Int): ComputedAction? { val computedAction = this.sortedActions.first { it.action.index == index } val action = computedAction.action @@ -117,11 +151,17 @@ class Builder { return previousActions.lastOrNull { it.action.position == action.position } } + /*** + * Returns the last significant player action, if any, for the action at the provided [index] + */ private fun getLastSignificantAction(index: Int): ComputedAction? { val previousActions = this.sortedActions.drop(index) return previousActions.lastOrNull { it.action.isActionSignificant } } + /*** + * Saves the current hand state, represented by [actionsPerStreet], in the database + */ private fun save() { } From 4fe79df78618c933a33626bb0399129c69b72927 Mon Sep 17 00:00:00 2001 From: Laurent Date: Fri, 10 Jan 2020 11:27:20 +0100 Subject: [PATCH 010/399] Adds HH migration --- .../android/PokerAnalyticsApplication.kt | 2 +- .../migrations/PokerAnalyticsMigration.kt | 45 +++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/net/pokeranalytics/android/PokerAnalyticsApplication.kt b/app/src/main/java/net/pokeranalytics/android/PokerAnalyticsApplication.kt index e6c52dc3..49a601f0 100644 --- a/app/src/main/java/net/pokeranalytics/android/PokerAnalyticsApplication.kt +++ b/app/src/main/java/net/pokeranalytics/android/PokerAnalyticsApplication.kt @@ -44,7 +44,7 @@ class PokerAnalyticsApplication : Application() { Realm.init(this) val realmConfiguration = RealmConfiguration.Builder() .name(Realm.DEFAULT_REALM_NAME) - .schemaVersion(8) + .schemaVersion(9) .migration(PokerAnalyticsMigration()) .initialData(Seed(this)) .build() diff --git a/app/src/main/java/net/pokeranalytics/android/model/migrations/PokerAnalyticsMigration.kt b/app/src/main/java/net/pokeranalytics/android/model/migrations/PokerAnalyticsMigration.kt index 32bebc81..843db86c 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/migrations/PokerAnalyticsMigration.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/migrations/PokerAnalyticsMigration.kt @@ -2,6 +2,7 @@ package net.pokeranalytics.android.model.migrations import io.realm.DynamicRealm import io.realm.RealmMigration +import net.pokeranalytics.android.exceptions.PAIllegalStateException import timber.log.Timber import java.util.* @@ -171,6 +172,50 @@ class PokerAnalyticsMigration : RealmMigration { currentVersion++ } + + // Migrate to version 9 + if (currentVersion == 8) { + schema.get("HandHistory")?.let { hhSchema -> + + schema.get("Session")?.let { + hhSchema.addRealmObjectField("session", it) + } ?: throw PAIllegalStateException("Session schema not found") + hhSchema.addField("smallBlind", Double::class.java) + hhSchema.addField("bigBlind", Double::class.java) + hhSchema.addField("ante", Double::class.java) + hhSchema.addField("bigBlindAnte", Boolean::class.java) + hhSchema.addField("numberOfPlayers", Int::class.java) + hhSchema.addField("comment", String::class.java) + hhSchema.addField("heroIndex", Int::class.java).setRequired("heroIndex", false) + + val cardSchema = schema.create("Card") + cardSchema.addField("value", Int::class.java) + cardSchema.addField("suit", Int::class.java) + hhSchema.addRealmListField("board", cardSchema) + + val actionSchema = schema.create("Action") + actionSchema.addField("street", Int::class.java) + actionSchema.addField("index", Int::class.java) + actionSchema.addField("position", Int::class.java) + actionSchema.addField("typeIdentifier", Int::class.java).setRequired("typeIdentifier", false) + actionSchema.addField("amount", Double::class.java).setRequired("amount", false) + actionSchema.addField("effectiveAmount", Double::class.java) + actionSchema.addField("positionRemainingStack", Double::class.java).setRequired("positionRemainingStack", false) + hhSchema.addRealmListField("actions", actionSchema) + + val playerSetupSchema = schema.create("PlayerSetup") + schema.get("Player")?.let { + playerSetupSchema.addRealmObjectField("player", it) + } ?: throw PAIllegalStateException("Session schema not found") + playerSetupSchema.addField("position", Int::class.java) + playerSetupSchema.addField("stack", Double::class.java).setRequired("stack", false) + playerSetupSchema.addRealmListField("cards", cardSchema) + hhSchema.addRealmListField("playerSetups", playerSetupSchema) + + } + + currentVersion++ + } } override fun equals(other: Any?): Boolean { From 296b7942b0351ca3d282e520a7b268c7eb4c5f6d Mon Sep 17 00:00:00 2001 From: Laurent Date: Fri, 10 Jan 2020 16:58:42 +0100 Subject: [PATCH 011/399] Adding new HH button linked to video test --- .../ui/activity/NewDataMenuActivity.kt | 4 + .../android/ui/extensions/UIExtensions.kt | 38 ++++--- .../android/ui/fragment/FeedFragment.kt | 106 ++++++++++++++++-- .../android/ui/view/handhistory/Circle.kt | 35 ++++++ .../android/ui/view/handhistory/VideoView.kt | 53 +++++++++ .../android/util/video/MMediaMuxer.kt | 28 +++-- app/src/main/res/layout/activity_new_data.xml | 34 +++++- app/src/main/res/layout/view_video.xml | 12 ++ 8 files changed, 271 insertions(+), 39 deletions(-) create mode 100644 app/src/main/java/net/pokeranalytics/android/ui/view/handhistory/Circle.kt create mode 100644 app/src/main/java/net/pokeranalytics/android/ui/view/handhistory/VideoView.kt create mode 100644 app/src/main/res/layout/view_video.xml diff --git a/app/src/main/java/net/pokeranalytics/android/ui/activity/NewDataMenuActivity.kt b/app/src/main/java/net/pokeranalytics/android/ui/activity/NewDataMenuActivity.kt index 3daab915..be5e8cc8 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/activity/NewDataMenuActivity.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/activity/NewDataMenuActivity.kt @@ -79,6 +79,10 @@ class NewDataMenuActivity : BaseActivity() { finishWithResult(2) } + newHandHistory.setOnClickListener { + finishWithResult(3) + } + container.setOnClickListener { hideMenu() } diff --git a/app/src/main/java/net/pokeranalytics/android/ui/extensions/UIExtensions.kt b/app/src/main/java/net/pokeranalytics/android/ui/extensions/UIExtensions.kt index 897bfc97..70445b27 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/extensions/UIExtensions.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/extensions/UIExtensions.kt @@ -11,6 +11,7 @@ import android.graphics.Color import android.net.Uri import android.util.TypedValue import android.view.View +import android.widget.ImageView import android.widget.LinearLayout import android.widget.Toast import androidx.appcompat.app.AlertDialog @@ -19,6 +20,7 @@ import androidx.appcompat.widget.SearchView import androidx.browser.customtabs.CustomTabsIntent import androidx.core.content.ContextCompat import androidx.core.content.FileProvider +import androidx.core.graphics.drawable.toBitmap import androidx.core.view.isVisible import androidx.fragment.app.Fragment import net.pokeranalytics.android.BuildConfig @@ -178,20 +180,28 @@ fun SearchView.removeMargins() { } fun View.toByteArray() : ByteArray { - - val bitmap = this.convertToBitmap() - val stream = ByteArrayOutputStream() - bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream) - return stream.toByteArray() + return this.convertToBitmap().toByteArray() } fun View.convertToBitmap(): Bitmap { - val measureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) - measure(measureSpec, measureSpec) - layout(0, 0, measuredWidth, measuredHeight) - val r = Bitmap.createBitmap(measuredWidth, measuredHeight, Bitmap.Config.ARGB_8888) - r.eraseColor(Color.TRANSPARENT) - val canvas = Canvas(r) - draw(canvas) - return r -} \ No newline at end of file + val b = Bitmap.createBitmap(this.width, this.height, Bitmap.Config.ARGB_8888) + val canvas = Canvas(b) + val background = this.background + this.background?.let { + background.draw(canvas) + } ?: run { + canvas.drawColor(Color.WHITE) + } + this.draw(canvas) + return b +} + +fun ImageView.toByteArray() : ByteArray { + return this.drawable.toBitmap().toByteArray() +} + +private fun Bitmap.toByteArray() : ByteArray { + val baos = ByteArrayOutputStream() + this.compress(Bitmap.CompressFormat.PNG, 100, baos) + return baos.toByteArray() +} diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/FeedFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/FeedFragment.kt index 31ec10c2..a725a701 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/FeedFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/FeedFragment.kt @@ -26,14 +26,18 @@ import net.pokeranalytics.android.ui.activity.components.RequestCode import net.pokeranalytics.android.ui.adapter.FeedSessionRowRepresentableAdapter import net.pokeranalytics.android.ui.adapter.FeedTransactionRowRepresentableAdapter import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate +import net.pokeranalytics.android.ui.extensions.toByteArray import net.pokeranalytics.android.ui.fragment.components.FilterableFragment import net.pokeranalytics.android.ui.interfaces.FilterActivityRequestCode import net.pokeranalytics.android.ui.interfaces.FilterableType import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.SmoothScrollLinearLayoutManager +import net.pokeranalytics.android.ui.view.handhistory.VideoView import net.pokeranalytics.android.util.Preferences import net.pokeranalytics.android.util.billing.AppGuard import net.pokeranalytics.android.util.extensions.count +import net.pokeranalytics.android.util.video.MMediaMuxer +import timber.log.Timber import java.text.SimpleDateFormat import java.util.* @@ -70,9 +74,13 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { private var selectedTransaction: Transaction? = null private var selectedTransactionPosition: Int = -1 - override val observedEntities: List> = listOf(Session::class.java, Transaction::class.java) + override val observedEntities: List> = + listOf(Session::class.java, Transaction::class.java) - override fun entitiesChanged(clazz: Class, results: RealmResults) { + override fun entitiesChanged( + clazz: Class, + results: RealmResults + ) { super.entitiesChanged(clazz, results) when (clazz.kotlin) { @@ -88,12 +96,20 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { super.onCreateView(inflater, container, savedInstanceState) return inflater.inflate(R.layout.fragment_feed, container, false) } - override fun onCreateContextMenu(menu: ContextMenu?, v: View?, menuInfo: ContextMenu.ContextMenuInfo?) { + override fun onCreateContextMenu( + menu: ContextMenu?, + v: View?, + menuInfo: ContextMenu.ContextMenuInfo? + ) { super.onCreateContextMenu(menu, v, menuInfo) if (v?.id == R.id.menuRecyclerView) { @@ -168,6 +184,7 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { 0 -> createNewSession(false) 1 -> createNewSession(true) 2 -> createNewTransaction() + 3 -> createNewHandHistory() } } else if (requestCode == RequestCode.FEED_TRANSACTION_DETAILS.value && resultCode == RESULT_OK && data != null) { if (data.getStringExtra(DataListActivity.IntentKey.ITEM_DELETED.keyName) != null) { @@ -175,7 +192,10 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { } } else if (requestCode == FilterActivityRequestCode.CREATE_FILTER.ordinal && resultCode == RESULT_OK) { data?.let { - this.saveFilter(this.requireContext(), it.getStringExtra(FiltersActivity.IntentKey.FILTER_ID.keyName)) + this.saveFilter( + this.requireContext(), + it.getStringExtra(FiltersActivity.IntentKey.FILTER_ID.keyName) + ) } } else if (requestCode == RequestCode.NEW_TRANSACTION.value && resultCode == RESULT_OK) { this.selectTab(Tab.TRANSACTIONS) @@ -192,7 +212,10 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) { when (row) { - is Session -> SessionActivity.newInstance(requireContext(), sessionId = (row as Editable).id) + is Session -> SessionActivity.newInstance( + requireContext(), + sessionId = (row as Editable).id + ) is Transaction -> { selectedTransaction = row selectedTransactionPosition = position @@ -215,7 +238,8 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { registerForContextMenu(this.menuRecyclerView) - val messageToShow: Preferences.FeedMessage? = Preferences.feedMessageToShow(requireContext()) + val messageToShow: Preferences.FeedMessage? = + Preferences.feedMessageToShow(requireContext()) if (messageToShow != null) { messageBox.isVisible = true @@ -322,7 +346,8 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { } // Transactions - this.realmTransactions = transactionFilter?.results() ?: run { getRealm().where().findAll() } + this.realmTransactions = + transactionFilter?.results() ?: run { getRealm().where().findAll() } this.realmTransactions = this.realmTransactions.sort("date", Sort.DESCENDING) var distinctDateTransactions = transactionFilter?.results("year", "month") ?: run { @@ -330,14 +355,22 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { } distinctDateTransactions = distinctDateTransactions.sort("date", Sort.DESCENDING) this.feedTransactionAdapter = - FeedTransactionRowRepresentableAdapter(this, realmTransactions, distinctDateTransactions) + FeedTransactionRowRepresentableAdapter( + this, + realmTransactions, + distinctDateTransactions + ) } /** * Create a new cash game */ - private fun createNewSession(isTournament: Boolean, sessionId: String? = null, duplicate: Boolean = false) { + private fun createNewSession( + isTournament: Boolean, + sessionId: String? = null, + duplicate: Boolean = false + ) { val sessionCount = getRealm().count(Session::class.java) if (!AppGuard.isProUser && sessionCount >= AppGuard.MAX_SESSIONS_BEFORE_REQUESTING_SUBSCRIPTION) { // && !BuildConfig.DEBUG @@ -374,7 +407,56 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { } } - EditableDataActivity.newInstanceForResult(this, LiveData.TRANSACTION, null, RequestCode.NEW_TRANSACTION.value) + EditableDataActivity.newInstanceForResult( + this, + LiveData.TRANSACTION, + null, + RequestCode.NEW_TRANSACTION.value + ) + } + + /*** + * Create a new hand history + */ + private fun createNewHandHistory() { + + Timber.d("**** Start video test") + + + val testView = View(requireContext()) + testView.setBackgroundColor(requireContext().getColor(R.color.blue)) + testView.layoutParams = ViewGroup.LayoutParams(480, 480) + + val videoView = VideoView(requireContext()) + + videoView.let { + + val muxer = MMediaMuxer() + + Timber.d("width = ${it.width}, height = ${it.height}") + + val width = (it.width / 2) * 2 + val height= (it.height / 2) * 2 + + muxer.Init(requireActivity(), width, height, "hhVideo", "YES!") + + Timber.d("**** Adds frames") + for (i in 0..50) { + + try { + val byteArray = it.toByteArray() + muxer.AddFrame(byteArray) + } catch (e: Exception) { + Timber.e("error = ${e.message}") + } + } + Timber.d("**** Create video") + muxer.CreateVideo() + + val path = muxer.GetPath() + Timber.d("**** Video path = $path") + } + } /** @@ -399,7 +481,7 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { ).show() } - // Filter Handler +// Filter Handler override fun applyFilter() { super.applyFilter() diff --git a/app/src/main/java/net/pokeranalytics/android/ui/view/handhistory/Circle.kt b/app/src/main/java/net/pokeranalytics/android/ui/view/handhistory/Circle.kt new file mode 100644 index 00000000..1853296f --- /dev/null +++ b/app/src/main/java/net/pokeranalytics/android/ui/view/handhistory/Circle.kt @@ -0,0 +1,35 @@ +package net.pokeranalytics.android.ui.view.handhistory + +import android.content.Context +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.RectF +import android.util.AttributeSet +import android.view.View +import net.pokeranalytics.android.R + +class Circle(context: Context, attrs: AttributeSet) : View(context, attrs) { + + companion object { + private val START_ANGLE_POINT = 270 + private val STROKE_WIDTH = 40 + } + + private val paint: Paint = Paint() + private lateinit var rect: RectF + + var angle: Float = 180.0f + + init { + paint.isAntiAlias = true + paint.style = Paint.Style.STROKE + paint.strokeWidth = STROKE_WIDTH.toFloat() + paint.color = getContext().getColor(R.color.chart_selected_bar) + } + + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + canvas.drawArc(rect, START_ANGLE_POINT.toFloat(), angle, false, paint) + } + +} \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/ui/view/handhistory/VideoView.kt b/app/src/main/java/net/pokeranalytics/android/ui/view/handhistory/VideoView.kt new file mode 100644 index 00000000..8d48d9eb --- /dev/null +++ b/app/src/main/java/net/pokeranalytics/android/ui/view/handhistory/VideoView.kt @@ -0,0 +1,53 @@ +package net.pokeranalytics.android.ui.view.handhistory + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import android.widget.FrameLayout +import net.pokeranalytics.android.R +import timber.log.Timber + +class VideoView : FrameLayout { + + /** + * Constructors + */ + constructor(context: Context) : super(context) { + init() + } + + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) { + init() + } + + constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { + init() + } + + open fun getResourceLayout(): Int { + return R.layout.view_video + } + + private lateinit var frameLayout: FrameLayout + + /** + * Init + */ + private fun init() { + + this.setBackgroundColor(context.getColor(R.color.blue)) + + val layoutInflater = LayoutInflater.from(context) + frameLayout = layoutInflater.inflate(getResourceLayout(), this, false) as FrameLayout +// val layoutParams = LayoutParams(480, 480) + + val width = frameLayout.layoutParams.width + val height = frameLayout.layoutParams.height + Timber.d("W = $width, H = $height") + + this.layoutParams = frameLayout.layoutParams + addView(frameLayout, frameLayout.layoutParams) + + } + +} \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/util/video/MMediaMuxer.kt b/app/src/main/java/net/pokeranalytics/android/util/video/MMediaMuxer.kt index d131a478..4c781376 100644 --- a/app/src/main/java/net/pokeranalytics/android/util/video/MMediaMuxer.kt +++ b/app/src/main/java/net/pokeranalytics/android/util/video/MMediaMuxer.kt @@ -9,6 +9,7 @@ import android.media.MediaCodecInfo.CodecCapabilities import android.os.Environment import android.os.Handler import android.util.Log +import timber.log.Timber import java.io.File import java.io.IOException import java.text.DateFormat @@ -203,7 +204,7 @@ class MMediaMuxer { DateFormat.getDateTimeInstance().format(Date()) outputPath = File( Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES), - "pixel$currentDateTimeString.mp4" + "pixel.mp4" ).toString() mediaMuxer = MediaMuxer(outputPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4) } catch (ioe: IOException) { @@ -278,7 +279,7 @@ class MMediaMuxer { mediaMuxer!!.stop() mediaMuxer!!.release() mediaMuxer = null - Logd("RELEASE MUXER") + Logd("RELEASE MUXER, path = ${outputPath}") } } @@ -310,21 +311,22 @@ class MMediaMuxer { var index = 0 for (j in 0 until height) { for (i in 0 until width) { + a = argb[index] and -0x1000000 shr 24 // a is not used obviously R = argb[index] and 0xff0000 shr 16 G = argb[index] and 0xff00 shr 8 B = argb[index] and 0xff shr 0 + Y = (66 * R + 129 * G + 25 * B + 128 shr 8) + 16 U = (-38 * R - 74 * G + 112 * B + 128 shr 8) + 128 V = (112 * R - 94 * G - 18 * B + 128 shr 8) + 128 - yuv420sp[yIndex++] = - (if (Y < 0) 0 else if (Y > 255) 255 else Y).toByte() + + yuv420sp[yIndex++] = (if (Y < 0) 0 else if (Y > 255) 255 else Y).toByte() if (j % 2 == 0 && index % 2 == 0) { - yuv420sp[uvIndex++] = - (if (U < 0) 0 else if (U > 255) 255 else U).toByte() - yuv420sp[uvIndex++] = - (if (V < 0) 0 else if (V > 255) 255 else V).toByte() + yuv420sp[uvIndex++] = (if (U < 0) 0 else if (U > 255) 255 else U).toByte() + yuv420sp[uvIndex++] = (if (V < 0) 0 else if (V > 255) 255 else V).toByte() } + index++ } } @@ -411,13 +413,15 @@ class MMediaMuxer { } private fun Logd(Mess: String) { - if (DEBUG) { - Log.d(TAG, Mess) - } + Timber.d("$Mess") +// if (DEBUG) { +// Log.d(TAG, Mess) +// } } private fun Loge(Mess: String?) { - Log.e(TAG, Mess) + Timber.e("$Mess") +// Log.e(TAG, Mess) } } } \ No newline at end of file diff --git a/app/src/main/res/layout/activity_new_data.xml b/app/src/main/res/layout/activity_new_data.xml index e1cdc09b..db71888c 100644 --- a/app/src/main/res/layout/activity_new_data.xml +++ b/app/src/main/res/layout/activity_new_data.xml @@ -104,7 +104,6 @@ android:orientation="horizontal" android:paddingStart="16dp" android:paddingEnd="16dp" - app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/newTournament"> @@ -127,6 +126,39 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/view_video.xml b/app/src/main/res/layout/view_video.xml new file mode 100644 index 00000000..f2fae02c --- /dev/null +++ b/app/src/main/res/layout/view_video.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file From 9da80a72227e1fa01ca983ecfb615dcf77a5424c Mon Sep 17 00:00:00 2001 From: Laurent Date: Fri, 10 Jan 2020 17:39:56 +0100 Subject: [PATCH 012/399] Working video test with bitmap + canvas --- .../android/ui/extensions/UIExtensions.kt | 2 +- .../android/ui/fragment/FeedFragment.kt | 29 +++++++++++++++---- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/net/pokeranalytics/android/ui/extensions/UIExtensions.kt b/app/src/main/java/net/pokeranalytics/android/ui/extensions/UIExtensions.kt index 70445b27..2aa93dd1 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/extensions/UIExtensions.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/extensions/UIExtensions.kt @@ -200,7 +200,7 @@ fun ImageView.toByteArray() : ByteArray { return this.drawable.toBitmap().toByteArray() } -private fun Bitmap.toByteArray() : ByteArray { +fun Bitmap.toByteArray() : ByteArray { val baos = ByteArrayOutputStream() this.compress(Bitmap.CompressFormat.PNG, 100, baos) return baos.toByteArray() diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/FeedFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/FeedFragment.kt index a725a701..0300ac10 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/FeedFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/FeedFragment.kt @@ -2,6 +2,10 @@ package net.pokeranalytics.android.ui.fragment import android.app.Activity.RESULT_OK import android.content.Intent +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.Rect import android.os.Bundle import android.view.* import android.widget.Toast @@ -32,7 +36,6 @@ import net.pokeranalytics.android.ui.interfaces.FilterActivityRequestCode import net.pokeranalytics.android.ui.interfaces.FilterableType import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.SmoothScrollLinearLayoutManager -import net.pokeranalytics.android.ui.view.handhistory.VideoView import net.pokeranalytics.android.util.Preferences import net.pokeranalytics.android.util.billing.AppGuard import net.pokeranalytics.android.util.extensions.count @@ -422,14 +425,28 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { Timber.d("**** Start video test") + val width = 480 + val height = 480 - val testView = View(requireContext()) - testView.setBackgroundColor(requireContext().getColor(R.color.blue)) - testView.layoutParams = ViewGroup.LayoutParams(480, 480) - val videoView = VideoView(requireContext()) +// val testView = View(requireContext()) +// testView.setBackgroundColor(requireContext().getColor(R.color.blue)) +// testView.layoutParams = ViewGroup.LayoutParams(width, height) +// +// val videoView = VideoView(requireContext()) + + val bitmap = Bitmap.createBitmap(480, 480, Bitmap.Config.ARGB_8888) + val canvas = Canvas(bitmap) + + val paint = Paint() + paint.isAntiAlias = true + paint.style = Paint.Style.STROKE + paint.strokeWidth = 20.0.toFloat() + paint.color = requireContext().getColor(R.color.blue) + + canvas.drawRect(Rect(0,0, width, height), paint) - videoView.let { + bitmap.let { val muxer = MMediaMuxer() From 8dcf26815de9099c37b8c041bfc3006908a903b0 Mon Sep 17 00:00:00 2001 From: Laurent Date: Mon, 13 Jan 2020 12:58:36 +0100 Subject: [PATCH 013/399] Added more hand history case management --- .../android/model/handhistory/Builder.kt | 118 +++++++++++++++--- .../model/handhistory/ComputedAction.kt | 18 ++- .../android/model/realm/handhistory/Action.kt | 12 +- .../model/realm/handhistory/HandHistory.kt | 4 +- 4 files changed, 129 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/net/pokeranalytics/android/model/handhistory/Builder.kt b/app/src/main/java/net/pokeranalytics/android/model/handhistory/Builder.kt index f3397d02..5e7962f5 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/handhistory/Builder.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/handhistory/Builder.kt @@ -3,6 +3,7 @@ package net.pokeranalytics.android.model.handhistory import net.pokeranalytics.android.exceptions.PAIllegalStateException import net.pokeranalytics.android.model.realm.handhistory.Action import net.pokeranalytics.android.model.realm.handhistory.HandHistory +import kotlin.math.min class Builder { @@ -23,10 +24,10 @@ class Builder { /*** * All actions sorted by index */ - private val sortedActions: List - get() { - return actionsPerStreet.flatMap { it.value } - } + private var sortedActions: List = listOf() +// get() { +// return actionsPerStreet.flatMap { it.value } +// } /*** * Creates a builder using a [handSetup] @@ -52,7 +53,7 @@ class Builder { */ private fun load() { - var potSize = 0.0 +// var potSize = 0.0 var totalPotSize = 0.0 // on veut sortir: @@ -61,16 +62,24 @@ class Builder { // => ne pas oublier flop / turn / river / résumé // sorted actions + val computedActions = mutableListOf() val sortedActions = this.handHistory.actions.sortedBy { it.index } - Street.values().forEach { street -> - val filteredActions = sortedActions.filter { it.street == street.ordinal } - val computedActions = filteredActions.map { action -> - totalPotSize += action.effectiveAmount - ComputedAction(action, potSize, totalPotSize, action.positionRemainingStack) - } - this.actionsPerStreet[street] = computedActions - potSize = totalPotSize + sortedActions.forEach { action -> + totalPotSize += action.effectiveAmount + val ca = ComputedAction(action, totalPotSize, action.positionRemainingStack) + computedActions.add(ca) } + this.sortedActions = computedActions + +// Street.values().forEach { street -> +// val filteredActions = sortedActions.filter { it.street == street.ordinal } +// val computedActions = filteredActions.map { action -> +// totalPotSize += action.effectiveAmount +// ComputedAction(action, potSize, totalPotSize, action.positionRemainingStack) +// } +// this.actionsPerStreet[street] = computedActions +// potSize = totalPotSize +// } } /*** @@ -88,13 +97,13 @@ class Builder { when (lastSignificantAction.action.type) { Action.Type.POST_BB, Action.Type.STRADDLE -> { - setOf(Action.Type.STRADDLE, Action.Type.FOLD, Action.Type.CALL, Action.Type.BET, Action.Type.BET_ALLIN) + setOf(Action.Type.STRADDLE, Action.Type.FOLD, Action.Type.CALL, Action.Type.BET, Action.Type.UNDEFINED_ALLIN) } Action.Type.BET, Action.Type.RAISE -> { if (remainingStack != null && actionAmount != null && remainingStack <= actionAmount) { setOf(Action.Type.FOLD, Action.Type.CALL_ALLIN) } else { - setOf(Action.Type.FOLD, Action.Type.CALL, Action.Type.RAISE, Action.Type.RAISE_ALLIN) + setOf(Action.Type.FOLD, Action.Type.CALL, Action.Type.RAISE, Action.Type.UNDEFINED_ALLIN) } } Action.Type.RAISE_ALLIN, Action.Type.BET_ALLIN -> { @@ -103,7 +112,7 @@ class Builder { } else if (this.remainingPlayerCount(index) == 2 && remainingStack != null && actionAmount != null && remainingStack > actionAmount) { setOf(Action.Type.FOLD, Action.Type.CALL) } else { - setOf(Action.Type.FOLD, Action.Type.CALL, Action.Type.RAISE, Action.Type.RAISE_ALLIN) + setOf(Action.Type.FOLD, Action.Type.CALL, Action.Type.RAISE, Action.Type.UNDEFINED_ALLIN) } } else -> { @@ -129,15 +138,90 @@ class Builder { * If the user changes the current action, * for convenience we remove all the following actions to avoid managing complex cases */ - private fun selectAction(index: Int, actionType: Action.Type) { + private fun selectAction(index: Int, actionType: Action.Type) : Boolean { // if the selected action is different from current, remove any action after the index + val computedAction = this.sortedActions.first { it.action.index == index } + computedAction.action.type = actionType + + dropIfNecessary(index) + + // remaining stack + effective amount + + when (actionType) { + Action.Type.CALL -> { + val significantAmount = getLastSignificantAction(index)?.action?.amount ?: throw PAIllegalStateException("Cannot happen") + val committedAmount = getPreviousCommittedAmount(index) ?: 0.0 + computedAction.setEffectiveAmount(significantAmount - committedAmount) + } + Action.Type.CALL_ALLIN -> { + computedAction.setEffectiveAmount(computedAction.playerRemainingStack!!) + } + Action.Type.STRADDLE -> { + // TODO + } + } + + return computedAction.requireAmount + } + + private fun dropIfNecessary(index: Int) { + this.sortedActions.drop(index + 1) } /*** * Sets the amount for the action at the provided [index] */ private fun setAmount(index: Int, amount: Double) { + val computedAction = this.sortedActions.first { it.action.index == index } + + val adjustedAmount = computedAction.playerRemainingStack?.let { min(it, amount) } ?: amount + computedAction.action.amount = adjustedAmount + + when (computedAction.action.type) { + Action.Type.UNDEFINED_ALLIN -> { + getLastSignificantAction(index)?.action?.amount?.let { significantActionAmount -> + + val committedAmount = getPreviousCommittedAmount(index) ?: 0.0 + val askedAmount = significantActionAmount - committedAmount + + computedAction.playerRemainingStack?.let { remainingStack -> + + if (remainingStack > askedAmount) { + selectAction(index, Action.Type.RAISE_ALLIN) + } else { + selectAction(index, Action.Type.CALL_ALLIN) + } + + } ?: run { + selectAction(index, Action.Type.RAISE_ALLIN) + } + + } ?: run { + selectAction(index, Action.Type.RAISE_ALLIN) + } + } + else -> {} + } + + } + + private fun getPreviousCommittedAmount(index: Int) : Double? { + val computedAction = this.sortedActions.first { it.action.index == index } + val position = computedAction.action.position + + val previousActions = this.sortedActions.drop(index) + val previousComputedAction = previousActions.lastOrNull { it.action.position == position } + + previousComputedAction?.action?.let { action -> + + return when (action.type) { + Action.Type.POST_BB, Action.Type.POST_SB, Action.Type.STRADDLE, Action.Type.BET, Action.Type.RAISE -> action.amount + Action.Type.CALL -> getLastSignificantAction(action.index)?.action?.amount + else -> null + } + } + return null } /*** diff --git a/app/src/main/java/net/pokeranalytics/android/model/handhistory/ComputedAction.kt b/app/src/main/java/net/pokeranalytics/android/model/handhistory/ComputedAction.kt index 4620e052..7682b469 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/handhistory/ComputedAction.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/handhistory/ComputedAction.kt @@ -3,7 +3,6 @@ package net.pokeranalytics.android.model.handhistory import net.pokeranalytics.android.model.realm.handhistory.Action class ComputedAction(var action: Action, - var potSize: Double = 0.0, var totalPotSize: Double = 0.0, var playerRemainingStack: Double? = null) { @@ -16,4 +15,21 @@ class ComputedAction(var action: Action, // // var playerRemainingStack = 0.0 + + val requireAmount: Boolean + get() { + return when(this.action.type) { + Action.Type.BET, Action.Type.RAISE -> true + Action.Type.BET_ALLIN, Action.Type.RAISE_ALLIN -> (this.playerRemainingStack == null) + else -> false + } + } + + fun setEffectiveAmount(amount: Double) { + this.action.effectiveAmount = amount + this.playerRemainingStack?.let { + this.playerRemainingStack = it - amount + } + } + } \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/Action.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/Action.kt index 5f9d4ebc..30f056a6 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/Action.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/Action.kt @@ -1,6 +1,7 @@ package net.pokeranalytics.android.model.realm.handhistory import io.realm.RealmObject +import net.pokeranalytics.android.exceptions.PAIllegalStateException open class Action : RealmObject() { @@ -13,6 +14,7 @@ open class Action : RealmObject() { CALL, BET, RAISE, + UNDEFINED_ALLIN, CALL_ALLIN, BET_ALLIN, RAISE_ALLIN; @@ -21,9 +23,11 @@ open class Action : RealmObject() { get() { return when (this) { POST_BB, STRADDLE, BET, RAISE, BET_ALLIN, RAISE_ALLIN -> true + UNDEFINED_ALLIN -> throw PAIllegalStateException("Can't ask for UNDEFINED_ALLIN") else -> false } } + } /*** @@ -44,9 +48,12 @@ open class Action : RealmObject() { /*** * The type of action: check, fold, raise... */ - var typeIdentifier: Int? = null + private var typeIdentifier: Int? = null - val type: Type? + var type: Type? + set(value) { + this.typeIdentifier = value?.ordinal + } get() { return typeIdentifier?.let { Type.values()[it] } } @@ -57,7 +64,6 @@ open class Action : RealmObject() { var amount: Double? = null var effectiveAmount: Double = 0.0 - private set var positionRemainingStack: Double? = null diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt index c1a79d74..6647bcbb 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt @@ -85,11 +85,11 @@ open class HandHistory : RealmObject() { action.position = i when (i) { 0 -> { - action.typeIdentifier = Action.Type.POST_SB.ordinal + action.type = Action.Type.POST_SB action.amount = this.smallBlind } 1 -> { - action.typeIdentifier = Action.Type.POST_BB.ordinal + action.type = Action.Type.POST_BB action.amount = this.bigBlind } else -> { From c1e09c41ebd433ca801db45ba04828105e4b1cae Mon Sep 17 00:00:00 2001 From: Laurent Date: Mon, 13 Jan 2020 15:07:20 +0100 Subject: [PATCH 014/399] Refactoring + implementation of dropNextActionsIfNecessary --- .../android/model/handhistory/Builder.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/net/pokeranalytics/android/model/handhistory/Builder.kt b/app/src/main/java/net/pokeranalytics/android/model/handhistory/Builder.kt index 5e7962f5..2e2f16b5 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/handhistory/Builder.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/handhistory/Builder.kt @@ -143,9 +143,7 @@ class Builder { val computedAction = this.sortedActions.first { it.action.index == index } computedAction.action.type = actionType - dropIfNecessary(index) - - // remaining stack + effective amount + dropNextActionsIfNecessary(index) when (actionType) { Action.Type.CALL -> { @@ -164,8 +162,10 @@ class Builder { return computedAction.requireAmount } - private fun dropIfNecessary(index: Int) { - this.sortedActions.drop(index + 1) + private fun dropNextActionsIfNecessary(index: Int) { + if (index > this.handHistory.numberOfPlayers) { + this.sortedActions.drop(index + 1) + } } /*** From 62c24590f061586b6767e3a5c895f141f3a663b0 Mon Sep 17 00:00:00 2001 From: Laurent Date: Mon, 13 Jan 2020 15:23:04 +0100 Subject: [PATCH 015/399] Fixes issue + refactoring + doc + cleanup --- .../android/model/handhistory/Builder.kt | 40 ++++++++++++------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/net/pokeranalytics/android/model/handhistory/Builder.kt b/app/src/main/java/net/pokeranalytics/android/model/handhistory/Builder.kt index 2e2f16b5..96e7c371 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/handhistory/Builder.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/handhistory/Builder.kt @@ -19,15 +19,12 @@ class Builder { /*** * The map containing all actions present in a street */ - private val actionsPerStreet = hashMapOf>() +// private val actionsPerStreet = hashMapOf>() /*** * All actions sorted by index */ - private var sortedActions: List = listOf() -// get() { -// return actionsPerStreet.flatMap { it.value } -// } + private var sortedActions: List = mutableListOf() /*** * Creates a builder using a [handSetup] @@ -137,9 +134,10 @@ class Builder { * Selects an action type for the action at the provided [index] * If the user changes the current action, * for convenience we remove all the following actions to avoid managing complex cases + * Also calculates the player effective amounts in proper cases */ private fun selectAction(index: Int, actionType: Action.Type) : Boolean { - // if the selected action is different from current, remove any action after the index + val computedAction = this.sortedActions.first { it.action.index == index } computedAction.action.type = actionType @@ -157,11 +155,16 @@ class Builder { Action.Type.STRADDLE -> { // TODO } + else -> {} } return computedAction.requireAmount } + /*** + * Removes the actions, if necessary, following a action change + * We only want to keep the first set of actions whatever happens + */ private fun dropNextActionsIfNecessary(index: Int) { if (index > this.handHistory.numberOfPlayers) { this.sortedActions.drop(index + 1) @@ -170,12 +173,13 @@ class Builder { /*** * Sets the amount for the action at the provided [index] + * In the case of an UNDEFINED_ALLIN, define if it's a RAISE_ALLIN or a CALL_ALLIN */ private fun setAmount(index: Int, amount: Double) { val computedAction = this.sortedActions.first { it.action.index == index } - val adjustedAmount = computedAction.playerRemainingStack?.let { min(it, amount) } ?: amount - computedAction.action.amount = adjustedAmount + val revisedAmount = computedAction.playerRemainingStack?.let { min(it, amount) } ?: amount + computedAction.action.amount = revisedAmount when (computedAction.action.type) { Action.Type.UNDEFINED_ALLIN -> { @@ -205,18 +209,24 @@ class Builder { } + /*** + * Returns the committed amount by the player for the street at the current [index] + */ private fun getPreviousCommittedAmount(index: Int) : Double? { - val computedAction = this.sortedActions.first { it.action.index == index } - val position = computedAction.action.position + val action = this.sortedActions.first { it.action.index == index }.action + val position = action.position + val street = action.street val previousActions = this.sortedActions.drop(index) - val previousComputedAction = previousActions.lastOrNull { it.action.position == position } + val previousComputedAction = previousActions.lastOrNull { + it.action.position == position && it.action.street == street + } - previousComputedAction?.action?.let { action -> + previousComputedAction?.action?.let { previousAction -> - return when (action.type) { - Action.Type.POST_BB, Action.Type.POST_SB, Action.Type.STRADDLE, Action.Type.BET, Action.Type.RAISE -> action.amount - Action.Type.CALL -> getLastSignificantAction(action.index)?.action?.amount + return when (previousAction.type) { + Action.Type.POST_BB, Action.Type.POST_SB, Action.Type.STRADDLE, Action.Type.BET, Action.Type.RAISE -> previousAction.amount + Action.Type.CALL -> getLastSignificantAction(previousAction.index)?.action?.amount else -> null } } From 15db3c4d3abe130e09bf892d1c9433dab7127063 Mon Sep 17 00:00:00 2001 From: Laurent Date: Mon, 13 Jan 2020 16:53:49 +0100 Subject: [PATCH 016/399] Adds basic UI for hand history --- app/src/main/AndroidManifest.xml | 5 + .../model/handhistory/ComputedAction.kt | 19 +-- .../handhistory/{Builder.kt => HHBuilder.kt} | 18 ++- .../android/model/handhistory/Street.kt | 12 +- .../migrations/PokerAnalyticsMigration.kt | 1 + .../android/model/realm/handhistory/Card.kt | 5 + .../model/realm/handhistory/HandHistory.kt | 18 ++- .../ui/activity/HandHistoryActivity.kt | 48 ++++++++ .../android/ui/activity/components/Codes.kt | 1 + .../ui/fragment/HandHistoryFragment.kt | 112 ++++++++++++++++++ .../android/ui/view/RowViewType.kt | 24 ++++ .../ui/view/handhistory/StreetHeader.kt | 9 ++ .../ui/viewmodel/HandHistoryViewModel.kt | 19 +++ .../main/res/layout/activity_hand_history.xml | 7 ++ .../main/res/layout/fragment_hand_history.xml | 27 +++++ app/src/main/res/layout/row_hand_action.xml | 6 + app/src/main/res/layout/row_hand_street.xml | 6 + 17 files changed, 321 insertions(+), 16 deletions(-) rename app/src/main/java/net/pokeranalytics/android/model/handhistory/{Builder.kt => HHBuilder.kt} (93%) create mode 100644 app/src/main/java/net/pokeranalytics/android/ui/activity/HandHistoryActivity.kt create mode 100644 app/src/main/java/net/pokeranalytics/android/ui/fragment/HandHistoryFragment.kt create mode 100644 app/src/main/java/net/pokeranalytics/android/ui/view/handhistory/StreetHeader.kt create mode 100644 app/src/main/java/net/pokeranalytics/android/ui/viewmodel/HandHistoryViewModel.kt create mode 100644 app/src/main/res/layout/activity_hand_history.xml create mode 100644 app/src/main/res/layout/fragment_hand_history.xml create mode 100644 app/src/main/res/layout/row_hand_action.xml create mode 100644 app/src/main/res/layout/row_hand_street.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2145a55a..d833681a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -71,6 +71,11 @@ android:launchMode="singleTop" android:screenOrientation="portrait" /> + + { + val rows: MutableList = mutableListOf() + + var potSize = 0.0 + Street.values().forEach { street -> + rows.add(StreetHeader(street, this.handHistory.cardsForStreet(street), potSize)) + val actions = this.sortedActions.filter { it.action.street == street.ordinal } + rows.addAll(actions) + potSize = actions.last().totalPotSize + } + + return rows + } + } \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/model/handhistory/Street.kt b/app/src/main/java/net/pokeranalytics/android/model/handhistory/Street.kt index b4120df6..f823dd0c 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/handhistory/Street.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/handhistory/Street.kt @@ -4,6 +4,16 @@ enum class Street { PREFLOP, FLOP, TURN, - RIVER + RIVER; + + val totalBoardCards: Int + get() { + return when (this) { + PREFLOP -> 0 + FLOP -> 3 + TURN -> 4 + RIVER -> 5 + } + } } diff --git a/app/src/main/java/net/pokeranalytics/android/model/migrations/PokerAnalyticsMigration.kt b/app/src/main/java/net/pokeranalytics/android/model/migrations/PokerAnalyticsMigration.kt index 843db86c..cbdec38b 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/migrations/PokerAnalyticsMigration.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/migrations/PokerAnalyticsMigration.kt @@ -191,6 +191,7 @@ class PokerAnalyticsMigration : RealmMigration { val cardSchema = schema.create("Card") cardSchema.addField("value", Int::class.java) cardSchema.addField("suit", Int::class.java) + cardSchema.addField("index", Int::class.java) hhSchema.addRealmListField("board", cardSchema) val actionSchema = schema.create("Action") diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/Card.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/Card.kt index 68637116..1273ba7e 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/Card.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/Card.kt @@ -22,4 +22,9 @@ open class Card : RealmObject() { */ var suit: Int = 0 + /*** + * The card index in a list + */ + var index: Int = 0 + } \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt index 6647bcbb..1242fe1d 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt @@ -2,16 +2,22 @@ package net.pokeranalytics.android.model.realm.handhistory import io.realm.RealmList import io.realm.RealmObject +import io.realm.annotations.Ignore import io.realm.annotations.PrimaryKey import net.pokeranalytics.android.model.handhistory.HandSetup +import net.pokeranalytics.android.model.handhistory.Street +import net.pokeranalytics.android.model.interfaces.Identifiable import net.pokeranalytics.android.model.realm.Session import java.util.* -open class HandHistory : RealmObject() { +open class HandHistory : RealmObject(), Identifiable { @PrimaryKey - var id = UUID.randomUUID().toString() + override var id = UUID.randomUUID().toString() + + @Ignore + override val realmObjectClass: Class = HandHistory::class.java /*** * The date of the hand history @@ -92,13 +98,15 @@ open class HandHistory : RealmObject() { action.type = Action.Type.POST_BB action.amount = this.bigBlind } - else -> { - - } + else -> {} } this.actions.add(action) } } + fun cardsForStreet(street: Street): List { + return this.board.sortedBy { it.index }.drop(street.totalBoardCards) + } + } \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/ui/activity/HandHistoryActivity.kt b/app/src/main/java/net/pokeranalytics/android/ui/activity/HandHistoryActivity.kt new file mode 100644 index 00000000..522eb07d --- /dev/null +++ b/app/src/main/java/net/pokeranalytics/android/ui/activity/HandHistoryActivity.kt @@ -0,0 +1,48 @@ +package net.pokeranalytics.android.ui.activity + +import android.content.Intent +import android.os.Bundle +import androidx.fragment.app.Fragment +import net.pokeranalytics.android.R +import net.pokeranalytics.android.ui.activity.components.BaseActivity +import net.pokeranalytics.android.ui.activity.components.RequestCode +import net.pokeranalytics.android.ui.fragment.HandHistoryFragment + +class HandHistoryActivity : BaseActivity() { + + enum class IntentKey(val keyName: String) { + IDENTIFIER("identifier") + } + + companion object { + + fun newInstance(fragment: Fragment, id: String?) { + val intent = Intent(fragment.requireContext(), DataListActivity::class.java) + id?.let { intent.putExtra(IntentKey.IDENTIFIER.keyName, it) } + fragment.startActivityForResult(intent, RequestCode.NEW_HAND_HISTORY.value) + } + + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_hand_history) + + initUI() + } + + /** + * Init UI + */ + private fun initUI() { + + val fragmentTransaction = supportFragmentManager.beginTransaction() + + val handHistoryId = intent.getStringExtra(IntentKey.IDENTIFIER.keyName) + val fragment = HandHistoryFragment.newInstance(handHistoryId) + fragmentTransaction.add(R.id.container, fragment) + fragmentTransaction.commit() + + } + +} \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/ui/activity/components/Codes.kt b/app/src/main/java/net/pokeranalytics/android/ui/activity/components/Codes.kt index 8ea9aa87..3a5465a8 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/activity/components/Codes.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/activity/components/Codes.kt @@ -10,6 +10,7 @@ enum class RequestCode(var value: Int) { NEW_SESSION(800), NEW_TRANSACTION(801), NEW_REPORT(802), + NEW_HAND_HISTORY(803), IMPORT(900), SUBSCRIPTION(901), CURRENCY(902), diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/HandHistoryFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/HandHistoryFragment.kt new file mode 100644 index 00000000..7dd4fe74 --- /dev/null +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/HandHistoryFragment.kt @@ -0,0 +1,112 @@ +package net.pokeranalytics.android.ui.fragment + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.lifecycle.ViewModelProviders +import androidx.recyclerview.widget.LinearLayoutManager +import kotlinx.android.synthetic.main.fragment_settings.* +import net.pokeranalytics.android.R +import net.pokeranalytics.android.exceptions.PAIllegalStateException +import net.pokeranalytics.android.model.handhistory.HHBuilder +import net.pokeranalytics.android.model.handhistory.HandSetup +import net.pokeranalytics.android.model.realm.handhistory.HandHistory +import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter +import net.pokeranalytics.android.ui.adapter.RowRepresentableDataSource +import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate +import net.pokeranalytics.android.ui.fragment.components.RealmFragment +import net.pokeranalytics.android.ui.view.RowRepresentable +import net.pokeranalytics.android.ui.viewmodel.HandHistoryViewModel +import net.pokeranalytics.android.util.extensions.findById + +class HandHistoryFragment : RealmFragment(), RowRepresentableDataSource, RowRepresentableDelegate { + + private lateinit var model: HandHistoryViewModel + + private lateinit var handHistoryAdapter: RowRepresentableAdapter + + private var rows: List = listOf() + + companion object { + + fun newInstance(id: String? = null): HandHistoryFragment { + val fragment = HandHistoryFragment() + val bundle = Bundle() + bundle.putSerializable(BundleKey.PRIMARY_KEY.value, id) + fragment.arguments = bundle + return fragment + } + + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + model = activity?.run { + ViewModelProviders.of(this)[HandHistoryViewModel::class.java] + } ?: throw Exception("Invalid Activity") + + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.fragment_hand_history, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + initData() + initUI() + } + + private fun initData() { + + val handHistoryId = this.arguments?.getString(BundleKey.PRIMARY_KEY.value) + val builder = handHistoryId?.let { + val handHistory = getRealm().findById(it) ?: throw PAIllegalStateException("HandHistory not found") + HHBuilder(handHistory) + } ?: run { + HHBuilder(HandSetup()) + } + this.model.setBuilder(builder) + + } + + private fun initUI() { + + setToolbarTitle(getString(R.string.hand_history)) + + setDisplayHomeAsUpEnabled(true) + + val viewManager = LinearLayoutManager(requireContext()) + handHistoryAdapter = RowRepresentableAdapter(this, this) + + recyclerView.apply { + setHasFixedSize(true) + layoutManager = viewManager + adapter = handHistoryAdapter + } + + this.rows = this.model.builder.value?.rowRepresentables() ?: listOf() + + } + + // RowRepresentableDataSource + + override fun adapterRows(): List? { + return this.rows + } + + override fun rowRepresentableForPosition(position: Int): RowRepresentable? { + return this.rows[position] + } + + override fun numberOfRows(): Int { + return this.rows.size + } + + override fun viewTypeForPosition(position: Int): Int { + return this.rows[position].viewType + } + +} \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt b/app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt index 08093edb..f957f5d2 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt @@ -94,6 +94,8 @@ enum class RowViewType(private var layoutRes: Int) { LIST(R.layout.row_list), ROW_PLAYER(R.layout.row_player), ROW_PLAYER_IMAGE(R.layout.row_player_image), + ROW_HAND_ACTION(R.layout.row_hand_action), + ROW_HAND_STREET(R.layout.row_hand_street), // Separator SEPARATOR(R.layout.row_separator); @@ -142,6 +144,10 @@ enum class RowViewType(private var layoutRes: Int) { GRAPH -> GraphViewHolder(layout) LEGEND_DEFAULT -> LegendDefaultViewHolder(layout) + // Hand History + ROW_HAND_ACTION -> RowHandAction(layout) + ROW_HAND_STREET -> RowHandStreet(layout) + // Separator SEPARATOR -> SeparatorViewHolder(layout) @@ -675,6 +681,24 @@ enum class RowViewType(private var layoutRes: Int) { } } + /** + * Display a hand action + */ + inner class RowHandAction(itemView: View) : RecyclerView.ViewHolder(itemView), BindableHolder { + override fun bind(position: Int, row: RowRepresentable, adapter: RowRepresentableAdapter) { + + } + } + + /** + * Display a hand street + */ + inner class RowHandStreet(itemView: View) : RecyclerView.ViewHolder(itemView), BindableHolder { + override fun bind(position: Int, row: RowRepresentable, adapter: RowRepresentableAdapter) { + + } + } + /** * Display a separator */ diff --git a/app/src/main/java/net/pokeranalytics/android/ui/view/handhistory/StreetHeader.kt b/app/src/main/java/net/pokeranalytics/android/ui/view/handhistory/StreetHeader.kt new file mode 100644 index 00000000..dde88bbf --- /dev/null +++ b/app/src/main/java/net/pokeranalytics/android/ui/view/handhistory/StreetHeader.kt @@ -0,0 +1,9 @@ +package net.pokeranalytics.android.ui.view.handhistory + +import net.pokeranalytics.android.model.handhistory.Street +import net.pokeranalytics.android.model.realm.handhistory.Card +import net.pokeranalytics.android.ui.view.RowRepresentable + +class StreetHeader(var street: Street, var cards: List, var potSize: Double) : RowRepresentable { + +} \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/ui/viewmodel/HandHistoryViewModel.kt b/app/src/main/java/net/pokeranalytics/android/ui/viewmodel/HandHistoryViewModel.kt new file mode 100644 index 00000000..87c7bb21 --- /dev/null +++ b/app/src/main/java/net/pokeranalytics/android/ui/viewmodel/HandHistoryViewModel.kt @@ -0,0 +1,19 @@ +package net.pokeranalytics.android.ui.viewmodel + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import net.pokeranalytics.android.model.handhistory.HHBuilder + +class HandHistoryViewModel : ViewModel() { + + var builder = MutableLiveData() + + fun setBuilder(builder: HHBuilder) { + this.builder.value = builder + } + + fun test() { +// this.builder. + } + +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_hand_history.xml b/app/src/main/res/layout/activity_hand_history.xml new file mode 100644 index 00000000..3451ec02 --- /dev/null +++ b/app/src/main/res/layout/activity_hand_history.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_hand_history.xml b/app/src/main/res/layout/fragment_hand_history.xml new file mode 100644 index 00000000..51122938 --- /dev/null +++ b/app/src/main/res/layout/fragment_hand_history.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/row_hand_action.xml b/app/src/main/res/layout/row_hand_action.xml new file mode 100644 index 00000000..3509b841 --- /dev/null +++ b/app/src/main/res/layout/row_hand_action.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/row_hand_street.xml b/app/src/main/res/layout/row_hand_street.xml new file mode 100644 index 00000000..3509b841 --- /dev/null +++ b/app/src/main/res/layout/row_hand_street.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file From 42001c8f0de4e8384949eec974e4c986de59aa2f Mon Sep 17 00:00:00 2001 From: Laurent Date: Mon, 13 Jan 2020 17:39:00 +0100 Subject: [PATCH 017/399] Refactoring + todos --- .../android/model/handhistory/HHBuilder.kt | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/net/pokeranalytics/android/model/handhistory/HHBuilder.kt b/app/src/main/java/net/pokeranalytics/android/model/handhistory/HHBuilder.kt index 58495327..c29a249b 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/handhistory/HHBuilder.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/handhistory/HHBuilder.kt @@ -46,6 +46,14 @@ class HHBuilder { this.handHistory = handHistory } + /*** + * Returns the action + */ + private fun actionForIndex(index: Int) : ComputedAction { + return this.sortedActions[index] +// return this.sortedActions.first { it.action.index == index } + } + /*** * Fills the [actionsPerStreet] variable with the proper actions * Pre-computes the potsizes for the video export @@ -129,7 +137,7 @@ class HHBuilder { * Returns the remaining player count at the provided [index] */ private fun remainingPlayerCount(index: Int): Int { - return 0 + return 0 // TODO } /*** @@ -140,11 +148,13 @@ class HHBuilder { */ private fun selectAction(index: Int, actionType: Action.Type) : Boolean { - val computedAction = this.sortedActions.first { it.action.index == index } + val computedAction = this.actionForIndex(index) computedAction.action.type = actionType dropNextActionsIfNecessary(index) + // TODO si on set une action et que les actions precedentes ne sont pas renseignees, on fold PF ou check apres + when (actionType) { Action.Type.CALL -> { val significantAmount = getLastSignificantAction(index)?.action?.amount ?: throw PAIllegalStateException("Cannot happen") @@ -164,13 +174,12 @@ class HHBuilder { } /*** - * Removes the actions, if necessary, following a action change - * We only want to keep the first set of actions whatever happens + * Removes the actions, if necessary, following an action change + * We want drop all non-auto added rows after the index */ private fun dropNextActionsIfNecessary(index: Int) { - if (index > this.handHistory.numberOfPlayers) { - this.sortedActions.drop(index + 1) - } + val dropIndex = index + 1 // TODO determine dropIndex + this.sortedActions.drop(dropIndex) } /*** @@ -178,11 +187,13 @@ class HHBuilder { * In the case of an UNDEFINED_ALLIN, define if it's a RAISE_ALLIN or a CALL_ALLIN */ private fun setAmount(index: Int, amount: Double) { - val computedAction = this.sortedActions.first { it.action.index == index } + val computedAction = this.actionForIndex(index) val revisedAmount = computedAction.playerRemainingStack?.let { min(it, amount) } ?: amount computedAction.action.amount = revisedAmount + // TODO si on change un montant de mise, on change les calls suivants + when (computedAction.action.type) { Action.Type.UNDEFINED_ALLIN -> { getLastSignificantAction(index)?.action?.amount?.let { significantActionAmount -> @@ -215,7 +226,7 @@ class HHBuilder { * Returns the committed amount by the player for the street at the current [index] */ private fun getPreviousCommittedAmount(index: Int) : Double? { - val action = this.sortedActions.first { it.action.index == index }.action + val action = this.actionForIndex(index).action val position = action.position val street = action.street @@ -240,9 +251,7 @@ class HHBuilder { * Returns the last user action, if any, for the action at the provided [index] */ private fun getLastUserAction(index: Int): ComputedAction? { - val computedAction = this.sortedActions.first { it.action.index == index } - val action = computedAction.action - + val action = this.actionForIndex(index).action val previousActions = this.sortedActions.drop(index) return previousActions.lastOrNull { it.action.position == action.position } } From df13c43e3d2ab0a9140ef4c666b7dd2e0ee104c1 Mon Sep 17 00:00:00 2001 From: Laurent Date: Tue, 14 Jan 2020 12:03:27 +0100 Subject: [PATCH 018/399] Adds UI elements to HH --- .../android/model/handhistory/HHBuilder.kt | 24 +-- .../migrations/PokerAnalyticsMigration.kt | 5 + .../model/realm/handhistory/HandHistory.kt | 15 +- .../ui/activity/HandHistoryActivity.kt | 4 +- .../FeedHandHistoryRowRepresentableAdapter.kt | 164 +++++++++++++++++ .../android/ui/fragment/FeedFragment.kt | 173 +++++++++++------- .../android/ui/view/HandHistoryRowView.kt | 68 +++++++ .../android/ui/view/RowViewType.kt | 6 +- .../ui/view/handhistory/StreetHeader.kt | 2 + .../view/holder/RowHandHistoryViewHolder.kt | 22 +++ app/src/main/res/layout/fragment_feed.xml | 6 + app/src/main/res/layout/row_hand_action.xml | 38 +++- app/src/main/res/layout/row_hand_cards.xml | 37 ++++ app/src/main/res/layout/row_hand_history.xml | 12 ++ .../main/res/layout/row_hand_history_view.xml | 108 +++++++++++ app/src/main/res/layout/row_hand_street.xml | 6 - 16 files changed, 590 insertions(+), 100 deletions(-) create mode 100644 app/src/main/java/net/pokeranalytics/android/ui/adapter/FeedHandHistoryRowRepresentableAdapter.kt create mode 100644 app/src/main/java/net/pokeranalytics/android/ui/view/HandHistoryRowView.kt create mode 100644 app/src/main/java/net/pokeranalytics/android/ui/view/holder/RowHandHistoryViewHolder.kt create mode 100644 app/src/main/res/layout/row_hand_cards.xml create mode 100644 app/src/main/res/layout/row_hand_history.xml create mode 100644 app/src/main/res/layout/row_hand_history_view.xml delete mode 100644 app/src/main/res/layout/row_hand_street.xml diff --git a/app/src/main/java/net/pokeranalytics/android/model/handhistory/HHBuilder.kt b/app/src/main/java/net/pokeranalytics/android/model/handhistory/HHBuilder.kt index c29a249b..56ae164f 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/handhistory/HHBuilder.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/handhistory/HHBuilder.kt @@ -55,19 +55,13 @@ class HHBuilder { } /*** - * Fills the [actionsPerStreet] variable with the proper actions + * Fills the [sortedActions] variable with the proper actions * Pre-computes the potsizes for the video export */ private fun load() { -// var potSize = 0.0 var totalPotSize = 0.0 - // on veut sortir: - // une liste d'action pour générer des images / des animations => ImageRepresentable? - // une liste de lignes à afficher dans un tableau => RowRepresentable - // => ne pas oublier flop / turn / river / résumé - // sorted actions val computedActions = mutableListOf() val sortedActions = this.handHistory.actions.sortedBy { it.index } @@ -78,15 +72,6 @@ class HHBuilder { } this.sortedActions = computedActions -// Street.values().forEach { street -> -// val filteredActions = sortedActions.filter { it.street == street.ordinal } -// val computedActions = filteredActions.map { action -> -// totalPotSize += action.effectiveAmount -// ComputedAction(action, potSize, totalPotSize, action.positionRemainingStack) -// } -// this.actionsPerStreet[street] = computedActions -// potSize = totalPotSize -// } } /*** @@ -265,7 +250,7 @@ class HHBuilder { } /*** - * Saves the current hand state, represented by [actionsPerStreet], in the database + * Saves the current hand state in the database */ private fun save() { @@ -279,7 +264,10 @@ class HHBuilder { rows.add(StreetHeader(street, this.handHistory.cardsForStreet(street), potSize)) val actions = this.sortedActions.filter { it.action.street == street.ordinal } rows.addAll(actions) - potSize = actions.last().totalPotSize + + if (actions.isNotEmpty()) { + potSize = actions.last().totalPotSize + } } return rows diff --git a/app/src/main/java/net/pokeranalytics/android/model/migrations/PokerAnalyticsMigration.kt b/app/src/main/java/net/pokeranalytics/android/model/migrations/PokerAnalyticsMigration.kt index cbdec38b..90601f1d 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/migrations/PokerAnalyticsMigration.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/migrations/PokerAnalyticsMigration.kt @@ -187,6 +187,11 @@ class PokerAnalyticsMigration : RealmMigration { hhSchema.addField("numberOfPlayers", Int::class.java) hhSchema.addField("comment", String::class.java) hhSchema.addField("heroIndex", Int::class.java).setRequired("heroIndex", false) + hhSchema.addField("dayOfWeek", Integer::class.java) + hhSchema.addField("month", Integer::class.java) + hhSchema.addField("year", Integer::class.java) + hhSchema.addField("dayOfMonth", Integer::class.java) + val cardSchema = schema.create("Card") cardSchema.addField("value", Int::class.java) diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt index 1242fe1d..6e0e8d48 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt @@ -4,14 +4,17 @@ import io.realm.RealmList import io.realm.RealmObject import io.realm.annotations.Ignore import io.realm.annotations.PrimaryKey +import net.pokeranalytics.android.model.filter.Filterable import net.pokeranalytics.android.model.handhistory.HandSetup import net.pokeranalytics.android.model.handhistory.Street import net.pokeranalytics.android.model.interfaces.Identifiable +import net.pokeranalytics.android.model.interfaces.TimeFilterable import net.pokeranalytics.android.model.realm.Session +import net.pokeranalytics.android.ui.view.RowRepresentable import java.util.* -open class HandHistory : RealmObject(), Identifiable { +open class HandHistory : RealmObject(), RowRepresentable, Identifiable, Filterable, TimeFilterable { @PrimaryKey override var id = UUID.randomUUID().toString() @@ -23,6 +26,10 @@ open class HandHistory : RealmObject(), Identifiable { * The date of the hand history */ var date: Date = Date() + set(value) { + field = value + this.updateTimeParameter(field) + } /*** * The session whose hand was played @@ -79,6 +86,12 @@ open class HandHistory : RealmObject(), Identifiable { */ var playerSetups: RealmList = RealmList() + // Timed interface + override var dayOfWeek: Int? = null + override var month: Int? = null + override var year: Int? = null + override var dayOfMonth: Int? = null + fun configure(handSetup: HandSetup) { handSetup.tableSize?.let { this.numberOfPlayers = it } diff --git a/app/src/main/java/net/pokeranalytics/android/ui/activity/HandHistoryActivity.kt b/app/src/main/java/net/pokeranalytics/android/ui/activity/HandHistoryActivity.kt index 522eb07d..3b8cbb9c 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/activity/HandHistoryActivity.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/activity/HandHistoryActivity.kt @@ -16,8 +16,8 @@ class HandHistoryActivity : BaseActivity() { companion object { - fun newInstance(fragment: Fragment, id: String?) { - val intent = Intent(fragment.requireContext(), DataListActivity::class.java) + fun newInstance(fragment: Fragment, id: String? = null) { + val intent = Intent(fragment.requireContext(), HandHistoryActivity::class.java) id?.let { intent.putExtra(IntentKey.IDENTIFIER.keyName, it) } fragment.startActivityForResult(intent, RequestCode.NEW_HAND_HISTORY.value) } diff --git a/app/src/main/java/net/pokeranalytics/android/ui/adapter/FeedHandHistoryRowRepresentableAdapter.kt b/app/src/main/java/net/pokeranalytics/android/ui/adapter/FeedHandHistoryRowRepresentableAdapter.kt new file mode 100644 index 00000000..ac6f60f5 --- /dev/null +++ b/app/src/main/java/net/pokeranalytics/android/ui/adapter/FeedHandHistoryRowRepresentableAdapter.kt @@ -0,0 +1,164 @@ +package net.pokeranalytics.android.ui.adapter + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.widget.AppCompatTextView +import androidx.recyclerview.widget.RecyclerView +import io.realm.RealmResults +import kotlinx.android.synthetic.main.row_hand_history.view.* +import net.pokeranalytics.android.R +import net.pokeranalytics.android.model.realm.handhistory.HandHistory +import net.pokeranalytics.android.ui.view.BindableHolder +import net.pokeranalytics.android.ui.view.RowViewType +import net.pokeranalytics.android.util.NULL_TEXT +import net.pokeranalytics.android.util.extensions.getMonthAndYear +import java.util.* +import kotlin.collections.HashMap + + +/** + * An adapter capable of displaying a list of RowRepresentables + * @param dataSource the datasource providing rows + * @param delegate the delegate, notified of UI actions + */ +class FeedHandHistoryRowRepresentableAdapter( + var delegate: RowRepresentableDelegate? = null, + var realmHandHistories: RealmResults, + var distinctHandHistoryHeaders: RealmResults +) : + RecyclerView.Adapter() { + + private var headersPositions = HashMap() + private lateinit var sortedHeaders: SortedMap + + init { + refreshData() + } + + inner class RowHandHistoryViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), + BindableHolder { + + fun bind(position: Int, row: HandHistory?, adapter: FeedHandHistoryRowRepresentableAdapter) { + + itemView.handHistoryRow.setData(row as HandHistory) + val listener = View.OnClickListener { + adapter.delegate?.onRowSelected(position, row) + } + itemView.handHistoryRow.setOnClickListener(listener) + } + + } + /** + * Display a header + */ + inner class HeaderTitleViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), + BindableHolder { + fun bind(title: String) { + // Title + itemView.findViewById(R.id.title)?.let { + it.text = title + } + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return if (viewType == RowViewType.ROW_TRANSACTION.ordinal) { + val layout = LayoutInflater.from(parent.context).inflate(R.layout.row_transaction, parent, false) + RowHandHistoryViewHolder(layout) + } else { + val layout = LayoutInflater.from(parent.context).inflate(R.layout.row_header_title, parent, false) + HeaderTitleViewHolder(layout) + } + + } + + + override fun getItemViewType(position: Int): Int { + return if (sortedHeaders.containsKey(position)) { + RowViewType.HEADER_TITLE.ordinal + } else { + 1 +// RowViewType.ROW_HAND_HISTORY.ordinal + } + } + + override fun getItemCount(): Int { + return realmHandHistories.size + distinctHandHistoryHeaders.size + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + if (holder is RowHandHistoryViewHolder) { + holder.bind(position, getHandHistoryForPosition(position), this) + } else if (holder is HeaderTitleViewHolder) { + holder.bind(getHeaderForPosition(position)) + } + } + + /** + * Return the header + */ + private fun getHeaderForPosition(position: Int): String { + if (sortedHeaders.containsKey(position)) { + val realmHeaderPosition = sortedHeaders.keys.indexOf(position) + return distinctHandHistoryHeaders[realmHeaderPosition]?.date?.getMonthAndYear() ?: "" + } + return NULL_TEXT + } + + /** + * Get real index + */ + private fun getHandHistoryForPosition(position: Int): HandHistory? { + + // Row position + var headersBefore = 0 + for (key in sortedHeaders.keys) { + if (position > key) { + headersBefore++ + } else { + break + } + } + + return realmHandHistories[position - headersBefore] + } + + /** + * Refresh headers positions + */ + fun refreshData() { + + headersPositions.clear() + + var previousYear = Int.MAX_VALUE + var previousMonth = Int.MAX_VALUE + + val calendar = Calendar.getInstance() + + // Add headers if the date doesn't exist yet + for ((index, handHistory) in realmHandHistories.withIndex()) { + calendar.time = handHistory.date + if (checkHeaderCondition(calendar, previousYear, previousMonth)) { + headersPositions[index + headersPositions.size] = handHistory.date + previousYear = calendar.get(Calendar.YEAR) + previousMonth = calendar.get(Calendar.MONTH) + } + } + + sortedHeaders = headersPositions.toSortedMap() + + } + + /** + * Check if we need to add a header + * Can be change to manage different condition + */ + private fun checkHeaderCondition(currentCalendar: Calendar, previousYear: Int, previousMonth: Int): Boolean { + return currentCalendar.get(Calendar.YEAR) == previousYear && currentCalendar.get(Calendar.MONTH) < previousMonth || (currentCalendar.get( + Calendar.YEAR) < previousYear) + + } + + +} \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/FeedFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/FeedFragment.kt index 0300ac10..e02b4363 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/FeedFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/FeedFragment.kt @@ -2,10 +2,6 @@ package net.pokeranalytics.android.ui.fragment import android.app.Activity.RESULT_OK import android.content.Intent -import android.graphics.Bitmap -import android.graphics.Canvas -import android.graphics.Paint -import android.graphics.Rect import android.os.Bundle import android.view.* import android.widget.Toast @@ -25,12 +21,13 @@ import net.pokeranalytics.android.model.interfaces.Editable import net.pokeranalytics.android.model.realm.Filter import net.pokeranalytics.android.model.realm.Session import net.pokeranalytics.android.model.realm.Transaction +import net.pokeranalytics.android.model.realm.handhistory.HandHistory import net.pokeranalytics.android.ui.activity.* import net.pokeranalytics.android.ui.activity.components.RequestCode +import net.pokeranalytics.android.ui.adapter.FeedHandHistoryRowRepresentableAdapter import net.pokeranalytics.android.ui.adapter.FeedSessionRowRepresentableAdapter import net.pokeranalytics.android.ui.adapter.FeedTransactionRowRepresentableAdapter import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate -import net.pokeranalytics.android.ui.extensions.toByteArray import net.pokeranalytics.android.ui.fragment.components.FilterableFragment import net.pokeranalytics.android.ui.interfaces.FilterActivityRequestCode import net.pokeranalytics.android.ui.interfaces.FilterableType @@ -39,8 +36,6 @@ import net.pokeranalytics.android.ui.view.SmoothScrollLinearLayoutManager import net.pokeranalytics.android.util.Preferences import net.pokeranalytics.android.util.billing.AppGuard import net.pokeranalytics.android.util.extensions.count -import net.pokeranalytics.android.util.video.MMediaMuxer -import timber.log.Timber import java.text.SimpleDateFormat import java.util.* @@ -48,7 +43,8 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { private enum class Tab { SESSIONS, - TRANSACTIONS + TRANSACTIONS, + HAND_HISTORY } companion object { @@ -66,10 +62,12 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { private var currentTab = Tab.SESSIONS - private lateinit var feedSessionAdapter: FeedSessionRowRepresentableAdapter - private lateinit var feedTransactionAdapter: FeedTransactionRowRepresentableAdapter + private lateinit var sessionAdapter: FeedSessionRowRepresentableAdapter + private lateinit var transactionAdapter: FeedTransactionRowRepresentableAdapter + private lateinit var handHistoryAdapter: FeedHandHistoryRowRepresentableAdapter private lateinit var realmTransactions: RealmResults + private lateinit var realmHandHistories: RealmResults private lateinit var betaLimitDate: Date private var newSessionCreated: Boolean = false @@ -78,7 +76,7 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { private var selectedTransactionPosition: Int = -1 override val observedEntities: List> = - listOf(Session::class.java, Transaction::class.java) + listOf(Session::class.java, Transaction::class.java, HandHistory::class.java) override fun entitiesChanged( clazz: Class, @@ -88,12 +86,16 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { when (clazz.kotlin) { Session::class -> { - this.feedSessionAdapter.refreshData() - this.feedSessionAdapter.notifyDataSetChanged() + this.sessionAdapter.refreshData() + this.sessionAdapter.notifyDataSetChanged() } Transaction::class -> { - this.feedTransactionAdapter.refreshData() - this.feedTransactionAdapter.notifyDataSetChanged() + this.transactionAdapter.refreshData() + this.transactionAdapter.notifyDataSetChanged() + } + HandHistory::class -> { + this.handHistoryAdapter.refreshData() + this.handHistoryAdapter.notifyDataSetChanged() } } @@ -133,7 +135,7 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { when (item?.itemId) { R.id.duplicate -> { - val sessionId = this.feedSessionAdapter.sessionIdForPosition(menuPosition) + val sessionId = this.sessionAdapter.sessionIdForPosition(menuPosition) if (sessionId != null) { createNewSession(true, sessionId = sessionId, duplicate = true) } else { @@ -204,6 +206,8 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { this.selectTab(Tab.TRANSACTIONS) } else if (requestCode == RequestCode.NEW_SESSION.value && resultCode == RESULT_OK) { this.selectTab(Tab.SESSIONS) + } else if (requestCode == RequestCode.NEW_HAND_HISTORY.value && resultCode == RESULT_OK) { + this.selectTab(Tab.HAND_HISTORY) } } @@ -229,6 +233,9 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { RequestCode.FEED_TRANSACTION_DETAILS.value ) } + is HandHistory -> { + HandHistoryActivity.newInstance(this, row.id) + } } } @@ -237,7 +244,7 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { */ private fun initUI() { - this.feedSessionAdapter = FeedSessionRowRepresentableAdapter(getRealm(), this) + this.sessionAdapter = FeedSessionRowRepresentableAdapter(getRealm(), this) registerForContextMenu(this.menuRecyclerView) @@ -287,6 +294,9 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { Tab.TRANSACTIONS.ordinal -> { currentFilterable = FilterableType.TRANSACTION } + Tab.HAND_HISTORY.ordinal -> { + currentFilterable = FilterableType.HAND_HISTORY + } } tabChanged(tab.position) } @@ -331,10 +341,10 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { when (filter?.filterableType) { FilterableType.SESSION -> { - this.feedSessionAdapter.filter = filter + this.sessionAdapter.filter = filter } else -> { - this.feedSessionAdapter.filter = null + this.sessionAdapter.filter = null } } } @@ -357,7 +367,7 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { getRealm().where().distinct("year", "month").findAll() } distinctDateTransactions = distinctDateTransactions.sort("date", Sort.DESCENDING) - this.feedTransactionAdapter = + this.transactionAdapter = FeedTransactionRowRepresentableAdapter( this, realmTransactions, @@ -366,6 +376,33 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { } + private fun loadHandHistories(filter: Filter? = null) { + val handHistoryFilter: Filter? = filter?.let { + if (it.filterableType == FilterableType.HAND_HISTORY) { + it + } else { + null + } + } + + // Transactions + this.realmHandHistories = + handHistoryFilter?.results() ?: run { getRealm().where().findAll() } + this.realmHandHistories = this.realmHandHistories.sort("date", Sort.DESCENDING) + + var distinctDates = handHistoryFilter?.results("year", "month") ?: run { + getRealm().where().distinct("year", "month").findAll() + } + distinctDates = distinctDates.sort("date", Sort.DESCENDING) + this.handHistoryAdapter = + FeedHandHistoryRowRepresentableAdapter( + this, + realmHandHistories, + distinctDates + ) + + } + /** * Create a new cash game */ @@ -423,56 +460,52 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { */ private fun createNewHandHistory() { - Timber.d("**** Start video test") - - val width = 480 - val height = 480 + HandHistoryActivity.newInstance(this) -// val testView = View(requireContext()) -// testView.setBackgroundColor(requireContext().getColor(R.color.blue)) -// testView.layoutParams = ViewGroup.LayoutParams(width, height) +// Timber.d("**** Start video test") // -// val videoView = VideoView(requireContext()) - - val bitmap = Bitmap.createBitmap(480, 480, Bitmap.Config.ARGB_8888) - val canvas = Canvas(bitmap) - - val paint = Paint() - paint.isAntiAlias = true - paint.style = Paint.Style.STROKE - paint.strokeWidth = 20.0.toFloat() - paint.color = requireContext().getColor(R.color.blue) - - canvas.drawRect(Rect(0,0, width, height), paint) - - bitmap.let { - - val muxer = MMediaMuxer() - - Timber.d("width = ${it.width}, height = ${it.height}") - - val width = (it.width / 2) * 2 - val height= (it.height / 2) * 2 - - muxer.Init(requireActivity(), width, height, "hhVideo", "YES!") - - Timber.d("**** Adds frames") - for (i in 0..50) { - - try { - val byteArray = it.toByteArray() - muxer.AddFrame(byteArray) - } catch (e: Exception) { - Timber.e("error = ${e.message}") - } - } - Timber.d("**** Create video") - muxer.CreateVideo() - - val path = muxer.GetPath() - Timber.d("**** Video path = $path") - } +// val width = 480 +// val height = 480 +// +// val bitmap = Bitmap.createBitmap(480, 480, Bitmap.Config.ARGB_8888) +// val canvas = Canvas(bitmap) +// +// val paint = Paint() +// paint.isAntiAlias = true +// paint.style = Paint.Style.STROKE +// paint.strokeWidth = 20.0.toFloat() +// paint.color = requireContext().getColor(R.color.blue) +// +// canvas.drawRect(Rect(0,0, width, height), paint) +// +// bitmap.let { +// +// val muxer = MMediaMuxer() +// +// Timber.d("width = ${it.width}, height = ${it.height}") +// +// val width = (it.width / 2) * 2 +// val height= (it.height / 2) * 2 +// +// muxer.Init(requireActivity(), width, height, "hhVideo", "YES!") +// +// Timber.d("**** Adds frames") +// for (i in 0..50) { +// +// try { +// val byteArray = it.toByteArray() +// muxer.AddFrame(byteArray) +// } catch (e: Exception) { +// Timber.e("error = ${e.message}") +// } +// } +// Timber.d("**** Create video") +// muxer.CreateVideo() +// +// val path = muxer.GetPath() +// Timber.d("**** Video path = $path") +// } } @@ -506,6 +539,7 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { val filter: Filter? = this.currentFilter(this.requireContext(), getRealm()) this.loadSessions(filter) this.loadTransactions(filter) + this.loadHandHistories(filter) filter?.let { when (it.filterableType) { @@ -531,6 +565,7 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { super.removeFilter() this.loadSessions() this.loadTransactions() + this.loadHandHistories() this.setAdapter() } @@ -547,8 +582,8 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { private fun setAdapter() { when (this.currentTab) { - Tab.SESSIONS -> menuRecyclerView.adapter = feedSessionAdapter - Tab.TRANSACTIONS -> menuRecyclerView.adapter = feedTransactionAdapter + Tab.SESSIONS -> menuRecyclerView.adapter = sessionAdapter + Tab.TRANSACTIONS -> menuRecyclerView.adapter = transactionAdapter } } diff --git a/app/src/main/java/net/pokeranalytics/android/ui/view/HandHistoryRowView.kt b/app/src/main/java/net/pokeranalytics/android/ui/view/HandHistoryRowView.kt new file mode 100644 index 00000000..6b163de6 --- /dev/null +++ b/app/src/main/java/net/pokeranalytics/android/ui/view/HandHistoryRowView.kt @@ -0,0 +1,68 @@ +package net.pokeranalytics.android.ui.view + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import android.widget.FrameLayout +import androidx.constraintlayout.widget.ConstraintLayout +import kotlinx.android.synthetic.main.row_transaction_view.view.* +import net.pokeranalytics.android.R +import net.pokeranalytics.android.model.realm.handhistory.HandHistory +import net.pokeranalytics.android.util.extensions.getDayNumber +import net.pokeranalytics.android.util.extensions.getShortDayName + +/** + * Display a transaction row + */ +class HandHistoryRowView : FrameLayout { + + private lateinit var rowHandHistory: ConstraintLayout + + /** + * Constructors + */ + constructor(context: Context) : super(context) { + init() + } + + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) { + init() + } + + constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { + init() + } + + /** + * Init + */ + private fun init() { + val layoutInflater = LayoutInflater.from(context) + rowHandHistory = layoutInflater.inflate(R.layout.row_hand_history_view, this, false) as ConstraintLayout + val layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT) + addView(rowHandHistory, layoutParams) + } + + /** + * Set the session data to the view + */ + fun setData(handHistory: HandHistory) { + + // Date + rowHandHistory.transactionDateDay.text = handHistory.date.getShortDayName() + rowHandHistory.transactionDateNumber.text = handHistory.date.getDayNumber() + + // Title / Game type +// var title = handHistory.type?.name ?: "" + " " + handHistory.comment +// var subtitle = handHistory.bankroll?.name +// +// rowHandHistory.transactionTitle.text = title +// rowHandHistory.transactionSubtitle.text = subtitle +// +// Amount +// val formattedStat = ComputedStat(Stat.NET_RESULT, handHistory.amount, currency = handHistory.bankroll?.utilCurrency).format() +// rowHandHistory.transactionAmount.setTextFormat(formattedStat, context) + + } + +} \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt b/app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt index f957f5d2..4568665e 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt @@ -84,6 +84,7 @@ enum class RowViewType(private var layoutRes: Int) { // Custom row ROW_SESSION(R.layout.row_feed_session), ROW_TRANSACTION(R.layout.row_transaction), +// ROW_HAND_HISTORY(R.layout.row_hand_history), ROW_TOP_10(R.layout.row_top_10), ROW_BUTTON(R.layout.row_button), ROW_FOLLOW_US(R.layout.row_follow_us), @@ -95,7 +96,7 @@ enum class RowViewType(private var layoutRes: Int) { ROW_PLAYER(R.layout.row_player), ROW_PLAYER_IMAGE(R.layout.row_player_image), ROW_HAND_ACTION(R.layout.row_hand_action), - ROW_HAND_STREET(R.layout.row_hand_street), + ROW_HAND_STREET(R.layout.row_hand_cards), // Separator SEPARATOR(R.layout.row_separator); @@ -124,7 +125,6 @@ enum class RowViewType(private var layoutRes: Int) { // Row Transaction ROW_TOP_10 -> RowTop10ViewHolder(layout) - ROW_PLAYER -> RowPlayerViewHolder(layout) ROW_PLAYER_IMAGE -> RowPlayerImageViewHolder(layout) @@ -145,6 +145,7 @@ enum class RowViewType(private var layoutRes: Int) { LEGEND_DEFAULT -> LegendDefaultViewHolder(layout) // Hand History +// ROW_HAND_HISTORY -> RowHandHistoryViewHolder(layout) ROW_HAND_ACTION -> RowHandAction(layout) ROW_HAND_STREET -> RowHandStreet(layout) @@ -579,7 +580,6 @@ enum class RowViewType(private var layoutRes: Int) { } } - /** * Display a top 10 row */ diff --git a/app/src/main/java/net/pokeranalytics/android/ui/view/handhistory/StreetHeader.kt b/app/src/main/java/net/pokeranalytics/android/ui/view/handhistory/StreetHeader.kt index dde88bbf..776681e0 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/view/handhistory/StreetHeader.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/view/handhistory/StreetHeader.kt @@ -6,4 +6,6 @@ import net.pokeranalytics.android.ui.view.RowRepresentable class StreetHeader(var street: Street, var cards: List, var potSize: Double) : RowRepresentable { + override val viewType: Int = 0 + } \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/ui/view/holder/RowHandHistoryViewHolder.kt b/app/src/main/java/net/pokeranalytics/android/ui/view/holder/RowHandHistoryViewHolder.kt new file mode 100644 index 00000000..89da7c52 --- /dev/null +++ b/app/src/main/java/net/pokeranalytics/android/ui/view/holder/RowHandHistoryViewHolder.kt @@ -0,0 +1,22 @@ +package net.pokeranalytics.android.ui.view.holder + +import android.view.View +import androidx.recyclerview.widget.RecyclerView +import kotlinx.android.synthetic.main.row_hand_history.view.* +import net.pokeranalytics.android.model.realm.handhistory.HandHistory +import net.pokeranalytics.android.ui.adapter.FeedHandHistoryRowRepresentableAdapter +import net.pokeranalytics.android.ui.view.BindableHolder + +class RowHandHistoryViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), + BindableHolder { + + fun bind(position: Int, row: HandHistory?, adapter: FeedHandHistoryRowRepresentableAdapter) { + + itemView.handHistoryRow.setData(row as HandHistory) + val listener = View.OnClickListener { + adapter.delegate?.onRowSelected(position, row) + } + itemView.handHistoryRow.setOnClickListener(listener) + } + +} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_feed.xml b/app/src/main/res/layout/fragment_feed.xml index 3c58f015..54513ca7 100644 --- a/app/src/main/res/layout/fragment_feed.xml +++ b/app/src/main/res/layout/fragment_feed.xml @@ -44,6 +44,12 @@ android:layout_height="wrap_content" android:text="@string/operations" /> + + diff --git a/app/src/main/res/layout/row_hand_action.xml b/app/src/main/res/layout/row_hand_action.xml index 3509b841..a5ae5d16 100644 --- a/app/src/main/res/layout/row_hand_action.xml +++ b/app/src/main/res/layout/row_hand_action.xml @@ -1,6 +1,42 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/row_hand_cards.xml b/app/src/main/res/layout/row_hand_cards.xml new file mode 100644 index 00000000..7217bb2c --- /dev/null +++ b/app/src/main/res/layout/row_hand_cards.xml @@ -0,0 +1,37 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/row_hand_history.xml b/app/src/main/res/layout/row_hand_history.xml new file mode 100644 index 00000000..c1d8d759 --- /dev/null +++ b/app/src/main/res/layout/row_hand_history.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/row_hand_history_view.xml b/app/src/main/res/layout/row_hand_history_view.xml new file mode 100644 index 00000000..3473977d --- /dev/null +++ b/app/src/main/res/layout/row_hand_history_view.xml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/row_hand_street.xml b/app/src/main/res/layout/row_hand_street.xml deleted file mode 100644 index 3509b841..00000000 --- a/app/src/main/res/layout/row_hand_street.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file From d604e3371f7c7a10a44ce6eacce90e74c2c1ce3c Mon Sep 17 00:00:00 2001 From: Laurent Date: Tue, 14 Jan 2020 12:13:25 +0100 Subject: [PATCH 019/399] Better error handling --- .../net/pokeranalytics/android/ui/view/RowRepresentable.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 2239258c..069d566f 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 @@ -1,6 +1,7 @@ package net.pokeranalytics.android.ui.view import android.content.Context +import net.pokeranalytics.android.exceptions.PAIllegalStateException import net.pokeranalytics.android.model.LiveData import net.pokeranalytics.android.ui.fragment.components.bottomsheet.BottomSheetType import net.pokeranalytics.android.util.NULL_TEXT @@ -57,7 +58,7 @@ interface Displayable : Localizable { */ val viewType: Int get() { - return -1 + throw PAIllegalStateException("The Displayable instance $this must return a valid viewType") } val relatedResultsRepresentable: LiveData? From b738447f063672007d9beb0a1b194aac72951da0 Mon Sep 17 00:00:00 2001 From: Laurent Date: Tue, 14 Jan 2020 12:14:39 +0100 Subject: [PATCH 020/399] Defines view type for Street Header --- .../pokeranalytics/android/ui/view/handhistory/StreetHeader.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/net/pokeranalytics/android/ui/view/handhistory/StreetHeader.kt b/app/src/main/java/net/pokeranalytics/android/ui/view/handhistory/StreetHeader.kt index 776681e0..7c54d649 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/view/handhistory/StreetHeader.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/view/handhistory/StreetHeader.kt @@ -3,9 +3,10 @@ package net.pokeranalytics.android.ui.view.handhistory import net.pokeranalytics.android.model.handhistory.Street import net.pokeranalytics.android.model.realm.handhistory.Card import net.pokeranalytics.android.ui.view.RowRepresentable +import net.pokeranalytics.android.ui.view.RowViewType class StreetHeader(var street: Street, var cards: List, var potSize: Double) : RowRepresentable { - override val viewType: Int = 0 + override val viewType: Int = RowViewType.ROW_HAND_STREET.ordinal } \ No newline at end of file From acf77888a4c9a5ac49e2314c7cf734c848202aa8 Mon Sep 17 00:00:00 2001 From: Laurent Date: Tue, 14 Jan 2020 14:03:15 +0100 Subject: [PATCH 021/399] Fixes realm crash --- .../pokeranalytics/android/ui/fragment/HandHistoryFragment.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/HandHistoryFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/HandHistoryFragment.kt index 7dd4fe74..afc51b53 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/HandHistoryFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/HandHistoryFragment.kt @@ -50,6 +50,7 @@ class HandHistoryFragment : RealmFragment(), RowRepresentableDataSource, RowRepr } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + super.onCreateView(inflater, container, savedInstanceState) return inflater.inflate(R.layout.fragment_hand_history, container, false) } From 5c2542f758981686edf938caa07b9757b1d88db8 Mon Sep 17 00:00:00 2001 From: Laurent Date: Tue, 14 Jan 2020 16:27:46 +0100 Subject: [PATCH 022/399] Progression on Hand History UI --- .../model/handhistory/ComputedAction.kt | 3 +- .../android/model/handhistory/HHBuilder.kt | 33 ++++++++++++-- .../android/model/handhistory/Position.kt | 38 ++++++++++++++++ .../android/model/handhistory/Street.kt | 13 ++++++ .../android/model/realm/handhistory/Action.kt | 28 ++++++------ .../model/realm/handhistory/HandHistory.kt | 2 +- .../android/ui/view/RowViewType.kt | 14 ++++++ .../ui/view/rowrepresentable/MoreTabRow.kt | 2 +- .../main/res/layout/fragment_hand_history.xml | 44 ++++++++++++------- app/src/main/res/layout/row_hand_action.xml | 16 +++---- app/src/main/res/layout/row_hand_cards.xml | 2 +- .../res/menu/toolbar_bankroll_details.xml | 2 +- .../res/menu/toolbar_comparison_chart.xml | 2 +- app/src/main/res/values-de/strings.xml | 17 ++++++- app/src/main/res/values-es/strings.xml | 17 ++++++- app/src/main/res/values-fr/strings.xml | 18 +++++++- app/src/main/res/values-hi/strings.xml | 16 ++++++- app/src/main/res/values-it/strings.xml | 16 ++++++- app/src/main/res/values-ja/strings.xml | 16 ++++++- app/src/main/res/values-pt/strings.xml | 16 ++++++- app/src/main/res/values-ru/strings.xml | 16 ++++++- app/src/main/res/values-v24/strings.xml | 6 +++ app/src/main/res/values-zh/strings.xml | 15 ++++++- app/src/main/res/values/strings.xml | 16 ++++++- 24 files changed, 307 insertions(+), 61 deletions(-) create mode 100644 app/src/main/java/net/pokeranalytics/android/model/handhistory/Position.kt diff --git a/app/src/main/java/net/pokeranalytics/android/model/handhistory/ComputedAction.kt b/app/src/main/java/net/pokeranalytics/android/model/handhistory/ComputedAction.kt index ae8c8717..00e4b9cb 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/handhistory/ComputedAction.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/handhistory/ComputedAction.kt @@ -6,7 +6,8 @@ import net.pokeranalytics.android.ui.view.RowViewType class ComputedAction(var action: Action, var totalPotSize: Double = 0.0, - var playerRemainingStack: Double? = null) : RowRepresentable { + var playerRemainingStack: Double? = null, + var position: Position) : RowRepresentable { /*** * Returns whether the action requires the user to enter an amount for the selected action diff --git a/app/src/main/java/net/pokeranalytics/android/model/handhistory/HHBuilder.kt b/app/src/main/java/net/pokeranalytics/android/model/handhistory/HHBuilder.kt index 56ae164f..100552f3 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/handhistory/HHBuilder.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/handhistory/HHBuilder.kt @@ -1,10 +1,14 @@ package net.pokeranalytics.android.model.handhistory +import net.pokeranalytics.android.R import net.pokeranalytics.android.exceptions.PAIllegalStateException import net.pokeranalytics.android.model.realm.handhistory.Action import net.pokeranalytics.android.model.realm.handhistory.HandHistory import net.pokeranalytics.android.ui.view.RowRepresentable +import net.pokeranalytics.android.ui.view.RowViewType import net.pokeranalytics.android.ui.view.handhistory.StreetHeader +import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable +import java.util.* import kotlin.math.min class HHBuilder { @@ -15,6 +19,7 @@ class HHBuilder { private var handHistory: HandHistory set(value) { field = value + setNumberOfPlayers(value.numberOfPlayers) load() } @@ -28,6 +33,8 @@ class HHBuilder { */ private var sortedActions: List = mutableListOf() + var positions: LinkedHashSet = linkedSetOf() + /*** * Creates a builder using a [handSetup] * Creates a new Hand History and configures it according to the [handSetup] @@ -36,7 +43,6 @@ class HHBuilder { val handHistory = HandHistory() handHistory.configure(handSetup) this.handHistory = handHistory - } /*** @@ -67,7 +73,8 @@ class HHBuilder { val sortedActions = this.handHistory.actions.sortedBy { it.index } sortedActions.forEach { action -> totalPotSize += action.effectiveAmount - val ca = ComputedAction(action, totalPotSize, action.positionRemainingStack) + val position = this.positions.elementAt(action.position) + val ca = ComputedAction(action, totalPotSize, action.positionRemainingStack, position) computedActions.add(ca) } this.sortedActions = computedActions @@ -249,6 +256,11 @@ class HHBuilder { return previousActions.lastOrNull { it.action.isActionSignificant } } + fun setNumberOfPlayers(playerCount: Int) { + this.handHistory.numberOfPlayers = playerCount + this.positions = Position.positionsPerPlayers(playerCount) + } + /*** * Saves the current hand state in the database */ @@ -259,15 +271,28 @@ class HHBuilder { fun rowRepresentables() : List { val rows: MutableList = mutableListOf() + rows.add(CustomizableRowRepresentable(customViewType = RowViewType.HEADER_TITLE, resId = R.string.settings)) + var potSize = 0.0 Street.values().forEach { street -> - rows.add(StreetHeader(street, this.handHistory.cardsForStreet(street), potSize)) + val actions = this.sortedActions.filter { it.action.street == street.ordinal } - rows.addAll(actions) if (actions.isNotEmpty()) { + // Name of the street + pot size if not preflop + rows.add(CustomizableRowRepresentable(customViewType = RowViewType.HEADER_TITLE, resId = street.resId)) + + // Cards if not preflop + if (street.totalBoardCards > 0) { + rows.add(StreetHeader(street, this.handHistory.cardsForStreet(street), potSize)) + } + + // Actions + rows.addAll(actions) + potSize = actions.last().totalPotSize } + } return rows diff --git a/app/src/main/java/net/pokeranalytics/android/model/handhistory/Position.kt b/app/src/main/java/net/pokeranalytics/android/model/handhistory/Position.kt new file mode 100644 index 00000000..c22af621 --- /dev/null +++ b/app/src/main/java/net/pokeranalytics/android/model/handhistory/Position.kt @@ -0,0 +1,38 @@ +package net.pokeranalytics.android.model.handhistory + +import net.pokeranalytics.android.exceptions.PAIllegalStateException +import java.util.* + +enum class Position(var value: String) { + SB("SB"), + BB("BB"), + UTG("UTG"), + UTG1("UTG+1"), + UTG2("UTG+2"), + UTG3("UTG+3"), + MP("MP"), + HJ("HJ"), + CO("CO"), + BUT("BUT"); + + companion object { + + fun positionsPerPlayers(playerCount: Int) : LinkedHashSet { + return when(playerCount) { + 2 -> linkedSetOf(SB, BB) + 3 -> linkedSetOf(SB, BB, BUT) + 4 -> linkedSetOf(SB, BB, UTG, BUT) + 5 -> linkedSetOf(SB, BB, UTG, CO, BUT) + 6 -> linkedSetOf(SB, BB, UTG, HJ, CO, BUT) + 7 -> linkedSetOf(SB, BB, UTG, MP, HJ, CO, BUT) + 8 -> linkedSetOf(SB, BB, UTG, UTG1, MP, HJ, CO, BUT) + 9 -> linkedSetOf(SB, BB, UTG, UTG1, UTG2, MP, HJ, CO, BUT) + 10 -> linkedSetOf(SB, BB, UTG, UTG1, UTG2, UTG3, MP, HJ, CO, BUT) + else -> throw PAIllegalStateException("Unmanaged number of players") + } + } + + } + + +} \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/model/handhistory/Street.kt b/app/src/main/java/net/pokeranalytics/android/model/handhistory/Street.kt index f823dd0c..861e5c11 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/handhistory/Street.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/handhistory/Street.kt @@ -1,5 +1,7 @@ package net.pokeranalytics.android.model.handhistory +import net.pokeranalytics.android.R + enum class Street { PREFLOP, FLOP, @@ -15,5 +17,16 @@ enum class Street { RIVER -> 5 } } + + val resId: Int + get() { + return when (this) { + PREFLOP -> R.string.street_preflop + FLOP -> R.string.street_flop + TURN -> R.string.street_turn + RIVER -> R.string.street_river + } + } + } diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/Action.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/Action.kt index 30f056a6..fe91f2f7 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/Action.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/Action.kt @@ -1,23 +1,25 @@ package net.pokeranalytics.android.model.realm.handhistory import io.realm.RealmObject +import net.pokeranalytics.android.R import net.pokeranalytics.android.exceptions.PAIllegalStateException open class Action : RealmObject() { - enum class Type { - POST_SB, - POST_BB, - STRADDLE, - FOLD, - CHECK, - CALL, - BET, - RAISE, - UNDEFINED_ALLIN, - CALL_ALLIN, - BET_ALLIN, - RAISE_ALLIN; + enum class Type(var resId: Int) { + + POST_SB(R.string.posts_sb), + POST_BB(R.string.post_bb), + STRADDLE(R.string.straddle), + FOLD(R.string.fold), + CHECK(R.string.check), + CALL(R.string.call), + BET(R.string.bet), + RAISE(R.string.raise), + UNDEFINED_ALLIN(R.string.allin), + CALL_ALLIN(R.string.allin), + BET_ALLIN(R.string.allin), + RAISE_ALLIN(R.string.allin); val isSignificant: Boolean get() { diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt index 6e0e8d48..11b582a9 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt @@ -98,7 +98,7 @@ open class HandHistory : RealmObject(), RowRepresentable, Identifiable, Filterab handSetup.smallBlind?.let { this.smallBlind = it } handSetup.bigBlind?.let { this.bigBlind = it } - for (i in 0..this.numberOfPlayers) { + for (i in 0 until this.numberOfPlayers) { val action = Action() action.index = i action.position = i diff --git a/app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt b/app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt index 4568665e..a33110a6 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt @@ -3,6 +3,7 @@ package net.pokeranalytics.android.ui.view import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.Button import android.widget.FrameLayout import androidx.appcompat.widget.AppCompatImageView import androidx.appcompat.widget.AppCompatTextView @@ -25,6 +26,7 @@ import net.pokeranalytics.android.calculus.Stat import net.pokeranalytics.android.calculus.bankroll.BankrollReportManager import net.pokeranalytics.android.model.TableSize import net.pokeranalytics.android.model.extensions.getFormattedGameType +import net.pokeranalytics.android.model.handhistory.ComputedAction import net.pokeranalytics.android.model.realm.CustomField import net.pokeranalytics.android.model.realm.Player import net.pokeranalytics.android.model.realm.Session @@ -685,7 +687,19 @@ enum class RowViewType(private var layoutRes: Int) { * Display a hand action */ inner class RowHandAction(itemView: View) : RecyclerView.ViewHolder(itemView), BindableHolder { + override fun bind(position: Int, row: RowRepresentable, adapter: RowRepresentableAdapter) { + val computedAction = row as ComputedAction + + itemView.findViewById