diff --git a/app/build.gradle b/app/build.gradle index 89bab66a..0c7be826 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -38,7 +38,7 @@ android { compose true } composeOptions { - kotlinCompilerExtensionVersion '1.5.14' + kotlinCompilerExtensionVersion '1.5.9' } packagingOptions { resources { @@ -52,7 +52,7 @@ dependencies { implementation project(':library') implementation 'androidx.appcompat:appcompat:1.6.1' - implementation 'com.google.android.material:material:1.11.0' + implementation 'com.google.android.material:material:1.10.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'androidx.activity:activity-ktx:1.8.2' implementation "androidx.fragment:fragment-ktx:1.6.2" @@ -60,7 +60,7 @@ dependencies { implementation 'androidx.core:core-ktx:1.12.0' implementation 'net.danlew:android.joda:2.12.7' implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.7.0' - implementation 'androidx.activity:activity-compose:1.8.2' + implementation 'androidx.activity:activity-compose:1.8.0' implementation "androidx.compose.ui:ui:$compose_ui_version" implementation "androidx.compose.ui:ui-tooling-preview:$compose_ui_version" implementation "androidx.compose.runtime:runtime-livedata:$compose_ui_version" @@ -68,12 +68,11 @@ dependencies { implementation "androidx.compose.material:material-icons-extended:$compose_ui_version" implementation "com.google.accompanist:accompanist-systemuicontroller:0.34.0" - implementation 'androidx.compose.material:material:1.6.2' + implementation 'androidx.compose.material:material:1.6.1' implementation "com.squareup.okhttp3:logging-interceptor:4.12.0" implementation "com.squareup.okhttp3:okhttp:4.12.0" implementation 'com.squareup.retrofit2:retrofit:2.9.0' - implementation 'com.google.code.gson:gson:2.10.1' implementation 'com.squareup.retrofit2:converter-gson:2.9.0' implementation 'com.squareup.retrofit2:converter-scalars:2.9.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 72052409..f0e072e7 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,12 +2,6 @@ - - - - - + android:maxSdkVersion="29" /> - - \ No newline at end of file diff --git a/app/src/main/java/com/mux/video/vod/demo/UploadExampleApp.kt b/app/src/main/java/com/mux/video/vod/demo/UploadExampleApp.kt index b603c1d4..828c7ea6 100644 --- a/app/src/main/java/com/mux/video/vod/demo/UploadExampleApp.kt +++ b/app/src/main/java/com/mux/video/vod/demo/UploadExampleApp.kt @@ -1,34 +1,11 @@ package com.mux.video.vod.demo -import android.annotation.TargetApi import android.app.Application -import android.app.NotificationChannel -import android.app.NotificationManager -import android.os.Build import com.mux.video.upload.MuxUploadSdk -import com.mux.video.upload.api.MuxUploadManager class UploadExampleApp : Application() { - override fun onCreate() { super.onCreate() - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - createNotificationChannel() - } MuxUploadSdk.initialize(this) - if (MuxUploadManager.allUploadJobs().isNotEmpty()) { - UploadNotificationService.startCompat(this) - } - } - - @TargetApi(Build.VERSION_CODES.O) - private fun createNotificationChannel() { - val channel = NotificationChannel( - UploadNotificationService.CHANNEL_UPLOAD_PROGRESS, - getString(R.string.notif_channel_name), - NotificationManager.IMPORTANCE_LOW - ) - channel.description = getString(R.string.notif_channel_desc) - getSystemService(NotificationManager::class.java).createNotificationChannel(channel) } } diff --git a/app/src/main/java/com/mux/video/vod/demo/UploadNotificationService.kt b/app/src/main/java/com/mux/video/vod/demo/UploadNotificationService.kt deleted file mode 100644 index 2fcf5bbf..00000000 --- a/app/src/main/java/com/mux/video/vod/demo/UploadNotificationService.kt +++ /dev/null @@ -1,202 +0,0 @@ -package com.mux.video.vod.demo - -import android.annotation.SuppressLint -import android.annotation.TargetApi -import android.app.Service -import android.content.Context -import android.content.Intent -import android.content.pm.ServiceInfo -import android.os.Binder -import android.os.Build -import android.os.IBinder -import android.util.Log -import androidx.core.app.NotificationCompat -import androidx.core.app.ServiceCompat -import com.mux.video.upload.api.MuxUpload -import com.mux.video.upload.api.MuxUploadManager -import com.mux.video.upload.api.UploadEventListener -import com.mux.video.upload.api.UploadStatus - -/** - * Service that monitors ongoing [MuxUpload]s, showing progress notifications for them. This - * service will enter the foreground whenever there are uploads in progress and will exit foreground - * and stop itself when there are no more uploads in progress (ie, all have completed, paused, or - * failed) - */ -class UploadNotificationService : Service() { - - companion object { - private const val TAG = "BackgroundUploadService" - - const val ACTION_START = "start" - const val NOTIFICATION_FG = 200002 - const val CHANNEL_UPLOAD_PROGRESS = "upload_progress" - - fun startCompat(context: Context) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - startImplApiO(context) - } else { - startImplLegacy(context) - } - } - - @TargetApi(Build.VERSION_CODES.O) - private fun startImplApiO(context: Context) { - val startIntent = Intent(context, UploadNotificationService::class.java) - startIntent.action = ACTION_START - context.startForegroundService(startIntent) - } - - private fun startImplLegacy(context: Context) { - val startIntent = Intent(context, UploadNotificationService::class.java) - startIntent.action = ACTION_START - context.startService(startIntent) - } - } - - private var uploadListListener: UploadListListener? = null - // uploads tracked by this Service, regardless of state. cleared when the service is destroyed - private val uploadsByFile = mutableMapOf() - - override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { - val action = intent?.action - if (action != ACTION_START) { - throw RuntimeException("Unknown action") - } - - // can be commanded to start arbitrary number of times - if (uploadListListener == null) { - notify(MuxUploadManager.allUploadJobs()) - - val lis = UploadListListener() - this.uploadListListener = lis - MuxUploadManager.addUploadsUpdatedListener(lis) - } - - return super.onStartCommand(intent, flags, startId) - } - - override fun onBind(intent: Intent?): IBinder? { - return MyBinder() - } - - override fun onDestroy() { - uploadListListener?.let { MuxUploadManager.removeUploadsUpdatedListener(it) } - } - - private fun notifyWithCurrentUploads() = notify(this.uploadsByFile.values) - - @SuppressLint("InlinedApi", "MissingPermission") // inline use of FOREGROUND_SERVICE - private fun notify(uploads: Collection) { - if (uploads.isEmpty()) { - // only notify if there are uploads being tracked (in-progress or finished) - return - } - - val uploadsInProgress = uploads.filter { it.isRunning } - val uploadsCompleted = uploads.filter { it.isSuccessful } - val uploadsFailed = uploads.filter { it.error != null } - - Log.v(TAG, "notify: uploadsInProgress: ${uploadsInProgress.size}") - Log.v(TAG, "notify: uploadsCompleted: ${uploadsCompleted.size}") - Log.v(TAG, "notify: uploadsFailed: ${uploadsFailed.size}") - - val builder = NotificationCompat.Builder(this, CHANNEL_UPLOAD_PROGRESS) - builder.setSmallIcon(R.drawable.ic_launcher) - builder.setAutoCancel(false) - builder.setOngoing(true) - - if (uploadsInProgress.isNotEmpty()) { - Log.d(TAG, "notifying progress") - if (uploadsInProgress.size == 1 && this.uploadsByFile.size == 1) { - // Special case: A single upload in progress, with a single upload requested - val upload = uploadsInProgress.first() - val kbUploaded = (upload.currentProgress.bytesUploaded / 1024).toInt() - val kbTotal = (upload.currentProgress.totalBytes / 1024).toInt() - - Log.d(TAG, "upload state: ${upload.uploadStatus}") - - builder.setProgress(kbTotal, kbUploaded, false) - builder.setContentTitle( - resources.getQuantityString( - R.plurals.notif_txt_uploading, 1, 1, 1 - ) - ) - } else { - // Multiple uploads requested simultaneously so we batch them into one - val totalKbUploaded = uploadsInProgress.sumOf { it.currentProgress.bytesUploaded / 1024 } - val totalKb = uploadsInProgress.sumOf { it.currentProgress.totalBytes / 1024 } - - builder.setProgress(totalKb.toInt(),totalKbUploaded.toInt(), false) - builder.setContentTitle( - resources.getQuantityString( - R.plurals.notif_txt_uploading, - uploads.size, - uploads.size, - ) - ) - } - } else if (uploadsFailed.isNotEmpty()) { - Log.i(TAG, "notifying Fail") - builder.setContentTitle( - resources.getQuantityString( - R.plurals.notif_txt_failed, - uploadsFailed.size, - uploadsFailed.size - ) - ) - } else if (uploadsCompleted.isNotEmpty()) { - Log.i(TAG, "notifying Complete") - builder.setContentTitle( - resources.getQuantityString( - R.plurals.notif_txt_success, - uploadsCompleted.size, - uploadsCompleted.size, - ) - ) - } - - // always startForeground even if we're about to detach (to update the notification) - ServiceCompat.startForeground( - this, - NOTIFICATION_FG, - builder.build(), - ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC - ) - - if (uploadsInProgress.isEmpty()) { - // we only need foreground/to even be running while uploads are actually running - ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_DETACH) - stopSelf() - } - } - - private fun updateCurrentUploads(incomingUploads: List) { - // listen to status of new uploads - incomingUploads - .filter { !this.uploadsByFile.containsKey(it.videoFile.path) } - .forEach { - this.uploadsByFile[it.videoFile.path] = it - it.setStatusListener(UploadStatusListener()) - } - } - - private inner class UploadListListener : UploadEventListener> { - override fun onEvent(event: List) { - val service = this@UploadNotificationService - service.updateCurrentUploads(event) - service.notifyWithCurrentUploads() - } - } - - private inner class UploadStatusListener : UploadEventListener { - override fun onEvent(event: UploadStatus) { - val service = this@UploadNotificationService - service.notifyWithCurrentUploads() - } - } - - private inner class MyBinder : Binder() { - fun getService(): UploadNotificationService = this@UploadNotificationService - } -} \ No newline at end of file diff --git a/app/src/main/java/com/mux/video/vod/demo/backend/ImaginaryBackend.kt b/app/src/main/java/com/mux/video/vod/demo/backend/ImaginaryBackend.kt index 266d5468..c8570760 100644 --- a/app/src/main/java/com/mux/video/vod/demo/backend/ImaginaryBackend.kt +++ b/app/src/main/java/com/mux/video/vod/demo/backend/ImaginaryBackend.kt @@ -56,8 +56,8 @@ object ImaginaryBackend { // note: You shouldn't do basic auth with hard-coded keys in a real app private fun basicCredential(): String = Credentials.basic(ACCESS_TOKEN_ID, ACCESS_TOKEN_SECRET) - private const val ACCESS_TOKEN_ID = "d354b6f0-c753-40de-86a0-f92d0d08d699" - private const val ACCESS_TOKEN_SECRET = "HPcVJBvQL+PTRn3NXX40UqJaV94XM1kuj5AdeG/WR92RDY4w9TapQlFJnk1l3FmIpF2SrGJPuXj" + private const val ACCESS_TOKEN_ID = "YOUR TOKEN ID HERE" + private const val ACCESS_TOKEN_SECRET = "YOUR TOKEN SECRET HERE" } private interface ImaginaryWebapp { diff --git a/app/src/main/java/com/mux/video/vod/demo/upload/viewmodel/CreateUploadViewModel.kt b/app/src/main/java/com/mux/video/vod/demo/upload/viewmodel/CreateUploadViewModel.kt index ae710302..a0588743 100644 --- a/app/src/main/java/com/mux/video/vod/demo/upload/viewmodel/CreateUploadViewModel.kt +++ b/app/src/main/java/com/mux/video/vod/demo/upload/viewmodel/CreateUploadViewModel.kt @@ -3,6 +3,7 @@ package com.mux.video.vod.demo.upload.viewmodel import android.app.Application import android.database.Cursor import android.graphics.Bitmap +import android.media.MediaMetadataRetriever import android.net.Uri import android.os.Build import android.provider.MediaStore @@ -12,7 +13,6 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope import com.mux.video.upload.api.MuxUpload -import com.mux.video.vod.demo.UploadNotificationService import com.mux.video.vod.demo.backend.ImaginaryBackend import com.mux.video.vod.demo.upload.model.MediaStoreVideo import com.mux.video.vod.demo.upload.model.extractThumbnail @@ -67,8 +67,6 @@ class CreateUploadViewModel(private val app: Application) : AndroidViewModel(app ).build() // Force restart when creating brand new uploads (because we're making new Direct uploads) .start(forceRestart = true) - - UploadNotificationService.startCompat(app) } } @@ -76,6 +74,7 @@ class CreateUploadViewModel(private val app: Application) : AndroidViewModel(app * In order to upload a file from the device's media store, the file must be copied into the app's * temp directory. (Technically we could stream it from the source, but this prevents the other * app from modifying the file if we pause the upload for a long time or whatever) + * TODO Is this something that should go in the SDK? This is a common workflow */ @Throws private suspend fun copyIntoTempFile(contentUri: Uri): File { diff --git a/app/src/main/java/com/mux/video/vod/demo/upload/viewmodel/UploadListViewModel.kt b/app/src/main/java/com/mux/video/vod/demo/upload/viewmodel/UploadListViewModel.kt index 2a02e69e..89d45b22 100644 --- a/app/src/main/java/com/mux/video/vod/demo/upload/viewmodel/UploadListViewModel.kt +++ b/app/src/main/java/com/mux/video/vod/demo/upload/viewmodel/UploadListViewModel.kt @@ -1,7 +1,6 @@ package com.mux.video.vod.demo.upload.viewmodel import android.app.Application -import android.util.Log import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData @@ -23,8 +22,7 @@ class UploadListViewModel(app: Application) : AndroidViewModel(app) { private val listUpdateListener: UploadEventListener> by lazy { UploadEventListener { newUploads -> - //uploadMap.forEach { entry -> entry.value.clearListeners() } - observeUploads(newUploads) + newUploads.forEach { uploadMap[it.videoFile] = it } updateUiData(uploadMap.values.toList()) } } @@ -50,13 +48,11 @@ class UploadListViewModel(app: Application) : AndroidViewModel(app) { } private fun observeUploads(recentUploads: List) { - recentUploads - .filter { !this.uploadMap.containsKey(it.videoFile) } - .forEach { upload -> - upload.setStatusListener { - updateUiData(uploadMap.values.toList()) - } + recentUploads.forEach { upload -> + upload.setProgressListener { uploadMap[upload.videoFile] = upload + updateUiData(uploadMap.values.toList()) + } } // recentUploads.forEach } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 793c13b3..edfc851e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,25 +1,9 @@ - Mux Upload Demo + Mux Video VOD Upload SDK Mux Upload Plain-View Mux Upload Example Settings Pause Button Create New Upload Upload! - - Upload status notifications - Progress Notifications for uploads moving in the background - - - Uploading video - Uploading %1$d videos - - - Failed to upload video. Tap to retry - %1$d uploads failed. Tap to retry - - - Upload succeeded - Successfully uploaded %1$d videos - - + \ No newline at end of file diff --git a/build.gradle b/build.gradle index 48f811bd..64bfd76e 100644 --- a/build.gradle +++ b/build.gradle @@ -4,8 +4,8 @@ buildscript { } }// Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { - id 'com.android.application' version '8.4.1' apply false - id 'com.android.library' version '8.4.1' apply false - id 'org.jetbrains.kotlin.android' version '1.9.24' apply false + id 'com.android.application' version '8.1.2' apply false + id 'com.android.library' version '8.1.2' apply false + id 'org.jetbrains.kotlin.android' version '1.9.22' apply false id 'com.mux.gradle.android.mux-android-distribution' version '1.1.2' apply false } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 3155ea18..d5e1fba2 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Thu Jan 19 16:04:49 PST 2023 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/library/build.gradle b/library/build.gradle index aefcfb06..127e217a 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -72,7 +72,7 @@ muxDistribution { dependencies { implementation "com.squareup.okhttp3:logging-interceptor:4.12.0" implementation "com.squareup.okhttp3:okhttp:4.12.0" - implementation "io.github.crow-misia.libyuv:libyuv-android:0.31.1" + implementation "io.github.crow-misia.libyuv:libyuv-android:0.29.0" implementation 'androidx.core:core-ktx:1.12.0' implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3" diff --git a/library/src/main/java/com/mux/video/upload/api/MuxUpload.kt b/library/src/main/java/com/mux/video/upload/api/MuxUpload.kt index b2653048..371a8a82 100644 --- a/library/src/main/java/com/mux/video/upload/api/MuxUpload.kt +++ b/library/src/main/java/com/mux/video/upload/api/MuxUpload.kt @@ -1,14 +1,12 @@ package com.mux.video.upload.api import android.net.Uri -import android.util.Log import androidx.annotation.MainThread import com.mux.video.upload.MuxUploadSdk import com.mux.video.upload.api.MuxUpload.Builder import com.mux.video.upload.internal.UploadInfo import com.mux.video.upload.internal.update import kotlinx.coroutines.* -import kotlinx.coroutines.flow.distinctUntilChangedBy import java.io.File /** @@ -35,6 +33,7 @@ import java.io.File class MuxUpload private constructor( private var uploadInfo: UploadInfo, private val autoManage: Boolean = true, + initialStatus: UploadStatus = UploadStatus.Ready ) { /** @@ -57,18 +56,13 @@ class MuxUpload private constructor( * To be notified of status updates (including upload progress), use [setStatusListener] */ @Suppress("MemberVisibilityCanBePrivate") - val uploadStatus: UploadStatus get() = uploadInfo.statusFlow?.value ?: currentStatus + val uploadStatus: UploadStatus /** * True when the upload is running, false if it's paused, failed, or canceled */ val isRunning get() = uploadInfo.isRunning() - /** - * True when the upload is paused by [pause], false otherwise - */ - val isPaused get() = currentStatus is UploadStatus.UploadPaused - /** * If the upload has failed, gets the error associated with the failure */ @@ -78,25 +72,22 @@ class MuxUpload private constructor( /** * True if the upload was successful, false otherwise */ - val isSuccessful get() = uploadInfo.statusFlow?.value?.isSuccessful() ?: _successful + val isSuccessful get() = _successful private var _successful: Boolean = false private var resultListener: UploadEventListener>? = null private var progressListener: UploadEventListener? = null private var statusListener: UploadEventListener? = null private var observerJob: Job? = null - private val currentStatus: UploadStatus get() = - uploadInfo.statusFlow?.value ?: lastKnownStatus ?: UploadStatus.Ready - private var lastKnownStatus: UploadStatus? = null + private var currentStatus: UploadStatus = UploadStatus.Ready private val lastKnownProgress: Progress? get() = currentStatus.getProgress() private val callbackScope: CoroutineScope = MainScope() private val logger get() = MuxUploadSdk.logger init { - // Catch state if an upload was already in progress - // no need to observe: the Flow will have the most-recent values when queried - uploadInfo.statusFlow?.value?.let { status -> this.lastKnownStatus = status } + // Catch Events if an upload was already in progress + observeUpload(uploadInfo) } /** @@ -203,16 +194,8 @@ class MuxUpload private constructor( */ @MainThread fun setProgressListener(listener: UploadEventListener?) { - if (listener == null) { - observerJob?.cancel("clearing listeners") - observerJob = null - } else { - observeUpload(uploadInfo) - } - progressListener = listener lastKnownProgress?.let { listener?.onEvent(it) } - observeUpload(uploadInfo) } /** @@ -221,18 +204,11 @@ class MuxUpload private constructor( * @see setStatusListener */ @MainThread - fun setResultListener(listener: UploadEventListener>?) { - if (listener == null) { - observerJob?.cancel("clearing listeners") - observerJob = null - } else { - observeUpload(uploadInfo) - } - + fun setResultListener(listener: UploadEventListener>) { resultListener = listener lastKnownProgress?.let { if (it.bytesUploaded >= it.totalBytes) { - listener?.onEvent(Result.success(it)) + listener.onEvent(Result.success(it)) } } } @@ -244,13 +220,6 @@ class MuxUpload private constructor( */ @MainThread fun setStatusListener(listener: UploadEventListener?) { - if (listener == null) { - observerJob?.cancel("clearing listeners") - observerJob = null - } else { - observeUpload(uploadInfo) - } - statusListener = listener listener?.onEvent(currentStatus) } @@ -261,40 +230,39 @@ class MuxUpload private constructor( @Suppress("unused") @MainThread fun clearListeners() { - observerJob?.cancel("clearing listeners") resultListener = null progressListener = null statusListener = null } - private fun newObserveProgressJob(upload: UploadInfo): Job? { + private fun newObserveProgressJob(upload: UploadInfo): Job { // Job that collects and notifies state updates on the main thread (suspending on main is safe) - return upload.statusFlow?.let { flow -> - callbackScope.launch { - flow.collect { status -> - // Update the status of our upload - lastKnownStatus = status - - // Notify the old listeners - when (status) { - is UploadStatus.Uploading -> { progressListener?.onEvent(status.uploadProgress) } - is UploadStatus.UploadPaused -> { progressListener?.onEvent(status.uploadProgress) } - is UploadStatus.UploadSuccess -> { - _successful = true - progressListener?.onEvent(status.uploadProgress) - resultListener?.onEvent(Result.success(status.uploadProgress)) - } - is UploadStatus.UploadFailed -> { - progressListener?.onEvent(status.uploadProgress) // Make sure we're most up-to-date - if (status.exception !is CancellationException) { - _error = status.exception - resultListener?.onEvent(Result.failure(status.exception)) + return callbackScope.launch { + upload.statusFlow?.let { flow -> + launch { + flow.collect { status -> + // Update the status of our upload + currentStatus = status + statusListener?.onEvent(status) + + // Notify the old listeners + when (status) { + is UploadStatus.Uploading -> { progressListener?.onEvent(status.uploadProgress) } + is UploadStatus.UploadPaused -> { progressListener?.onEvent(status.uploadProgress) } + is UploadStatus.UploadSuccess -> { + progressListener?.onEvent(status.uploadProgress) + resultListener?.onEvent(Result.success(status.uploadProgress)) } + is UploadStatus.UploadFailed -> { + progressListener?.onEvent(status.uploadProgress) // Make sure we're most up-to-date + if (status.exception !is CancellationException) { + _error = status.exception + resultListener?.onEvent(Result.failure(status.exception)) + } + } + else -> { } // no relevant info } - else -> { } // no relevant info } - - statusListener?.onEvent(status) } } } @@ -305,6 +273,10 @@ class MuxUpload private constructor( observerJob = newObserveProgressJob(uploadInfo) } + init { + uploadStatus = initialStatus + } + /** * The current progress of an upload, in terms of time elapsed and data transmitted */ @@ -339,7 +311,7 @@ class MuxUpload private constructor( * @param videoFile a File that represents the video file you want to upload */ @Suppress("MemberVisibilityCanBePrivate") - class Builder(val uploadUri: Uri, val videoFile: File) { + class Builder constructor(val uploadUri: Uri, val videoFile: File) { /** * Create a new Builder with the specified input file and upload URL @@ -348,7 +320,8 @@ class MuxUpload private constructor( * @param videoFile a File that represents the video file you want to upload */ @Suppress("unused") - constructor(uploadUri: String, videoFile: File): this(Uri.parse(uploadUri), videoFile) + constructor(uploadUri: String, videoFile: File) + : this(Uri.parse(uploadUri), videoFile) private var manageTask: Boolean = true private var uploadInfo: UploadInfo = UploadInfo( @@ -430,6 +403,7 @@ class MuxUpload private constructor( * [MuxUploadManager] */ @JvmSynthetic - internal fun create(uploadInfo: UploadInfo) = MuxUpload(uploadInfo = uploadInfo) + internal fun create(uploadInfo: UploadInfo, initialStatus: UploadStatus = UploadStatus.Ready) + = MuxUpload(uploadInfo = uploadInfo, initialStatus = initialStatus) } } diff --git a/library/src/main/java/com/mux/video/upload/api/UploadResult.kt b/library/src/main/java/com/mux/video/upload/api/UploadResult.kt index 41ddb334..31e96440 100644 --- a/library/src/main/java/com/mux/video/upload/api/UploadResult.kt +++ b/library/src/main/java/com/mux/video/upload/api/UploadResult.kt @@ -5,7 +5,6 @@ package com.mux.video.upload.api * * Kotlin callers can use the [Result] API as normal */ -@Suppress("unused") class UploadResult { companion object { diff --git a/library/src/main/java/com/mux/video/upload/api/UploadStatus.kt b/library/src/main/java/com/mux/video/upload/api/UploadStatus.kt index d4df0a55..556150ad 100644 --- a/library/src/main/java/com/mux/video/upload/api/UploadStatus.kt +++ b/library/src/main/java/com/mux/video/upload/api/UploadStatus.kt @@ -35,19 +35,19 @@ sealed class UploadStatus { /** * This upload hos not been started. It is ready to start by calling [MuxUpload.start] */ - data object Ready: UploadStatus() + object Ready: UploadStatus() /** * This upload has been started via [MuxUpload.start] but has not yet started processing anything */ - data object Started: UploadStatus() + object Started: UploadStatus() /** * This upload is being prepared. If standardization is required, it is done during this step * * @see MuxUpload.Builder.standardizationRequested */ - data object Preparing: UploadStatus() + object Preparing: UploadStatus() /** * The upload is currently being sent to Mux Video. The progress is available diff --git a/library/src/main/java/com/mux/video/upload/internal/UploadInfo.kt b/library/src/main/java/com/mux/video/upload/internal/UploadInfo.kt index 7e6d70cf..a0d31406 100644 --- a/library/src/main/java/com/mux/video/upload/internal/UploadInfo.kt +++ b/library/src/main/java/com/mux/video/upload/internal/UploadInfo.kt @@ -28,10 +28,7 @@ internal data class UploadInfo( @JvmSynthetic internal val uploadJob: Deferred>?, @JvmSynthetic internal val statusFlow: StateFlow?, ) { - fun isRunning(): Boolean = /*uploadJob?.isActive*/ - statusFlow?.value?.let { - it is UploadStatus.Uploading || it is UploadStatus.Started || it is UploadStatus.Preparing - } ?: false + fun isRunning(): Boolean = uploadJob?.isActive ?: false } /** diff --git a/library/src/main/java/com/mux/video/upload/internal/UploadMetrics.kt b/library/src/main/java/com/mux/video/upload/internal/UploadMetrics.kt index 4b6c9324..f5ff1f32 100644 --- a/library/src/main/java/com/mux/video/upload/internal/UploadMetrics.kt +++ b/library/src/main/java/com/mux/video/upload/internal/UploadMetrics.kt @@ -24,6 +24,16 @@ internal class UploadMetrics private constructor() { private val logger get() = MuxUploadSdk.logger + + private fun formatMilliseconds(ms:Long):String { + return String.format("%02d:%02d:%02d", + TimeUnit.MILLISECONDS.toHours(ms), + TimeUnit.MILLISECONDS.toMinutes(ms) - + TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(ms)), // The change is in this line + TimeUnit.MILLISECONDS.toSeconds(ms) - + TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(ms))); + } + private suspend fun getEventInfo(startTimeMillis: Long, startTimeKey: String, endTimeMillis: Long, @@ -115,8 +125,8 @@ internal class UploadMetrics private constructor() { maximumResolution:String, sessionId: String, uploadInfo: UploadInfo - ) = runCatching { - val body = JSONObject() + ) { + var body = JSONObject() body.put("type", "upload_input_standardization_succeeded") body.put("session_id", sessionId) body.put("version", "1") @@ -139,8 +149,8 @@ internal class UploadMetrics private constructor() { maximumResolution:String, sessionId: String, uploadInfo: UploadInfo - ) = runCatching { - val body = JSONObject() + ) { + var body = JSONObject() body.put("type", "upload_input_standardization_failed") body.put("session_id", sessionId) body.put("version", "1") @@ -162,8 +172,8 @@ internal class UploadMetrics private constructor() { inputFileDurationMs: Long, sessionId: String, uploadInfo: UploadInfo - ) = runCatching { - val body = JSONObject() + ) { + var body = JSONObject() body.put("type", "upload_succeeded") body.put("session_id", sessionId) body.put("version", "1") @@ -183,8 +193,8 @@ internal class UploadMetrics private constructor() { errorDescription:String, sessionId: String, uploadInfo: UploadInfo - ) = runCatching { - val body = JSONObject() + ) { + var body = JSONObject() body.put("type", "uploadfailed") body.put("session_id", sessionId) body.put("version", "1")