diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 77eeaac0..e269fd9e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -6,26 +6,10 @@ - - - + = Build.VERSION_CODES.Q) { + MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY) + } else { + MediaStore.Images.Media.EXTERNAL_CONTENT_URI + } - var animationCount = 0 - animator.frames(context) { bitmap, count -> + val gifDetails = ContentValues().apply { + put(MediaStore.Images.Media.DISPLAY_NAME, fileName) + put(MediaStore.Images.Media.MIME_TYPE, "image/gif") + } - when { - count > 10 -> { - writer.writeFrame(os, bitmap, count * 8) - animationCount = 0 - } - else -> { - if (animationCount % 2 == 0) { - writer.writeFrame(os, bitmap) + val uri = resolver.insert(videoCollection, gifDetails) + + if (uri != null) { + val os = resolver.openOutputStream(uri) + + val writer = AnimatedGIFWriter(false) + writer.prepareForWrite(os, width, height) + + val drawer = TableDrawer() + drawer.configurePaints(context, animator) + + 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++ } - animationCount++ } + } + writer.finishWrite(os) + realm.close() + notifyUser(uri, FileType.IMAGE) } - writer.finishWrite(os) - - realm.close() - notifyUser(path) - } c.await() } @@ -148,30 +168,79 @@ class ReplayExportService : Service() { val tmpDir = animator.generateVideoContent(this@ReplayExportService) val dpath = "${tmpDir.path}/$FFMPEG_DESCRIPTOR_FILE" -// val directory = context.getExternalFilesDir(null) ?: throw PAIllegalStateException("File is invalid") -// val output = "${directory.path}/video_${Date().dateTimeFileFormatted}.mp4" - val formattedDate = Date().dateTimeFileFormatted - val output = File( - Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES), - "video_${formattedDate}.mp4" - ).path + val fileName = "hand_${formattedDate}.mp4" +// val output = File( +// Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES), +// fileName +// ).path + + val outputDirectory = context.getExternalFilesDir(Environment.DIRECTORY_MOVIES) ?: throw PAIllegalStateException("File is invalid") + val output = "${outputDirectory.path}/$fileName" - Environment.getExternalStorageState(tmpDir) +// Environment.getExternalStorageState(tmpDir) 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 -> - if (rc == RETURN_CODE_SUCCESS) { - Timber.d("FFMPEG command execution completed successfully") - } else if (rc == RETURN_CODE_CANCEL) { - Timber.d("Command execution cancelled by user.") - } else { - Timber.d(String.format("Command execution failed with rc=%d and the output below.", rc)) + when (rc) { + RETURN_CODE_SUCCESS -> { + Timber.d("FFMPEG command execution completed successfully") + } + RETURN_CODE_CANCEL -> { + Timber.d("Command execution cancelled by user.") + } + else -> { + Timber.d(String.format("Command execution failed with rc=%d and the output below.", rc)) + } } + tmpDir.delete() - notifyUser(output) + val file = File(output) + + val resolver = applicationContext.contentResolver + val videoCollection = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY) + } else { + MediaStore.Video.Media.EXTERNAL_CONTENT_URI + } + + val fileDetails = ContentValues().apply { + Timber.d("set file details = $fileName") + put(MediaStore.Video.Media.DISPLAY_NAME, fileName) +// put(MediaStore.Video.Media.CONTENT_TYPE, "video/mp4") + } + + resolver.insert(videoCollection, fileDetails)?.let { uri -> + + Timber.d("copy file at uri = $uri") + + val os = resolver.openOutputStream(uri) + os?.write(file.readBytes()) + os?.close() + + file.delete() + + notifyUser(uri, FileType.VIDEO) + + } ?: run { + Timber.w("Resolver insert ended without uri...") + } + + +// val uri = FileProvider.getUriForFile( +// applicationContext, +// applicationContext.packageName.toString() + ".fileprovider", +// File(output) +// ) + +// Timber.d("File exported at $output") + + +// notifyUser(output) } } @@ -226,26 +295,26 @@ class ReplayExportService : Service() { // // } - private fun notifyUser(path: String) { - - Timber.d("Show local notification") + private fun notifyUser(uri: Uri, type: FileType) { val title = getString(R.string.video_available) - val body = getString(R.string.video_retrieval_message) + ": " + path + val body = getString(R.string.video_retrieval_message) + ": " + uri.path - val uri = FileProvider.getUriForFile( - this, - this.applicationContext.packageName.toString() + ".fileprovider", - File(path) - ) + Timber.d("Show local notification, path of file: ${uri.path}") - val type = when { - path.contains("gif") -> "image/gif" - else -> "video/*" - } +// val uri = FileProvider.getUriForFile( +// this, +// this.applicationContext.packageName.toString() + ".fileprovider", +// File(path) +// ) +// +// val type = when { +// path.contains("gif") -> "image/gif" +// else -> "video/*" +// } val intent = Intent(Intent.ACTION_VIEW) - intent.setDataAndType(uri, type) + intent.setDataAndType(uri, type.value) 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)) diff --git a/app/src/main/java/net/pokeranalytics/android/util/extensions/DateExtension.kt b/app/src/main/java/net/pokeranalytics/android/util/extensions/DateExtension.kt index c80cc2ef..414d111d 100644 --- a/app/src/main/java/net/pokeranalytics/android/util/extensions/DateExtension.kt +++ b/app/src/main/java/net/pokeranalytics/android/util/extensions/DateExtension.kt @@ -120,7 +120,7 @@ fun Date.getDayMonthYear(): String { // Returns a file friendly date time string val Date.dateTimeFileFormatted: String get() { - return SimpleDateFormat("yy_MM_dd_hh_mm_ss", Locale.getDefault()).format(this) + return SimpleDateFormat("yy_MM_dd-hh_mm_ss", Locale.getDefault()).format(this) } // Return the netDuration between two dates