Add pause, resume, and cancel controls for uploads

- Add pause/resume/cancel actions to notification bar
- Add pause/resume/cancel buttons to in-app upload progress card
- Track upload state (paused, uploading, cancelled) and upload type (chunked vs standard)
- Chunked uploads support pause/resume, standard uploads only support cancel
- Fix notification to persist cancelled state without flickering
This commit is contained in:
2025-12-27 22:09:14 -05:00
parent c80d523972
commit d40814ec4c
4 changed files with 441 additions and 9 deletions

View File

@@ -6,10 +6,18 @@ package com.mattintech.simplelogupload.constant;
public class AppConstants { public class AppConstants {
public static final String APP_TAG = "SLU::"; public static final String APP_TAG = "SLU::";
// Broadcast actions // Broadcast actions (service -> UI)
public static final String ACTION_UPLOAD_COMPLETE = "com.mattintech.simplelogupload.UPLOAD_COMPLETE"; public static final String ACTION_UPLOAD_COMPLETE = "com.mattintech.simplelogupload.UPLOAD_COMPLETE";
public static final String ACTION_UPLOAD_ERROR = "com.mattintech.simplelogupload.UPLOAD_ERROR"; public static final String ACTION_UPLOAD_ERROR = "com.mattintech.simplelogupload.UPLOAD_ERROR";
public static final String ACTION_UPLOAD_PROGRESS = "com.mattintech.simplelogupload.UPLOAD_PROGRESS"; public static final String ACTION_UPLOAD_PROGRESS = "com.mattintech.simplelogupload.UPLOAD_PROGRESS";
public static final String ACTION_UPLOAD_PAUSED = "com.mattintech.simplelogupload.UPLOAD_PAUSED";
public static final String ACTION_UPLOAD_RESUMED = "com.mattintech.simplelogupload.UPLOAD_RESUMED";
public static final String ACTION_UPLOAD_CANCELLED = "com.mattintech.simplelogupload.UPLOAD_CANCELLED";
// Intent actions (UI/notification -> service)
public static final String ACTION_PAUSE_UPLOAD = "com.mattintech.simplelogupload.PAUSE_UPLOAD";
public static final String ACTION_RESUME_UPLOAD = "com.mattintech.simplelogupload.RESUME_UPLOAD";
public static final String ACTION_CANCEL_UPLOAD = "com.mattintech.simplelogupload.CANCEL_UPLOAD";
// Intent extras // Intent extras
public static final String EXTRA_UPLOAD_CODE = "upload_code"; public static final String EXTRA_UPLOAD_CODE = "upload_code";

View File

@@ -66,6 +66,15 @@ class FileUploadForegroundService : Service() {
private var notificationBuilder: NotificationCompat.Builder? = null private var notificationBuilder: NotificationCompat.Builder? = null
private var lastProgressUpdate: Long = 0 private var lastProgressUpdate: Long = 0
// Upload state tracking
private var currentUploadManager: ChunkedUploadManager? = null
private var currentStandardUploadCall: Call? = null
private var uploadState: UploadState = UploadState.IDLE
enum class UploadState {
IDLE, UPLOADING, PAUSED, CANCELLED
}
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
uploadController = UploadController(this) uploadController = UploadController(this)
@@ -83,6 +92,22 @@ class FileUploadForegroundService : Service() {
return START_NOT_STICKY return START_NOT_STICKY
} }
// Handle control actions
when (intent.action) {
AppConstants.ACTION_PAUSE_UPLOAD -> {
handlePauseUpload()
return START_STICKY
}
AppConstants.ACTION_RESUME_UPLOAD -> {
handleResumeUpload()
return START_STICKY
}
AppConstants.ACTION_CANCEL_UPLOAD -> {
handleCancelUpload()
return START_NOT_STICKY
}
}
Log.d(TAG, "Intent received, extracting parameters...") Log.d(TAG, "Intent received, extracting parameters...")
// Get file path from intent // Get file path from intent
@@ -260,6 +285,10 @@ class FileUploadForegroundService : Service() {
// Create chunked upload manager directly // Create chunked upload manager directly
val uploadManager = ChunkedUploadManager(this, file, maxDownloads, expiryHours) val uploadManager = ChunkedUploadManager(this, file, maxDownloads, expiryHours)
// Store reference for pause/resume/cancel control
currentUploadManager = uploadManager
uploadState = UploadState.UPLOADING
// Set progress listener with direct notification updates // Set progress listener with direct notification updates
uploadManager.setProgressListener(object : ChunkedUploadManager.UploadProgressListener { uploadManager.setProgressListener(object : ChunkedUploadManager.UploadProgressListener {
override fun onProgress(bytesUploaded: Long, totalBytes: Long, percentComplete: Int) { override fun onProgress(bytesUploaded: Long, totalBytes: Long, percentComplete: Int) {
@@ -288,6 +317,10 @@ class FileUploadForegroundService : Service() {
override fun onUploadComplete(result: UploadResult) { override fun onUploadComplete(result: UploadResult) {
Log.d(TAG, "Chunked upload complete: $result") Log.d(TAG, "Chunked upload complete: $result")
// Clear upload reference
currentUploadManager = null
uploadState = UploadState.IDLE
// Save to history directly if successful // Save to history directly if successful
if (result.isSuccess) { if (result.isSuccess) {
saveToHistory(result) saveToHistory(result)
@@ -298,17 +331,22 @@ class FileUploadForegroundService : Service() {
override fun onError(error: String) { override fun onError(error: String) {
Log.e(TAG, "Chunked upload error: $error") Log.e(TAG, "Chunked upload error: $error")
// Clear upload reference
currentUploadManager = null
uploadState = UploadState.IDLE
future.complete(UploadResult(error)) future.complete(UploadResult(error))
} }
override fun onPaused() { override fun onPaused() {
Log.d(TAG, "Chunked upload paused") Log.d(TAG, "Chunked upload paused")
updateNotification("Upload paused", -1, 100) // Note: updateNotification and sendPausedBroadcast are called in handlePauseUpload()
} }
override fun onResumed() { override fun onResumed() {
Log.d(TAG, "Chunked upload resumed") Log.d(TAG, "Chunked upload resumed")
updateNotification("Resuming upload...", -1, 100) // Note: updateNotification and sendResumedBroadcast are called in handleResumeUpload()
} }
}) })
@@ -372,13 +410,27 @@ class FileUploadForegroundService : Service() {
// Show initial progress // Show initial progress
updateNotification("Uploading ${file.name}...", 0, 100) updateNotification("Uploading ${file.name}...", 0, 100)
client.newCall(request).enqueue(object : Callback { // Create call and store reference for cancellation
val call = client.newCall(request)
currentStandardUploadCall = call
uploadState = UploadState.UPLOADING
call.enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) { override fun onFailure(call: Call, e: IOException) {
Log.e(TAG, "Upload failed", e) Log.e(TAG, "Upload failed", e)
// Clear upload reference
currentStandardUploadCall = null
uploadState = UploadState.IDLE
future.complete(UploadResult("Upload failed: ${e.message}")) future.complete(UploadResult("Upload failed: ${e.message}"))
} }
override fun onResponse(call: Call, response: Response) { override fun onResponse(call: Call, response: Response) {
// Clear upload reference
currentStandardUploadCall = null
uploadState = UploadState.IDLE
response.use { response.use {
if (response.isSuccessful) { if (response.isSuccessful) {
try { try {
@@ -482,6 +534,7 @@ class FileUploadForegroundService : Service() {
.setOngoing(true) .setOngoing(true)
.setOnlyAlertOnce(true) // Don't alert on every progress update .setOnlyAlertOnce(true) // Don't alert on every progress update
.setPriority(NotificationCompat.PRIORITY_HIGH) // High priority for better visibility .setPriority(NotificationCompat.PRIORITY_HIGH) // High priority for better visibility
.addAction(createCancelAction()) // Add cancel action from the start
.apply { .apply {
// Force immediate notification display on Android 14+ (prevents system delay) // Force immediate notification display on Android 14+ (prevents system delay)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
@@ -506,18 +559,49 @@ class FileUploadForegroundService : Service() {
Log.d(TAG, "Updating notification: $status (progress: $progress/$max)") Log.d(TAG, "Updating notification: $status (progress: $progress/$max)")
// Ignore updates if upload is cancelled (unless this IS the cancellation message)
if (uploadState == UploadState.CANCELLED && !isComplete) {
Log.d(TAG, "Ignoring notification update because upload is cancelled")
return
}
builder.setContentText(status) builder.setContentText(status)
if (progress >= 0) { if (progress >= 0 && !isComplete) {
// Determinate progress // Determinate progress
builder.setProgress(max, progress, false) builder.setProgress(max, progress, false)
} else { } else if (!isComplete) {
// Indeterminate progress // Indeterminate progress
builder.setProgress(max, 0, true) builder.setProgress(max, 0, true)
} }
// When complete, make notification dismissible and remove progress bar // Add action buttons based on upload state
if (isComplete) { if (!isComplete && uploadState != UploadState.CANCELLED) {
builder.clearActions() // Remove old actions
when (uploadState) {
UploadState.PAUSED -> {
// Show Resume and Cancel buttons
builder.addAction(createResumeAction())
builder.addAction(createCancelAction())
}
UploadState.UPLOADING -> {
// Show Pause (if chunked) and Cancel buttons
if (currentUploadManager != null) {
builder.addAction(createPauseAction())
}
builder.addAction(createCancelAction())
}
else -> {
// For IDLE or other states, just show Cancel
builder.addAction(createCancelAction())
}
}
}
// When complete or cancelled, make notification dismissible and remove progress bar
if (isComplete || uploadState == UploadState.CANCELLED) {
builder.clearActions() // Remove all actions
builder.setOngoing(false) builder.setOngoing(false)
.setProgress(0, 0, false) // Remove progress bar .setProgress(0, 0, false) // Remove progress bar
.setAutoCancel(true) // Allow swipe to dismiss .setAutoCancel(true) // Allow swipe to dismiss
@@ -611,4 +695,182 @@ class FileUploadForegroundService : Service() {
// Send local broadcast to the ViewModel // Send local broadcast to the ViewModel
LocalBroadcastManager.getInstance(this).sendBroadcast(intent) LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
} }
/**
* Send a broadcast when upload is paused
*/
private fun sendPausedBroadcast() {
Log.d(TAG, "Sending paused broadcast")
val intent = Intent(AppConstants.ACTION_UPLOAD_PAUSED)
sendBroadcast(intent)
LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
}
/**
* Send a broadcast when upload is resumed
*/
private fun sendResumedBroadcast() {
Log.d(TAG, "Sending resumed broadcast")
val intent = Intent(AppConstants.ACTION_UPLOAD_RESUMED)
sendBroadcast(intent)
LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
}
/**
* Send a broadcast when upload is cancelled
*/
private fun sendCancelledBroadcast() {
Log.d(TAG, "Sending cancelled broadcast")
val intent = Intent(AppConstants.ACTION_UPLOAD_CANCELLED)
sendBroadcast(intent)
LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
}
/**
* Create a pause action for the notification
*/
private fun createPauseAction(): NotificationCompat.Action {
val pauseIntent = Intent(this, FileUploadForegroundService::class.java).apply {
action = AppConstants.ACTION_PAUSE_UPLOAD
}
val pausePendingIntent = PendingIntent.getService(
this,
1,
pauseIntent,
PendingIntent.FLAG_IMMUTABLE
)
return NotificationCompat.Action.Builder(
android.R.drawable.ic_media_pause,
"Pause",
pausePendingIntent
).build()
}
/**
* Create a resume action for the notification
*/
private fun createResumeAction(): NotificationCompat.Action {
val resumeIntent = Intent(this, FileUploadForegroundService::class.java).apply {
action = AppConstants.ACTION_RESUME_UPLOAD
}
val resumePendingIntent = PendingIntent.getService(
this,
2,
resumeIntent,
PendingIntent.FLAG_IMMUTABLE
)
return NotificationCompat.Action.Builder(
android.R.drawable.ic_media_play,
"Resume",
resumePendingIntent
).build()
}
/**
* Create a cancel action for the notification
*/
private fun createCancelAction(): NotificationCompat.Action {
val cancelIntent = Intent(this, FileUploadForegroundService::class.java).apply {
action = AppConstants.ACTION_CANCEL_UPLOAD
}
val cancelPendingIntent = PendingIntent.getService(
this,
3,
cancelIntent,
PendingIntent.FLAG_IMMUTABLE
)
return NotificationCompat.Action.Builder(
android.R.drawable.ic_menu_close_clear_cancel,
"Cancel",
cancelPendingIntent
).build()
}
/**
* Handle pause upload request
*/
private fun handlePauseUpload() {
Log.d(TAG, "Handling pause upload request")
if (uploadState != UploadState.UPLOADING) {
Log.w(TAG, "Cannot pause: upload not in progress (state: $uploadState)")
return
}
uploadState = UploadState.PAUSED
currentUploadManager?.let { manager ->
manager.pauseUpload()
updateNotification("Upload paused", -1, 100)
sendPausedBroadcast()
Log.d(TAG, "Chunked upload paused successfully")
} ?: run {
Log.w(TAG, "Cannot pause standard upload (not supported)")
updateNotification("Cannot pause standard upload", -1, 100)
}
}
/**
* Handle resume upload request
*/
private fun handleResumeUpload() {
Log.d(TAG, "Handling resume upload request")
if (uploadState != UploadState.PAUSED) {
Log.w(TAG, "Cannot resume: upload not paused (state: $uploadState)")
return
}
uploadState = UploadState.UPLOADING
currentUploadManager?.let { manager ->
manager.resumeUpload()
updateNotification("Resuming upload...", -1, 100)
sendResumedBroadcast()
Log.d(TAG, "Chunked upload resumed successfully")
}
}
/**
* Handle cancel upload request
*/
private fun handleCancelUpload() {
Log.d(TAG, "Handling cancel upload request")
uploadState = UploadState.CANCELLED
// Cancel chunked upload
currentUploadManager?.let { manager ->
manager.cancelUpload()
currentUploadManager = null
Log.d(TAG, "Cancelled chunked upload")
}
// Cancel standard upload
currentStandardUploadCall?.let { call ->
call.cancel()
currentStandardUploadCall = null
Log.d(TAG, "Cancelled standard upload")
}
// Clear upload in progress flag
PreferencesUtil.setUploadInProgress(this, false, null)
// Send cancelled broadcast
sendCancelledBroadcast()
// Update notification
updateNotification("Upload cancelled", -1, 100, isComplete = true)
// Stop service after a short delay
Handler(Looper.getMainLooper()).postDelayed({
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
stopForeground(STOP_FOREGROUND_DETACH)
} else {
@Suppress("DEPRECATION")
stopForeground(false)
}
stopSelf()
}, 500)
}
} }

View File

@@ -3,13 +3,17 @@ package com.mattintech.simplelogupload.ui.screens
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.BugReport import androidx.compose.material.icons.filled.BugReport
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.History import androidx.compose.material.icons.filled.History
import androidx.compose.material.icons.filled.Pause
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material.icons.filled.Settings import androidx.compose.material.icons.filled.Settings
import androidx.compose.material.icons.filled.Upload import androidx.compose.material.icons.filled.Upload
import androidx.compose.material3.* import androidx.compose.material3.*
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
@@ -113,7 +117,7 @@ fun MainScreen(
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Text( Text(
text = "Uploading", text = if (uiState.isPaused) "Upload Paused" else "Uploading",
style = MaterialTheme.typography.titleSmall, style = MaterialTheme.typography.titleSmall,
fontWeight = FontWeight.SemiBold, fontWeight = FontWeight.SemiBold,
color = MaterialTheme.colorScheme.onPrimaryContainer color = MaterialTheme.colorScheme.onPrimaryContainer
@@ -149,6 +153,70 @@ fun MainScreen(
color = MaterialTheme.colorScheme.onPrimaryContainer color = MaterialTheme.colorScheme.onPrimaryContainer
) )
} }
// Status message
uiState.uploadStatusMessage?.let { message ->
Spacer(modifier = Modifier.height(4.dp))
Text(
text = message,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onPrimaryContainer,
fontStyle = FontStyle.Italic
)
}
// Control buttons
Spacer(modifier = Modifier.height(8.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.End,
verticalAlignment = Alignment.CenterVertically
) {
if (uiState.isPaused) {
// Show Resume button
TextButton(
onClick = { viewModel.resumeUpload() }
) {
Icon(
Icons.Default.PlayArrow,
contentDescription = "Resume",
modifier = Modifier.size(18.dp)
)
Spacer(modifier = Modifier.width(4.dp))
Text("Resume")
}
} else {
// Show Pause button (only for chunked uploads)
if (uiState.isChunkedUpload) {
TextButton(
onClick = { viewModel.pauseUpload() }
) {
Icon(
Icons.Default.Pause,
contentDescription = "Pause",
modifier = Modifier.size(18.dp)
)
Spacer(modifier = Modifier.width(4.dp))
Text("Pause")
}
}
}
Spacer(modifier = Modifier.width(8.dp))
// Cancel button (always shown)
TextButton(
onClick = { viewModel.cancelUpload() }
) {
Icon(
Icons.Default.Close,
contentDescription = "Cancel",
modifier = Modifier.size(18.dp)
)
Spacer(modifier = Modifier.width(4.dp))
Text("Cancel")
}
}
} }
} }
} }

View File

@@ -14,6 +14,8 @@ import com.mattintech.simplelogupload.constant.AppConstants
import com.mattintech.simplelogupload.controller.FileController import com.mattintech.simplelogupload.controller.FileController
import com.mattintech.simplelogupload.controller.UploadController import com.mattintech.simplelogupload.controller.UploadController
import com.mattintech.simplelogupload.model.UploadResult import com.mattintech.simplelogupload.model.UploadResult
import com.mattintech.simplelogupload.service.FileUploadForegroundService
import com.mattintech.simplelogupload.upload.UploadStrategySelector
import com.mattintech.simplelogupload.util.PreferencesUtil import com.mattintech.simplelogupload.util.PreferencesUtil
import com.mattintech.simplelogupload.util.ZipUtil import com.mattintech.simplelogupload.util.ZipUtil
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@@ -30,6 +32,9 @@ import java.util.*
*/ */
data class MainUiState( data class MainUiState(
val isLoading: Boolean = false, val isLoading: Boolean = false,
val isPaused: Boolean = false,
val isChunkedUpload: Boolean = false,
val uploadStatusMessage: String? = null,
val selectedFile: File? = null, val selectedFile: File? = null,
val selectedFileName: String? = null, val selectedFileName: String? = null,
val dumpstateFiles: List<File> = emptyList(), val dumpstateFiles: List<File> = emptyList(),
@@ -150,6 +155,41 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
totalBytes = 0 totalBytes = 0
) )
} }
AppConstants.ACTION_UPLOAD_PAUSED -> {
Log.d(TAG, "Upload paused")
_uiState.value = _uiState.value.copy(
isPaused = true,
uploadStatusMessage = "Upload paused"
)
}
AppConstants.ACTION_UPLOAD_RESUMED -> {
Log.d(TAG, "Upload resumed")
_uiState.value = _uiState.value.copy(
isPaused = false,
uploadStatusMessage = "Resuming upload..."
)
}
AppConstants.ACTION_UPLOAD_CANCELLED -> {
Log.d(TAG, "Upload cancelled")
// Clean up temporary zip file if it exists
currentUploadTempZipFile?.let { tempFile ->
if (tempFile.exists()) {
val deleted = tempFile.delete()
Log.d(TAG, "Cleaned up temp zip file after cancellation: ${tempFile.name}, deleted: $deleted")
}
currentUploadTempZipFile = null
}
_uiState.value = _uiState.value.copy(
isLoading = false,
isPaused = false,
uploadProgress = 0,
bytesUploaded = 0,
totalBytes = 0,
uploadStatusMessage = "Upload cancelled"
)
}
} }
} }
} }
@@ -158,6 +198,9 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
addAction(AppConstants.ACTION_UPLOAD_PROGRESS) addAction(AppConstants.ACTION_UPLOAD_PROGRESS)
addAction(AppConstants.ACTION_UPLOAD_COMPLETE) addAction(AppConstants.ACTION_UPLOAD_COMPLETE)
addAction(AppConstants.ACTION_UPLOAD_ERROR) addAction(AppConstants.ACTION_UPLOAD_ERROR)
addAction(AppConstants.ACTION_UPLOAD_PAUSED)
addAction(AppConstants.ACTION_UPLOAD_RESUMED)
addAction(AppConstants.ACTION_UPLOAD_CANCELLED)
} }
LocalBroadcastManager.getInstance(getApplication()) LocalBroadcastManager.getInstance(getApplication())
@@ -288,8 +331,15 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
currentUploadMaxDownloads = maxDownloads currentUploadMaxDownloads = maxDownloads
currentUploadExpiryHours = expiryHours currentUploadExpiryHours = expiryHours
// Determine if this will be a chunked upload
val isChunked = UploadStrategySelector.shouldUseChunkedUpload(getApplication(), file)
Log.d(TAG, "Upload will use ${if (isChunked) "chunked" else "standard"} strategy")
_uiState.value = _uiState.value.copy( _uiState.value = _uiState.value.copy(
isLoading = true, isLoading = true,
isChunkedUpload = isChunked,
isPaused = false,
uploadStatusMessage = null,
showUploadParamsDialog = false showUploadParamsDialog = false
) )
@@ -358,6 +408,17 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
currentUploadMaxDownloads = maxDownloads currentUploadMaxDownloads = maxDownloads
currentUploadExpiryHours = expiryHours currentUploadExpiryHours = expiryHours
// Determine if this will be a chunked upload
val isChunked = UploadStrategySelector.shouldUseChunkedUpload(getApplication(), fileToUpload)
Log.d(TAG, "Upload will use ${if (isChunked) "chunked" else "standard"} strategy")
// Update state with upload type
_uiState.value = _uiState.value.copy(
isChunkedUpload = isChunked,
isPaused = false,
uploadStatusMessage = null
)
uploadController.startUploadService(fileToUpload, maxDownloads, expiryHours) uploadController.startUploadService(fileToUpload, maxDownloads, expiryHours)
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Error uploading dumpstate files", e) Log.e(TAG, "Error uploading dumpstate files", e)
@@ -378,6 +439,39 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
} }
} }
/**
* Pause the current upload
*/
fun pauseUpload() {
Log.d(TAG, "Pause upload requested")
val intent = Intent(getApplication(), FileUploadForegroundService::class.java).apply {
action = AppConstants.ACTION_PAUSE_UPLOAD
}
getApplication<Application>().startService(intent)
}
/**
* Resume the paused upload
*/
fun resumeUpload() {
Log.d(TAG, "Resume upload requested")
val intent = Intent(getApplication(), FileUploadForegroundService::class.java).apply {
action = AppConstants.ACTION_RESUME_UPLOAD
}
getApplication<Application>().startService(intent)
}
/**
* Cancel the current upload
*/
fun cancelUpload() {
Log.d(TAG, "Cancel upload requested")
val intent = Intent(getApplication(), FileUploadForegroundService::class.java).apply {
action = AppConstants.ACTION_CANCEL_UPLOAD
}
getApplication<Application>().startService(intent)
}
override fun onCleared() { override fun onCleared() {
super.onCleared() super.onCleared()
// Unregister broadcast receiver // Unregister broadcast receiver