Adds confirmation popup when hand uses wildcards + fixes bug in ffmpeg export by adding option

blinds
Laurent 5 years ago
parent 4a43d2fac3
commit 2a49f831ea
  1. 3
      app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt
  2. 36
      app/src/main/java/net/pokeranalytics/android/ui/extensions/UIExtensions.kt
  3. 38
      app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/HandHistoryActivity.kt
  4. 8
      app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/replayer/ReplayExportService.kt
  5. 13
      app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/replayer/ReplayerAnimator.kt
  6. 20
      app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/replayer/TableDrawer.kt
  7. 3
      app/src/main/res/values/strings.xml

@ -28,6 +28,7 @@ import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.util.extensions.addLineReturn import net.pokeranalytics.android.util.extensions.addLineReturn
import net.pokeranalytics.android.util.extensions.formatted import net.pokeranalytics.android.util.extensions.formatted
import net.pokeranalytics.android.util.extensions.fullDate import net.pokeranalytics.android.util.extensions.fullDate
import timber.log.Timber
import java.util.* import java.util.*
import kotlin.math.max import kotlin.math.max
@ -504,6 +505,8 @@ open class HandHistory : RealmObject(), Deletable, RowRepresentable, Filterable,
this.winnerPots.clear() this.winnerPots.clear()
this.winnerPots.addAll(wonPots) this.winnerPots.addAll(wonPots)
Timber.d("Pot won: ${this.winnerPots.size} for positions: ${this.winnerPots.map {it.position}} ")
} }
/*** /***

@ -3,6 +3,7 @@ package net.pokeranalytics.android.ui.extensions
import android.app.Activity import android.app.Activity
import android.content.ActivityNotFoundException import android.content.ActivityNotFoundException
import android.content.Context import android.content.Context
import android.content.DialogInterface
import android.content.Intent import android.content.Intent
import android.content.res.Resources import android.content.res.Resources
import android.graphics.Bitmap import android.graphics.Bitmap
@ -106,6 +107,41 @@ fun Context.openUrl(url: String) {
ContextCompat.startActivity(this, browserIntent, null) ContextCompat.startActivity(this, browserIntent, null)
} }
// Open custom tab
fun Context.areYouSure(title: Int? = null, message: Int? = null, positiveTitle: Int? = null, proceed: () -> Unit) {
val builder: android.app.AlertDialog.Builder = android.app.AlertDialog.Builder(this)
val messageResource = message ?: R.string.are_you_sure_you_want_to_do_this
builder.setMessage(messageResource)
title?.let { builder.setTitle(it) }
val positiveButtonTitle = positiveTitle ?: R.string.yes
builder.setPositiveButton(positiveButtonTitle) { _, _ ->
proceed()
}
builder.setNegativeButton(R.string.cancel) { _, _ ->
// nothing
}
// builder.setItems(
// arrayOf<CharSequence>(
// getString(R.string.yes),
// getString(R.string.cancel)
// )
// ) { _, index ->
// // The 'which' argument contains the index position
// // of the selected item
// when (index) {
// 0 -> proceed()
// 1 -> {} // nothing
// }
// }
builder.create().show()
}
// Display Alert Dialog // Display Alert Dialog
fun Activity.showAlertDialog(title: Int? = null, message: Int? = null) { fun Activity.showAlertDialog(title: Int? = null, message: Int? = null) {
showAlertDialog(this, title, message) showAlertDialog(this, title, message)

@ -16,6 +16,7 @@ import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.model.realm.handhistory.HandHistory import net.pokeranalytics.android.model.realm.handhistory.HandHistory
import net.pokeranalytics.android.ui.activity.components.BaseActivity import net.pokeranalytics.android.ui.activity.components.BaseActivity
import net.pokeranalytics.android.ui.activity.components.RequestCode import net.pokeranalytics.android.ui.activity.components.RequestCode
import net.pokeranalytics.android.ui.extensions.areYouSure
import net.pokeranalytics.android.ui.modules.handhistory.editor.EditorFragment import net.pokeranalytics.android.ui.modules.handhistory.editor.EditorFragment
import net.pokeranalytics.android.ui.modules.handhistory.replayer.ReplayExportService import net.pokeranalytics.android.ui.modules.handhistory.replayer.ReplayExportService
import net.pokeranalytics.android.ui.modules.handhistory.replayer.ReplayerFragment import net.pokeranalytics.android.ui.modules.handhistory.replayer.ReplayerFragment
@ -153,14 +154,25 @@ class HandHistoryActivity : BaseActivity() {
// of the selected item // of the selected item
when (index) { when (index) {
0 -> this.textExport() 0 -> this.textExport()
1 -> this.videoExportAskForPermission() 1 -> this.videoExportWildCardsCheck()
2 -> this.gifExportAskForPermission() 2 -> this.gifExportWildCardsCheck()
} }
} }
builder.create().show() builder.create().show()
} }
private fun gifExportWildCardsCheck() {
if (this.handHistory.usesWildcards) {
areYouSure(message = R.string.wildcards_warning, positiveTitle = R.string.proceed) {
gifExportAskForPermission()
}
} else {
gifExportAskForPermission()
}
}
private fun gifExportAskForPermission() { private fun gifExportAskForPermission() {
Toast.makeText(this, R.string.video_export_started, Toast.LENGTH_LONG).show() Toast.makeText(this, R.string.video_export_started, Toast.LENGTH_LONG).show()
@ -168,12 +180,27 @@ class HandHistoryActivity : BaseActivity() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
gifExport() gifExport()
} else { } else {
askForPermission(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), RequestCode.PERMISSION_WRITE_EXTERNAL_STORAGE.value) { granted -> askForPermission(
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
RequestCode.PERMISSION_WRITE_EXTERNAL_STORAGE.value
) { granted ->
if (granted) { if (granted) {
gifExport() gifExport()
} }
} }
} }
}
private fun videoExportWildCardsCheck() {
if (this.handHistory.usesWildcards) {
areYouSure(message = R.string.wildcards_warning, positiveTitle = R.string.proceed) {
videoExportAskForPermission()
}
} else {
videoExportAskForPermission()
}
} }
private fun videoExportAskForPermission() { private fun videoExportAskForPermission() {
@ -183,7 +210,10 @@ class HandHistoryActivity : BaseActivity() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
videoExport() videoExport()
} else { } else {
askForPermission(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), RequestCode.PERMISSION_WRITE_EXTERNAL_STORAGE.value) { granted -> askForPermission(
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
RequestCode.PERMISSION_WRITE_EXTERNAL_STORAGE.value
) { granted ->
if (granted) { if (granted) {
videoExport() videoExport()
} }

@ -180,7 +180,7 @@ class ReplayExportService : Service() {
Timber.d("Assembling images for video...") Timber.d("Assembling images for video...")
FFmpeg.executeAsync("-f concat -safe 0 -i $dpath -vb 20M -vsync vfr -s ${width}x${height} -pix_fmt yuv420p $output") { id, rc -> FFmpeg.executeAsync("-f concat -safe 0 -i $dpath -vb 20M -vsync vfr -s ${width}x${height} -vf fps=20 -pix_fmt yuv420p $output") { id, rc ->
when (rc) { when (rc) {
RETURN_CODE_SUCCESS -> { RETURN_CODE_SUCCESS -> {
@ -378,7 +378,7 @@ class ReplayExportService : Service() {
Environment.getExternalStorageState(tmpDir) Environment.getExternalStorageState(tmpDir)
Timber.d("Assembling images for video...") Timber.d("Assembling images for video...")
FFmpeg.executeAsync("-f concat -safe 0 -i $dpath -vb 20M -vsync vfr -s ${width}x${height} -pix_fmt yuv420p $output") { id, rc -> FFmpeg.executeAsync("-f concat -safe 0 -i $dpath -vb 20M -vsync vfr -s ${width}x${height} -vf fps=20 -pix_fmt yuv420p $output") { id, rc ->
if (rc == RETURN_CODE_SUCCESS) { if (rc == RETURN_CODE_SUCCESS) {
Timber.d("FFMPEG command execution completed successfully") Timber.d("FFMPEG command execution completed successfully")
@ -388,8 +388,8 @@ class ReplayExportService : Service() {
Timber.d(String.format("Command execution failed with rc=%d and the output below.", rc)) Timber.d(String.format("Command execution failed with rc=%d and the output below.", rc))
} }
// Delete descriptor and image files // Delete descriptor and image files
tmpDir.delete() // tmpDir.delete()
File(dpath).delete() // File(dpath).delete()
notifyUser(output) notifyUser(output)
} }

@ -56,7 +56,7 @@ class ReplayerAnimator(var handHistory: HandHistory, var export: Boolean) {
this.frameManager.add(FrameType.GATHER_ANIMATION, framesForChipsAnimation) this.frameManager.add(FrameType.GATHER_ANIMATION, framesForChipsAnimation)
} }
// Chip distribution animation on the Summary // Chip distribution animation on the Summary
if (step == Street.SUMMARY && !this.handHistory.usesWildcards) { if (step == Street.SUMMARY) {
this.frameManager.add(FrameType.DISTRIBUTION_ANIMATION, framesForChipsAnimation) this.frameManager.add(FrameType.DISTRIBUTION_ANIMATION, framesForChipsAnimation)
} }
this.frameManager.add(FrameType.STATE, 1) this.frameManager.add(FrameType.STATE, 1)
@ -255,7 +255,7 @@ class ReplayerAnimator(var handHistory: HandHistory, var export: Boolean) {
*/ */
private fun setDimension(width: Float, height: Float) { private fun setDimension(width: Float, height: Float) {
Timber.d("Setting dimensions...") // Timber.d("Setting dimensions...")
this.width = width this.width = width
this.height = height this.height = height
@ -272,7 +272,7 @@ class ReplayerAnimator(var handHistory: HandHistory, var export: Boolean) {
// else -> Pair(3.7f, 4.7f) // else -> Pair(3.7f, 4.7f)
// } // }
Timber.d("playerPerRow = $playerPerRow, playerPerColumn = $playerPerColumn") // Timber.d("playerPerRow = $playerPerRow, playerPerColumn = $playerPerColumn")
// val playerPerColumn = if (portrait) grid.second else grid.first // val playerPerColumn = if (portrait) grid.second else grid.first
// val playerPerRow = if (portrait) grid.first else grid.second // val playerPerRow = if (portrait) grid.first else grid.second
@ -519,6 +519,7 @@ class ReplayerAnimator(var handHistory: HandHistory, var export: Boolean) {
suspend fun generateVideoContent(context: Context): File { suspend fun generateVideoContent(context: Context): File {
var ffmpegImageDescriptor = "" var ffmpegImageDescriptor = ""
var vo = 0.0
var count = 0 var count = 0
var imagePath = "" var imagePath = ""
@ -535,7 +536,7 @@ class ReplayerAnimator(var handHistory: HandHistory, var export: Boolean) {
val bitmap = Bitmap.createBitmap(this.width.toInt(), this.height.toInt(), Bitmap.Config.ARGB_8888) val bitmap = Bitmap.createBitmap(this.width.toInt(), this.height.toInt(), Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap) val canvas = Canvas(bitmap)
val vo = this.visualOccurences / 90.0 // this is needed before the call to drawTable which pass to the next frame vo = this.visualOccurences / 90.0 // this is needed before the call to drawTable which pass to the next frame
this.drawer.drawTable(canvas, context) this.drawer.drawTable(canvas, context)
@ -550,15 +551,13 @@ class ReplayerAnimator(var handHistory: HandHistory, var export: Boolean) {
count++ count++
awaitFrame() awaitFrame()
} }
nextStep() nextStep()
} }
Timber.d("COUNT = $count") Timber.d("COUNT = $count")
// from the ffmpeg doc: (Due to a quirk, the last image has to be specified twice - the 2nd time without any duration directive)
ffmpegImageDescriptor = ffmpegImageDescriptor.plus("file '$imagePath'\n") ffmpegImageDescriptor = ffmpegImageDescriptor.plus("file '$imagePath'\n")
FileUtils.writeToFile(ffmpegImageDescriptor, FFMPEG_DESCRIPTOR_FILE, directory) FileUtils.writeToFile(ffmpegImageDescriptor, FFMPEG_DESCRIPTOR_FILE, directory)

@ -70,7 +70,7 @@ class TableDrawer {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
textPaint.typeface = ResourcesCompat.getFont(context, R.font.roboto_bold) textPaint.typeface = ResourcesCompat.getFont(context, R.font.roboto_bold)
cardTextPaint.typeface = ResourcesCompat.getFont(context, R.font.roboto_bold) cardTextPaint.typeface = ResourcesCompat.getFont(context, R.font.roboto_black)
} }
} }
@ -133,7 +133,7 @@ class TableDrawer {
* Draw chips * Draw chips
* A chip should appears when the player has put some money during the current street * A chip should appears when the player has put some money during the current street
*/ */
private fun drawPlayerChips(i: Int, canvas: Canvas, context: Context) { private fun drawPlayerChips(position: Int, canvas: Canvas, context: Context) {
when (animator.currentStep) { when (animator.currentStep) {
is Street -> { is Street -> {
@ -141,36 +141,36 @@ class TableDrawer {
FrameType.STATE -> { FrameType.STATE -> {
if (animator.currentStep == Street.SUMMARY) { if (animator.currentStep == Street.SUMMARY) {
val winnerPots = animator.handHistory.winnerPots.firstOrNull { it.position == i } val winnerPots = animator.handHistory.winnerPots.firstOrNull { it.position == position }
winnerPots?.let { pot -> winnerPots?.let { pot ->
val color = colorForAmount(pot.amount) val color = colorForAmount(pot.amount)
val circle = animator.animatedChipCircleFromPot(i) val circle = animator.animatedChipCircleFromPot(position)
drawChipCircle(circle, color, canvas, context) drawChipCircle(circle, color, canvas, context)
drawChipText(pot.amount, animator.chipText(i), canvas, context) drawChipText(pot.amount, animator.chipText(position), canvas, context)
} }
} }
} }
FrameType.GATHER_ANIMATION -> { FrameType.GATHER_ANIMATION -> {
animator.lastCommittedAmount(i)?.let { amount -> animator.lastCommittedAmount(position)?.let { amount ->
val color = colorForAmount(amount) val color = colorForAmount(amount)
val circle = animator.animatedChipCircleToPot(i) val circle = animator.animatedChipCircleToPot(position)
drawChipCircle(circle, color, canvas, context) drawChipCircle(circle, color, canvas, context)
} }
} }
FrameType.DISTRIBUTION_ANIMATION -> { FrameType.DISTRIBUTION_ANIMATION -> {
val winnerPots = animator.handHistory.winnerPots.firstOrNull { it.position == i } val winnerPots = animator.handHistory.winnerPots.firstOrNull { it.position == position }
winnerPots?.let { pot -> winnerPots?.let { pot ->
val color = colorForAmount(pot.amount) val color = colorForAmount(pot.amount)
val circle = animator.animatedChipCircleFromPot(i) val circle = animator.animatedChipCircleFromPot(position)
drawChipCircle(circle, color, canvas, context) drawChipCircle(circle, color, canvas, context)
} }
} }
} }
} }
is ComputedAction -> { is ComputedAction -> {
drawChipForAction(i, canvas, context) drawChipForAction(position, canvas, context)
} }
} }

@ -114,6 +114,7 @@
<string name="ante_value">Ante</string> <string name="ante_value">Ante</string>
<string name="app_store">Upgrade</string> <string name="app_store">Upgrade</string>
<string name="are_you_sure_you_want_to_delete">Are you sure you want to delete?</string> <string name="are_you_sure_you_want_to_delete">Are you sure you want to delete?</string>
<string name="are_you_sure_you_want_to_do_this">Are you sure you want to do this?</string>
<string name="are_you_sure_you_want_to_import_your_data_from_v3">Are you sure you want to import your Poker Analytics 3 data? This will erase your current Poker Analytics 5 data.</string> <string name="are_you_sure_you_want_to_import_your_data_from_v3">Are you sure you want to import your Poker Analytics 3 data? This will erase your current Poker Analytics 5 data.</string>
<string name="ask_user_to_add_icloud_account_to_be_able_to_use_icloud_storage">You need to add an iCloud account on your device to be able to use this feature.</string> <string name="ask_user_to_add_icloud_account_to_be_able_to_use_icloud_storage">You need to add an iCloud account on your device to be able to use this feature.</string>
<string name="ask_user_to_enable_icloud_to_migrate_data_from_icloud_to_local">You need to re-enable iCloud to be able to migrate your data from iCloud and be able to turn off iCloud storage.</string> <string name="ask_user_to_enable_icloud_to_migrate_data_from_icloud_to_local">You need to re-enable iCloud to be able to migrate your data from iCloud and be able to turn off iCloud storage.</string>
@ -818,5 +819,7 @@
<string name="dhph_explanation">You can change the DHPH (Dealt Hands Per Hour) values here. It corresponds to the total number of hands all players receives at a table in one hour. Example: Your local 10-max game deals 25 hands per hour, enter 10 x 25 = 250.</string> <string name="dhph_explanation">You can change the DHPH (Dealt Hands Per Hour) values here. It corresponds to the total number of hands all players receives at a table in one hour. Example: Your local 10-max game deals 25 hands per hour, enter 10 x 25 = 250.</string>
<string name="dealt_hands_per_hour">Dealt hands per hour</string> <string name="dealt_hands_per_hour">Dealt hands per hour</string>
<string name="report_name_cannot_be_empty">You need to give a name to the report</string> <string name="report_name_cannot_be_empty">You need to give a name to the report</string>
<string name="wildcards_warning">The hand you\'ve created has suit wildcards. By convenience - sorry! - we\'re currently removing such cards to determine which hand wins, resulting in potentially wrong chip distribution.</string>
<string name="proceed">proceed</string>
</resources> </resources>

Loading…
Cancel
Save