coroutines changes

perftest
Laurent 3 years ago
parent 1718bea582
commit c99ef285af
  1. 2
      app/src/main/java/net/pokeranalytics/android/api/CurrencyConverterApi.kt
  2. 6
      app/src/main/java/net/pokeranalytics/android/calculus/ReportWhistleBlower.kt
  3. 12
      app/src/main/java/net/pokeranalytics/android/calculus/bankroll/BankrollReportManager.kt
  4. 13
      app/src/main/java/net/pokeranalytics/android/ui/activity/components/MediaActivity.kt
  5. 7
      app/src/main/java/net/pokeranalytics/android/ui/fragment/ImportFragment.kt
  6. 4
      app/src/main/java/net/pokeranalytics/android/ui/fragment/ReportsFragment.kt
  7. 15
      app/src/main/java/net/pokeranalytics/android/ui/fragment/StatisticsFragment.kt
  8. 3
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/RealmFragment.kt
  9. 12
      app/src/main/java/net/pokeranalytics/android/ui/fragment/report/ComposableTableReportFragment.kt
  10. 3
      app/src/main/java/net/pokeranalytics/android/ui/fragment/report/ProgressReportFragment.kt
  11. 3
      app/src/main/java/net/pokeranalytics/android/ui/modules/calendar/CalendarDetailsFragment.kt
  12. 6
      app/src/main/java/net/pokeranalytics/android/ui/modules/calendar/CalendarFragment.kt
  13. 21
      app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/model/EditorViewModel.kt
  14. 571
      app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/replayer/ReplayExportService.kt
  15. 30
      app/src/main/java/net/pokeranalytics/android/ui/modules/session/SessionFragment.kt
  16. 11
      app/src/main/java/net/pokeranalytics/android/util/ImageUtils.kt

