From 1907b97d59b8d87c0732120cc9063fa7bab35a07 Mon Sep 17 00:00:00 2001 From: Laurent Date: Tue, 23 Jun 2020 12:30:36 +0200 Subject: [PATCH] Create hh export service with notification when done --- app/src/main/AndroidManifest.xml | 4 +- .../handhistory/HandHistoryActivity.kt | 75 ++++++------ .../replayer/ReplayExportService.kt | 107 ++++++++++++++++++ .../android/util/NotificationScheduling.kt | 41 +++---- .../android/util/video/MMediaMuxer.kt | 11 +- app/src/main/res/values/strings.xml | 4 + 6 files changed, 184 insertions(+), 58 deletions(-) create mode 100644 app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/replayer/ReplayExportService.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6f8811d4..c6ff82b8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -168,13 +168,15 @@ android:theme="@style/PokerAnalyticsTheme.AlertDialog" android:launchMode="singleTop"/> + + -// if ((hhFragment as? HandHistoryFragment)?.isEditing == true) { -// shouldShowDataLossWarning = true -// } -// } - if (shouldShowDataLossWarning) { AlertDialog.Builder(this) @@ -142,6 +160,9 @@ class HandHistoryActivity : BaseActivity() { } private fun videoExportAskForPermission() { + + Toast.makeText(this, R.string.video_export_started, Toast.LENGTH_LONG).show() + askForPermission(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), RequestCode.PERMISSION_WRITE_EXTERNAL_STORAGE.value) { granted -> if (granted) { videoExport() @@ -151,34 +172,11 @@ class HandHistoryActivity : BaseActivity() { private fun videoExport() { - val animator = ReplayerAnimator(this.handHistory, true) - - val square = 1024f - - val width = square - val height = square - - animator.setDimension(width, height) - TableDrawer.configurePaints(this, animator) - - val muxer = MMediaMuxer() - muxer.Init(null, width.toInt(), height.toInt(), "hhVideo", "YES!") - - animator.frames(this) { bitmap, count -> - - try { - val byteArray = bitmap.toByteArray() - muxer.AddFrame(byteArray, count, false) - } catch (e: Exception) { - Timber.e("error = ${e.message}") - } - + val handHistoryId = intent.getStringExtra(IntentKey.IDENTIFIER.keyName) + this.replayExportService?.export(handHistoryId) ?: run { + Toast.makeText(this, "Export service not available. Please contact support", Toast.LENGTH_LONG).show() } - muxer.CreateVideo() - val path = muxer.GetPath() - Timber.d("**** Video path = $path") - } private fun gifExport() { @@ -198,4 +196,9 @@ class HandHistoryActivity : BaseActivity() { } + override fun onDestroy() { + super.onDestroy() + unbindService(connection) + } + } \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/replayer/ReplayExportService.kt b/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/replayer/ReplayExportService.kt new file mode 100644 index 00000000..7572bae5 --- /dev/null +++ b/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/replayer/ReplayExportService.kt @@ -0,0 +1,107 @@ +package net.pokeranalytics.android.ui.modules.handhistory.replayer + +import android.app.PendingIntent +import android.app.Service +import android.content.Intent +import android.net.Uri +import android.os.Binder +import android.os.IBinder +import androidx.core.content.FileProvider +import io.realm.Realm +import net.pokeranalytics.android.R +import net.pokeranalytics.android.exceptions.PAIllegalStateException +import net.pokeranalytics.android.model.realm.handhistory.HandHistory +import net.pokeranalytics.android.ui.extensions.toByteArray +import net.pokeranalytics.android.util.TriggerNotification +import net.pokeranalytics.android.util.extensions.findById +import net.pokeranalytics.android.util.video.MMediaMuxer +import timber.log.Timber +import java.io.File + + +class ReplayExportService : Service() { + + private lateinit var handHistoryId: String + + private val binder = LocalBinder() + + override fun onBind(intent: Intent?): IBinder? { + return binder + } + + inner class LocalBinder : Binder() { + + fun getService(): ReplayExportService = this@ReplayExportService + + } + + fun export(handHistoryId: String) { + this@ReplayExportService.handHistoryId = handHistoryId + export() + } + + private fun export() { + + val realm = Realm.getDefaultInstance() + realm.findById(this.handHistoryId)?.let { hh -> + startExport(hh) + } ?: throw PAIllegalStateException("HandHistory not found, id: $handHistoryId") + + } + + private fun startExport(handHistory: HandHistory) { + + val animator = ReplayerAnimator(handHistory, true) + + val square = 1024f + + val width = square + val height = square + + animator.setDimension(width, height) + TableDrawer.configurePaints(this, animator) + + val muxer = MMediaMuxer() + muxer.Init(null, width.toInt(), height.toInt(), "hhVideo", "YES!") + + animator.frames(this) { bitmap, count -> + + try { + val byteArray = bitmap.toByteArray() + muxer.AddFrame(byteArray, count, false) + } catch (e: Exception) { + Timber.e("error = ${e.message}") + } + + } + + muxer.CreateVideo { path -> + notifyUser(path) + } + + } + + private fun notifyUser(path: String) { + + val title = getString(R.string.video_available) + val body = getString(R.string.video_retrieval_message) + ": " + path + + val uri = FileProvider.getUriForFile( + this, + this.applicationContext.packageName.toString() + ".provider", + File(path) + ) + + val intent = Intent(Intent.ACTION_VIEW) + intent.setDataAndType(uri, "video/*") + 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) + + TriggerNotification(this, title, body, pendingIntent) + + } + +} \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/util/NotificationScheduling.kt b/app/src/main/java/net/pokeranalytics/android/util/NotificationScheduling.kt index d5aec554..5789b41c 100644 --- a/app/src/main/java/net/pokeranalytics/android/util/NotificationScheduling.kt +++ b/app/src/main/java/net/pokeranalytics/android/util/NotificationScheduling.kt @@ -3,6 +3,7 @@ package net.pokeranalytics.android.util import android.app.Notification import android.app.NotificationChannel import android.app.NotificationManager +import android.app.PendingIntent import android.content.Context import android.graphics.Color import android.media.RingtoneManager @@ -37,10 +38,28 @@ class NotificationSchedule(var context: Context, var params: WorkerParameters) : } } -class TriggerNotification(context: Context, title: String, body: String) { +class TriggerNotification(context: Context, title: String, body: String, intent: PendingIntent? = null) { init { - sendNotification(context, title, body) + sendNotification(context, title, body, intent) + } + + private fun sendNotification(context: Context, title: String, body: String, intent: PendingIntent? = null) { + + val notificationManager = NotificationManagerCompat.from(context) + val mBuilder = NotificationCompat.Builder(context, createNotificationChannel(context, title, body)) + val notificationId = (System.currentTimeMillis() and 0xfffffff).toInt() + + mBuilder.setDefaults(Notification.DEFAULT_ALL) + .setContentTitle(title) + .setContentText(body) + .setContentIntent(intent) + .setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)) + .setPriority(NotificationCompat.PRIORITY_HIGH) + .setSmallIcon(R.drawable.release_note_icon) + .setAutoCancel(true) + + notificationManager.notify(notificationId, mBuilder.build()) } private fun createNotificationChannel(context: Context, name: String, description: String): String { @@ -63,22 +82,4 @@ class TriggerNotification(context: Context, title: String, body: String) { return channelId } - private fun sendNotification(context: Context, title: String, body: String) { - - val notificationManager = NotificationManagerCompat.from(context) - val mBuilder = NotificationCompat.Builder(context, createNotificationChannel(context, title, body)) - val notificationId = (System.currentTimeMillis() and 0xfffffff).toInt() - - mBuilder.setDefaults(Notification.DEFAULT_ALL) - .setContentTitle(title) - .setContentText(body) - .setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)) - .setPriority(NotificationCompat.PRIORITY_HIGH) - .setSmallIcon(R.drawable.release_note_icon) -// .setContentInfo("Content Info") - .setAutoCancel(true) - - notificationManager.notify(notificationId, mBuilder.build()) - } - } \ 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 be41fbc3..29431116 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 @@ -33,6 +33,7 @@ class MMediaMuxer { private var pd: ProgressDialog? = null private var _title: String? = null private var _mess: String? = null + private var completion: ((String) -> (Unit))? = null fun Init( activity: Activity?, @@ -82,7 +83,8 @@ class MMediaMuxer { } } - fun CreateVideo() { + fun CreateVideo(handler: (String) -> (Unit)) { + this.completion = handler currentIndexFrame = 0 Logd("Prepare Frames Data") bitFirst!!.addAll(bitList!!) @@ -280,6 +282,13 @@ class MMediaMuxer { mediaMuxer = null Logd("RELEASE MUXER, path = $outputPath") } + + this.outputPath?.let { path -> + this.completion?.invoke(path) + } ?: run { + Logd("no path") + } + } private fun getNV21(inputWidth: Int, inputHeight: Int, scaled: Bitmap): ByteArray { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index afd30789..fd5066ac 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -800,5 +800,9 @@ board All unsaved changes will be lost. Do you really want to continue? Replayer + Video available! + Your video has been generated at the following path + Open file with + Your video is currently exported, we\'ll send you a notification when it\'s available. Expect approximately one minute!