@ -18,7 +18,7 @@ class CurrencyConverterApi {
companion object {
val json = Json { ignoreUnknownKeys = true }
private val json = Json { ignoreUnknownKeys = true }
fun currencyRate(fromCurrency: String, toCurrency: String, context: Context, callback: (Double?, VolleyError?) -> (Unit)) {

@ -131,9 +131,6 @@ class ReportTask(private var whistleBlower: ReportWhistleBlower, var context: Co
private var cancelled = false
private val coroutineContext: CoroutineContext
get() = Dispatchers.Default
fun start() {
launchReports()
}
@ -143,7 +140,8 @@ class ReportTask(private var whistleBlower: ReportWhistleBlower, var context: Co
}
private fun launchReports() {
CoroutineScope(coroutineContext).launch {
CoroutineScope(Dispatchers.Default).launch {
val realm = Realm.getDefaultInstance()

@ -2,10 +2,7 @@ package net.pokeranalytics.android.calculus.bankroll
import io.realm.Realm
import io.realm.RealmResults
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import kotlinx.coroutines.*
import net.pokeranalytics.android.model.realm.Bankroll
import net.pokeranalytics.android.model.realm.ComputableResult
import net.pokeranalytics.android.model.realm.Transaction
@ -15,9 +12,6 @@ import kotlin.coroutines.CoroutineContext
object BankrollReportManager {
private val coroutineContext: CoroutineContext
get() = Dispatchers.Main
private var reports: MutableMap<String?, BankrollReport> = mutableMapOf()
private var computableResults: RealmResults<ComputableResult>
@ -68,10 +62,10 @@ object BankrollReportManager {
}
// otherwise compute it
GlobalScope.launch(coroutineContext) {
CoroutineScope(Dispatchers.Default).launch {
var report: BankrollReport? = null
val coroutine = GlobalScope.async {
val coroutine = async {
val s = Date()
Timber.d(">>>>> start computing bankroll...")

@ -9,6 +9,7 @@ import android.provider.MediaStore
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
@ -52,11 +53,11 @@ open class MediaActivity : BaseActivity() {
val filesList = ArrayList<File>()
GlobalScope.launch {
CoroutineScope(Dispatchers.Default).launch {
if (tempFile != null) {
tempFile?.let {
GlobalScope.launch(Dispatchers.Main) {
CoroutineScope(Dispatchers.Main).launch {
filesList.add(it)
getPictures(filesList)
}
@ -65,7 +66,7 @@ open class MediaActivity : BaseActivity() {
data.clipData?.let { clipData ->
try {
GlobalScope.launch(Dispatchers.Main) {
CoroutineScope(Dispatchers.Main).launch {
isLoadingNewPictures()
}
@ -78,7 +79,7 @@ open class MediaActivity : BaseActivity() {
filesList.add(photoFile)
}
GlobalScope.launch(Dispatchers.Main) {
CoroutineScope(Dispatchers.Main).launch {
getPictures(filesList)
}
@ -90,7 +91,7 @@ open class MediaActivity : BaseActivity() {
data.data?.let { uri ->
try {
GlobalScope.launch(Dispatchers.Main) {
CoroutineScope(Dispatchers.Main).launch {
isLoadingNewPictures()
}
@ -98,7 +99,7 @@ open class MediaActivity : BaseActivity() {
val photoFile = ImageUtils.createTempImageFile(this@MediaActivity)
ImageUtils.copyInputStreamToFile(inputStream!!, photoFile)
filesList.add(photoFile)
GlobalScope.launch(Dispatchers.Main) {
CoroutineScope(Dispatchers.Main).launch {
getPictures(filesList)
}

@ -6,10 +6,7 @@ import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import com.google.android.material.snackbar.Snackbar
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import kotlinx.coroutines.*
import net.pokeranalytics.android.R
import net.pokeranalytics.android.databinding.FragmentImportBinding
import net.pokeranalytics.android.ui.fragment.components.RealmFragment
@ -89,7 +86,7 @@ class ImportFragment : RealmFragment(), ImportDelegate {
this.importer = CSVImporter(inputStream)
this.importer.delegate = this
CoroutineScope(coroutineContext).launch {
CoroutineScope(Dispatchers.Default).launch {
val coroutine = GlobalScope.async {
val s = Date()

@ -131,7 +131,7 @@ class ReportsFragment : DeletableItemFragment(), StaticRowRepresentableDataSourc
val itemToDeleteId = data?.getStringExtra(DataListActivity.IntentKey.ITEM_DELETED.keyName)
itemToDeleteId?.let { id ->
CoroutineScope(coroutineContext).launch {
CoroutineScope(Dispatchers.Default).launch {
delay(300)
deleteItem(dataListAdapter, reportSetups, id)
}
@ -313,7 +313,7 @@ class ReportsFragment : DeletableItemFragment(), StaticRowRepresentableDataSourc
showLoader()
CoroutineScope(coroutineContext).launch {
CoroutineScope(Dispatchers.Default).launch {
val startDate = Date()
val realm = Realm.getDefaultInstance()

@ -8,10 +8,7 @@ import android.os.Bundle
import android.view.*
import androidx.appcompat.widget.Toolbar
import io.realm.Realm
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import kotlinx.coroutines.*
import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.Calculator
import net.pokeranalytics.android.calculus.ComputableGroup
@ -158,9 +155,9 @@ class StatisticsFragment : FilterableFragment(), RealmAsyncListener {
*/
private fun launchStatComputation() {
CoroutineScope(coroutineContext).launch {
CoroutineScope(Dispatchers.Default).launch {
val async = GlobalScope.async {
val async = async {
val s = Date()
Timber.d(">>> start...")
@ -179,8 +176,10 @@ class StatisticsFragment : FilterableFragment(), RealmAsyncListener {
}
async.await()
if (isAdded && !isDetached) {
tableReportFragment.showResults()
launch(Dispatchers.Main) {
if (isAdded && !isDetached) {
tableReportFragment.showResults()
}
}
}
}

@ -18,9 +18,6 @@ interface RealmAsyncListener {
open class RealmFragment : BaseFragment() {
val coroutineContext: CoroutineContext
get() = Dispatchers.Main
/**
* A realm instance
*/

@ -7,10 +7,7 @@ import android.view.ViewGroup
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import io.realm.Realm
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import kotlinx.coroutines.*
import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.calcul.ReportDisplay
import net.pokeranalytics.android.calculus.Calculator
@ -33,12 +30,9 @@ import net.pokeranalytics.android.util.TextFormat
import timber.log.Timber
import java.util.*
open class ComposableTableReportFragment : RealmFragment(), StaticRowRepresentableDataSource, CoroutineScope,
open class ComposableTableReportFragment : RealmFragment(), StaticRowRepresentableDataSource,
RowRepresentableDelegate {
// override val coroutineContext: CoroutineContext
// get() = Dispatchers.Main
companion object {
/**
@ -211,7 +205,7 @@ open class ComposableTableReportFragment : RealmFragment(), StaticRowRepresentab
showLoader()
GlobalScope.launch(coroutineContext) {
CoroutineScope(Dispatchers.Default).launch {
var report: Report? = null
val test = GlobalScope.async {

@ -11,6 +11,7 @@ import com.github.mikephil.charting.data.LineDataSet
import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipGroup
import io.realm.Realm
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
@ -169,7 +170,7 @@ class ProgressReportFragment : AbstractReportFragment() {
graphContainer.hideWithAnimation()
progressBar.showWithAnimation()
GlobalScope.launch {
CoroutineScope(Dispatchers.Default).launch {
val s = Date()
Timber.d(">>> start...")

@ -13,6 +13,7 @@ import com.github.mikephil.charting.data.BarDataSet
import com.github.mikephil.charting.data.LineDataSet
import com.google.android.material.tabs.TabLayout
import io.realm.Realm
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
@ -167,7 +168,7 @@ class CalendarDetailsFragment : BaseFragment(), StaticRowRepresentableDataSource
this.model.computedResults?.let { computedResults ->
GlobalScope.launch {
CoroutineScope(Dispatchers.Default).launch {
val startDate = Date()

@ -44,7 +44,7 @@ import java.util.*
import kotlin.collections.set
class CalendarFragment : RealmFragment(), CoroutineScope, StaticRowRepresentableDataSource,
class CalendarFragment : RealmFragment(), StaticRowRepresentableDataSource,
RowRepresentableDelegate, RealmAsyncListener {
enum class TimeFilter {
@ -348,7 +348,7 @@ class CalendarFragment : RealmFragment(), CoroutineScope, StaticRowRepresentable
binding.progressBar.showWithAnimation()
binding.recyclerView.hideWithAnimation()
GlobalScope.launch {
CoroutineScope(Dispatchers.Default).launch {
val realm = Realm.getDefaultInstance()
realm.refresh()
@ -357,7 +357,7 @@ class CalendarFragment : RealmFragment(), CoroutineScope, StaticRowRepresentable
realm.close()
GlobalScope.launch(Dispatchers.Main) {
launch(Dispatchers.Main) {
displayData()
}
}

@ -5,10 +5,7 @@ import android.text.InputType
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import io.realm.Realm
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import kotlinx.coroutines.*
import net.pokeranalytics.android.R
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.handhistory.HandSetup
@ -49,9 +46,6 @@ interface PlayerSetupCreationListener {
class EditorViewModel : ViewModel(), RowRepresentableDataSource, CardCentralizer, ActionListListener {
private val coroutineContext: CoroutineContext
get() = Dispatchers.Main
/***
* The hand history
*/
@ -656,15 +650,12 @@ class EditorViewModel : ViewModel(), RowRepresentableDataSource, CardCentralizer
private fun defineWinnerPositions() {
val hhId = this.handHistory.id
GlobalScope.launch(coroutineContext) {
val c = GlobalScope.async {
val realm = Realm.getDefaultInstance()
realm.executeTransaction {
realm.findById<HandHistory>(hhId)?.defineWinnerPositions()
}
realm.close()
CoroutineScope(Dispatchers.Default).launch {
val realm = Realm.getDefaultInstance()
realm.executeTransaction {
realm.findById<HandHistory>(hhId)?.defineWinnerPositions()
}
c.await()
realm.close()
}
}

@ -13,7 +13,9 @@ import android.provider.MediaStore
import androidx.core.content.FileProvider
import com.arthenica.ffmpegkit.FFmpegKit
import io.realm.Realm
import kotlinx.coroutines.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import net.pokeranalytics.android.R
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.realm.handhistory.HandHistory
@ -26,373 +28,321 @@ import timber.log.Timber
import java.io.File
import java.io.FileOutputStream
import java.util.*
import kotlin.coroutines.CoroutineContext
enum class FileType(var value: String) {
IMAGE_GIF("image/gif"),
VIDEO_MP4("video/mp4")
IMAGE_GIF("image/gif"),
VIDEO_MP4("video/mp4")
}
class ReplayExportService : Service() {
private lateinit var handHistoryId: String
private lateinit var handHistoryId: String
private val binder = LocalBinder()
private val binder = LocalBinder()
private val coroutineContext: CoroutineContext
get() = Dispatchers.Main
override fun onBind(intent: Intent?): IBinder {
return binder
}
override fun onBind(intent: Intent?): IBinder {
return binder
}
inner class LocalBinder : Binder() {
fun getService(): ReplayExportService = this@ReplayExportService
}
inner class LocalBinder : Binder() {
fun getService(): ReplayExportService = this@ReplayExportService
}
fun videoExport(handHistoryId: String) {
this@ReplayExportService.handHistoryId = handHistoryId
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
startFFMPEGVideoExport()
} else {
startFFMPEGVideoExportPreQ()
}
}
fun videoExport(handHistoryId: String) {
this@ReplayExportService.handHistoryId = handHistoryId
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
startFFMPEGVideoExport()
} else {
startFFMPEGVideoExportPreQ()
}
}
fun gifExport(handHistoryId: String) {
this@ReplayExportService.handHistoryId = handHistoryId
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
startGIFExport()
} else {
startGIFExportPreQ()
}
}
fun gifExport(handHistoryId: String) {
this@ReplayExportService.handHistoryId = handHistoryId
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
startGIFExport()
} else {
startGIFExportPreQ()
}
}
private fun startGIFExport() {
private fun startGIFExport() {
CoroutineScope(Dispatchers.Default).launch {
GlobalScope.launch(coroutineContext) {
val c = GlobalScope.async {
val realm = Realm.getDefaultInstance()
realm.refresh()
val realm = Realm.getDefaultInstance()
realm.refresh()
val handHistory = realm.findById<HandHistory>(handHistoryId)
?: throw PAIllegalStateException("HandHistory not found, id: $handHistoryId")
val handHistory = realm.findById<HandHistory>(handHistoryId) ?: throw PAIllegalStateException("HandHistory not found, id: $handHistoryId")
val context = this@ReplayExportService
val context = this@ReplayExportService
val animator = ReplayerAnimator(handHistory, true)
val animator = ReplayerAnimator(handHistory, true)
val square = 1024
val square = 1024
val width = square
val height = square
val width = square
val height = square
animator.configure(width.toFloat(), height.toFloat(), context)
animator.configure(width.toFloat(), height.toFloat(), context)
val formattedDate = Date().dateTimeFileFormatted
val fileName = "hand_${formattedDate}"
val formattedDate = Date().dateTimeFileFormatted
val fileName = "hand_${formattedDate}"
// Add a specific media item.
val resolver = applicationContext.contentResolver
// Add a specific media item.
val resolver = applicationContext.contentResolver
// Q version tested before calling the function
val imageCollection =
MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
// Q version tested before calling the function
val imageCollection = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
val gifDetails = ContentValues().apply {
put(MediaStore.Images.Media.DISPLAY_NAME, fileName)
put(MediaStore.Images.Media.MIME_TYPE, FileType.IMAGE_GIF.value)
}
val gifDetails = ContentValues().apply {
put(MediaStore.Images.Media.DISPLAY_NAME, fileName)
put(MediaStore.Images.Media.MIME_TYPE, FileType.IMAGE_GIF.value)
}
val uri = resolver.insert(imageCollection, gifDetails)
val uri = resolver.insert(imageCollection, gifDetails)
if (uri != null) {
if (uri != null) {
val os = resolver.openOutputStream(uri)
val os = resolver.openOutputStream(uri)
val writer = AnimatedGIFWriter(false)
writer.prepareForWrite(os, width, height)
val writer = AnimatedGIFWriter(false)
writer.prepareForWrite(os, width, height)
val drawer = TableDrawer()
drawer.configurePaints(context, animator)
val drawer = TableDrawer()
drawer.configurePaints(context, animator)
var animationCount = 0
animator.frames(context) { bitmap, count ->
var animationCount = 0
animator.frames(context) { bitmap, count ->
when {
count > 10 -> {
writer.writeFrame(os, bitmap, count * 8)
animationCount = 0
}
else -> {
if (animationCount % 2 == 0) {
writer.writeFrame(os, bitmap)
}
animationCount++
}
}
}
writer.finishWrite(os)
when {
count > 10 -> {
writer.writeFrame(os, bitmap, count * 8)
animationCount = 0
}
else -> {
if (animationCount % 2 == 0) {
writer.writeFrame(os, bitmap)
}
animationCount++
}
}
}
writer.finishWrite(os)
realm.close()
notifyUser(uri, FileType.IMAGE_GIF)
} else {
Timber.w("Resolver insert ended without uri...")
}
}
realm.close()
notifyUser(uri, FileType.IMAGE_GIF)
} else {
Timber.w("Resolver insert ended without uri...")
}
}
c.await()
}
}
}
private fun startFFMPEGVideoExport() {
private fun startFFMPEGVideoExport() {
CoroutineScope(Dispatchers.Default).launch {
GlobalScope.launch(coroutineContext) {
val async = GlobalScope.async {
val realm = Realm.getDefaultInstance()
val handHistory = realm.findById<HandHistory>(handHistoryId)
?: throw PAIllegalStateException("HandHistory not found, id: $handHistoryId")
val realm = Realm.getDefaultInstance()
val handHistory = realm.findById<HandHistory>(handHistoryId) ?: throw PAIllegalStateException("HandHistory not found, id: $handHistoryId")
val context = this@ReplayExportService
val context = this@ReplayExportService
val animator = ReplayerAnimator(handHistory, true)
val animator = ReplayerAnimator(handHistory, true)
val square = 1024
val square = 1024
val width = square
val height = square
val width = square
val height = square
animator.configure(width.toFloat(), height.toFloat(), this@ReplayExportService)
val drawer = TableDrawer()
drawer.configurePaints(context, animator)
animator.configure(width.toFloat(), height.toFloat(), this@ReplayExportService)
val drawer = TableDrawer()
drawer.configurePaints(context, animator)
// generates all images and file descriptor
Timber.d("Generating images for video...")
val tmpDir = animator.generateVideoContent(this@ReplayExportService)
val dpath = "${tmpDir.path}/$FFMPEG_DESCRIPTOR_FILE"
// generates all images and file descriptor
Timber.d("Generating images for video...")
val tmpDir = animator.generateVideoContent(this@ReplayExportService)
val dpath = "${tmpDir.path}/$FFMPEG_DESCRIPTOR_FILE"
val formattedDate = Date().dateTimeFileFormatted
val fileName = "hand_${formattedDate}.mp4"
val formattedDate = Date().dateTimeFileFormatted
val fileName = "hand_${formattedDate}.mp4"
val outputDirectory = context.getExternalFilesDir(Environment.DIRECTORY_MOVIES)
?: throw PAIllegalStateException("File is invalid")
val output = "${outputDirectory.path}/$fileName"
val outputDirectory = context.getExternalFilesDir(Environment.DIRECTORY_MOVIES) ?: throw PAIllegalStateException("File is invalid")
val output = "${outputDirectory.path}/$fileName"
Timber.d("Assembling images for video...")
Timber.d("Assembling images for video...")
val command =
"-f concat -safe 0 -i $dpath -vb 20M -vsync vfr -s ${width}x${height} -vf fps=20 -pix_fmt yuv420p $output"
FFmpegKit.executeAsync(command) {
val command = "-f concat -safe 0 -i $dpath -vb 20M -vsync vfr -s ${width}x${height} -vf fps=20 -pix_fmt yuv420p $output"
FFmpegKit.executeAsync(command) {
when {
it.returnCode.isSuccess -> {
Timber.d("FFMPEG command execution completed successfully")
}
it.returnCode.isCancel -> {
Timber.d("Command execution cancelled by user.")
}
else -> {
Timber.d("Command execution failed with rc=${it.returnCode.value} and the output below.")
}
}
when {
it.returnCode.isSuccess -> {
Timber.d("FFMPEG command execution completed successfully")
}
it.returnCode.isCancel -> {
Timber.d("Command execution cancelled by user.")
}
else -> {
Timber.d(String.format("Command execution failed with rc=%d and the output below.", it.returnCode.value))
}
}
File(dpath).delete()
tmpDir.delete()
File(dpath).delete()
tmpDir.delete()
val file = File(output)
val file = File(output)
val resolver = applicationContext.contentResolver
val resolver = applicationContext.contentResolver
// Q version tested before calling the function
val videoCollection =
MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
// Q version tested before calling the function
val videoCollection = MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
val fileDetails = ContentValues().apply {
Timber.d("set file details = $fileName")
put(MediaStore.Video.Media.DISPLAY_NAME, fileName)
put(MediaStore.Images.Media.MIME_TYPE, FileType.VIDEO_MP4.value)
}
val fileDetails = ContentValues().apply {
Timber.d("set file details = $fileName")
put(MediaStore.Video.Media.DISPLAY_NAME, fileName)
put(MediaStore.Images.Media.MIME_TYPE, FileType.VIDEO_MP4.value)
}
// copy video to nice path
resolver.insert(videoCollection, fileDetails)?.let { uri ->
// copy video to nice path
resolver.insert(videoCollection, fileDetails)?.let { uri ->
Timber.d("copy file at uri = $uri")
Timber.d("copy file at uri = $uri")
val os = resolver.openOutputStream(uri)
os?.write(file.readBytes())
os?.close()
val os = resolver.openOutputStream(uri)
os?.write(file.readBytes())
os?.close()
file.delete() // delete temp file
file.delete() // delete temp file
notifyUser(uri, FileType.VIDEO_MP4)
notifyUser(uri, FileType.VIDEO_MP4)
} ?: run {
Timber.w("Resolver insert ended without uri...")
}
} ?: run {
Timber.w("Resolver insert ended without uri...")
}
}
}
}
}
async.await()
}
}
}
private fun startGIFExportPreQ() {
// private fun startVideoExport() {
//
// GlobalScope.launch(coroutineContext) {
// val c = GlobalScope.async {
//
// val realm = Realm.getDefaultInstance()
// val handHistory = realm.findById<HandHistory>(handHistoryId) ?: throw PAIllegalStateException("HandHistory not found, id: $handHistoryId")
//
// val context = this@ReplayExportService
//
// val animator = ReplayerAnimator(handHistory, true)
//
// val square = 1024
//
// val width = square
// val height = square
//
// animator.setDimension(width.toFloat(), height.toFloat())
// TableDrawer.configurePaints(context, animator)
//
// val muxer = MMediaMuxer()
// muxer.init(null, width, height, "hhVideo", "YES!")
//
// animator.frames(context) { bitmap, count ->
//
// try {
// val byteArray = bitmap.toByteArray()
// muxer.addFrame(byteArray, count, false)
// } catch (e: Exception) {
// Timber.e("error = ${e.message}")
// }
//
// }
//
// realm.close()
//
// muxer.createVideo { path ->
// notifyUser(path)
// }
//
// }
// c.await()
// }
//
// }
private fun startGIFExportPreQ() {
GlobalScope.launch(coroutineContext) {
val c = GlobalScope.async {
val realm = Realm.getDefaultInstance()
realm.refresh()
CoroutineScope(Dispatchers.Default).launch {
val handHistory = realm.findById<HandHistory>(handHistoryId) ?: throw PAIllegalStateException("HandHistory not found, id: $handHistoryId")
val realm = Realm.getDefaultInstance()
realm.refresh()
val context = this@ReplayExportService
val handHistory = realm.findById<HandHistory>(handHistoryId)
?: throw PAIllegalStateException("HandHistory not found, id: $handHistoryId")
val animator = ReplayerAnimator(handHistory, true)
val context = this@ReplayExportService
val square = 1024
val animator = ReplayerAnimator(handHistory, true)
val width = square
val height = square
val square = 1024
animator.configure(width.toFloat(), height.toFloat(), context)
val width = square
val height = square
val formattedDate = Date().dateTimeFileFormatted
val path = File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES),
"hand_${formattedDate}.gif"
).toString()
animator.configure(width.toFloat(), height.toFloat(), context)
val writer = AnimatedGIFWriter(false)
val os = FileOutputStream(path)
writer.prepareForWrite(os, width, height)
val formattedDate = Date().dateTimeFileFormatted
val path = File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES),
"hand_${formattedDate}.gif"
).toString()
val drawer = TableDrawer()
drawer.configurePaints(context, animator)
val writer = AnimatedGIFWriter(false)
val os = FileOutputStream(path)
writer.prepareForWrite(os, width, height)
var animationCount = 0
animator.frames(context) { bitmap, count ->
val drawer = TableDrawer()
drawer.configurePaints(context, animator)
when {
count > 10 -> {
writer.writeFrame(os, bitmap, count * 8)
animationCount = 0
}
else -> {
if (animationCount % 2 == 0) {
writer.writeFrame(os, bitmap)
}
animationCount++
}
}
var animationCount = 0
animator.frames(context) { bitmap, count ->
}
writer.finishWrite(os)
when {
count > 10 -> {
writer.writeFrame(os, bitmap, count * 8)
animationCount = 0
}
else -> {
if (animationCount % 2 == 0) {
writer.writeFrame(os, bitmap)
}
animationCount++
}
}
realm.close()
notifyUser(path)
}
writer.finishWrite(os)
}
c.await()
}
realm.close()
notifyUser(path)
}
}
private fun startFFMPEGVideoExportPreQ() {
}
GlobalScope.launch(coroutineContext) {
val async = GlobalScope.async {
private fun startFFMPEGVideoExportPreQ() {
val realm = Realm.getDefaultInstance()
val handHistory = realm.findById<HandHistory>(handHistoryId) ?: throw PAIllegalStateException("HandHistory not found, id: $handHistoryId")
CoroutineScope(Dispatchers.Default).launch {
val context = this@ReplayExportService
val realm = Realm.getDefaultInstance()
val handHistory = realm.findById<HandHistory>(handHistoryId)
?: throw PAIllegalStateException("HandHistory not found, id: $handHistoryId")
val animator = ReplayerAnimator(handHistory, true)
val context = this@ReplayExportService
val square = 1024
val animator = ReplayerAnimator(handHistory, true)
val width = square
val height = square
val square = 1024
animator.configure(width.toFloat(), height.toFloat(), this@ReplayExportService)
val drawer = TableDrawer()
drawer.configurePaints(context, animator)
val width = square
val height = square
// generates all images and file descriptor
Timber.d("Generating images for video...")
val tmpDir = animator.generateVideoContent(this@ReplayExportService)
val dpath = "${tmpDir.path}/$FFMPEG_DESCRIPTOR_FILE"
animator.configure(width.toFloat(), height.toFloat(), this@ReplayExportService)
val drawer = TableDrawer()
drawer.configurePaints(context, animator)
val formattedDate = Date().dateTimeFileFormatted
val output = File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES),
"hand_${formattedDate}.mp4"
).path
// generates all images and file descriptor
Timber.d("Generating images for video...")
val tmpDir = animator.generateVideoContent(this@ReplayExportService)
val dpath = "${tmpDir.path}/$FFMPEG_DESCRIPTOR_FILE"
Environment.getExternalStorageState(tmpDir)
val formattedDate = Date().dateTimeFileFormatted
val output = File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES),
"hand_${formattedDate}.mp4"
).path
Timber.d("Assembling images for video...")
Environment.getExternalStorageState(tmpDir)
Timber.d("Assembling images for video...")
val command = "-f concat -safe 0 -i $dpath -vb 20M -vsync vfr -s ${width}x${height} -vf fps=20 -pix_fmt yuv420p $output"
FFmpegKit.executeAsync(command) {
val command =
"-f concat -safe 0 -i $dpath -vb 20M -vsync vfr -s ${width}x${height} -vf fps=20 -pix_fmt yuv420p $output"
FFmpegKit.executeAsync(command) {
when {
it.returnCode.isSuccess -> {
Timber.d("FFMPEG command execution completed successfully")
}
it.returnCode.isCancel -> {
Timber.d("Command execution cancelled by user.")
}
else -> {
Timber.d(String.format("Command execution failed with rc=%d and the output below.", it.returnCode.value))
}
}
when {
it.returnCode.isSuccess -> {
Timber.d("FFMPEG command execution completed successfully")
}
it.returnCode.isCancel -> {
Timber.d("Command execution cancelled by user.")
}
else -> {
Timber.d("Command execution failed with rc=${it.returnCode.value} and the output below.")
}
}
// 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 ->
//
@ -403,61 +353,60 @@ class ReplayExportService : Service() {
// } else {
// 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()
// File(dpath).delete()
notifyUser(output)
}
notifyUser(output)
}
}
async.await()
}
}
}
}
private fun notifyUser(uri: Uri, type: FileType) {
private fun notifyUser(uri: Uri, type: FileType) {
val title = getString(R.string.video_available)
val body = getString(R.string.video_retrieval_message) + ": " + uri.path
val title = getString(R.string.video_available)
val body = getString(R.string.video_retrieval_message) + ": " + uri.path
this.showNotification(title, body, uri, type.value)
this.showNotification(title, body, uri, type.value)
}
}
private fun notifyUser(path: String) {
private fun notifyUser(path: String) {
val title = getString(R.string.video_available)
val body = getString(R.string.video_retrieval_message) + ": " + path
val title = getString(R.string.video_available)
val body = getString(R.string.video_retrieval_message) + ": " + path
Timber.d("Show local notification, path of file: ${path}")
Timber.d("Show local notification, path of file: ${path}")
val uri = FileProvider.getUriForFile(
this,
this.applicationContext.packageName.toString() + ".fileprovider",
File(path)
)
val uri = FileProvider.getUriForFile(
this,
this.applicationContext.packageName.toString() + ".fileprovider",
File(path)
)
val type = when {
path.contains("gif") -> "image/gif"
else -> "video/*"
}
val type = when {
path.contains("gif") -> "image/gif"
else -> "video/*"
}
this.showNotification(title, body, uri, type)
}
this.showNotification(title, body, uri, type)
}
private fun showNotification(title: String, body: String, uri: Uri, type: String) {
private fun showNotification(title: String, body: String, uri: Uri, type: String) {
val intent = Intent(Intent.ACTION_VIEW)
intent.setDataAndType(uri, type)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
val chooser = Intent.createChooser(intent, getString(R.string.open_file_with))
val intent = Intent(Intent.ACTION_VIEW)
intent.setDataAndType(uri, type)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
val chooser = Intent.createChooser(intent, getString(R.string.open_file_with))
val pendingIntent = PendingIntent.getActivity(this, 0, chooser, PendingIntent.FLAG_CANCEL_CURRENT)
val pendingIntent =
PendingIntent.getActivity(this, 0, chooser, PendingIntent.FLAG_CANCEL_CURRENT)
TriggerNotification(this, title, body, pendingIntent)
TriggerNotification(this, title, body, pendingIntent)
}
}
}

@ -11,10 +11,7 @@ import androidx.appcompat.content.res.AppCompatResources
import androidx.interpolator.view.animation.FastOutSlowInInterpolator
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.DiffUtil
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import kotlinx.coroutines.*
import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.bankroll.BankrollReportManager
import net.pokeranalytics.android.calculus.optimalduration.CashGameOptimalDurationCalculator
@ -42,6 +39,7 @@ import net.pokeranalytics.android.util.CrashLogging
import net.pokeranalytics.android.util.Preferences
import net.pokeranalytics.android.util.extensions.*
import timber.log.Timber
import java.lang.Runnable
import java.util.*
class SessionFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRepresentableDataSource, ResultCaptureTypeDelegate {
@ -406,14 +404,13 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRepr
Timber.d("Start optimal duration finding attempt...")
val isLive = this.currentSession.isLive
CoroutineScope(coroutineContext).launch {
CoroutineScope(Dispatchers.Default).launch {
var optimalDuration: Double? = null
val cr = GlobalScope.async {
optimalDuration = CashGameOptimalDurationCalculator.start(isLive)
}
cr.await()
val optimalDuration: Double? = CashGameOptimalDurationCalculator.start(isLive)
// optimalDuration =
// val cr = GlobalScope.async {
// }
// cr.await()
if (!isDetached) {
optimalDuration?.let {
@ -421,10 +418,13 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRepr
val delay = it.toLong() //5000L
currentSession.scheduleStopNotification(requireContext(), delay)
val formattedDuration = (it / 3600 / 1000).formattedHourlyDuration()
Timber.d("Setting stop notification in: $formattedDuration")
val message = requireContext().getString(R.string.stop_notification_in_, formattedDuration)
Toast.makeText(requireContext(), message, Toast.LENGTH_LONG).show()
launch(Dispatchers.Main) {
val formattedDuration = (it / 3600 / 1000).formattedHourlyDuration()
Timber.d("Setting stop notification in: $formattedDuration")
val message = requireContext().getString(R.string.stop_notification_in_, formattedDuration)
Toast.makeText(requireContext(), message, Toast.LENGTH_LONG).show()
}
}
}
}

@ -2,15 +2,12 @@ package net.pokeranalytics.android.util
import android.content.Context
import android.content.Intent
import android.graphics.*
import android.graphics.Paint.FILTER_BITMAP_FLAG
import android.media.ExifInterface
import android.net.Uri
import android.os.Environment
import androidx.core.content.ContextCompat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import net.pokeranalytics.android.R
import timber.log.Timber
@ -270,8 +267,8 @@ object ImageUtils {
* Save the bitmap in a file
*/
fun saveBitmapInFile(context: Context, bitmap: Bitmap, filename: String, action: (filePath: String) -> Unit) {
GlobalScope.launch {
CoroutineScope(Dispatchers.Default).launch {
val outputFile = File(context.filesDir, filename)
@ -291,7 +288,7 @@ object ImageUtils {
}
}
GlobalScope.launch(Dispatchers.Main) {
launch(Dispatchers.Main) {
Timber.d("Save file here: ${outputFile.absolutePath}")
action(outputFile.absolutePath)
}

Loading…
Cancel
Save