diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/feature/save/SaveSheetActivity.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/feature/save/SaveSheetActivity.kt index ba480fb9f..0cffa8b53 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/feature/save/SaveSheetActivity.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/feature/save/SaveSheetActivity.kt @@ -14,6 +14,8 @@ import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.* import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Alignment.Companion.BottomCenter import androidx.compose.ui.Alignment.Companion.TopCenter import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -56,7 +58,7 @@ class SaveSheetActivity : AppCompatActivity() { if (intent.type?.startsWith("text/plain") == true) { intent.getStringExtra(Intent.EXTRA_TEXT)?.let { extractedText = it - workManager.enqueueSaveWorker(this, it) + workManager.enqueueSaveWorker(it) Log.d(ContentValues.TAG, "Extracted text: $extractedText") } } @@ -75,7 +77,7 @@ class SaveSheetActivity : AppCompatActivity() { setContent { LaunchedEffect(extractedText) { extractedText?.let { url -> - workManager.getWorkInfosByTagFlow(url).map { + workManager.getWorkInfosForUniqueWorkFlow(url).map { saveState = when (it.firstOrNull()?.state) { WorkInfo.State.RUNNING -> SaveState.SAVING WorkInfo.State.SUCCEEDED -> SaveState.SAVED @@ -86,7 +88,6 @@ class SaveSheetActivity : AppCompatActivity() { } } - val scaffoldState: ScaffoldState = rememberScaffoldState() val message = when (saveState) { SaveState.DEFAULT -> "" SaveState.SAVING -> "Saved to Omnivore" @@ -94,35 +95,30 @@ class SaveSheetActivity : AppCompatActivity() { SaveState.SAVED -> "Saved to Omnivore" } - Scaffold( - modifier = Modifier.clickable { - Log.d("debug", "DISMISS SCAFFOLD") - exit() - }, - scaffoldState = scaffoldState, - backgroundColor = Color.Transparent, - - // TODO: In future versions we can present Label, Note, Highlight options here - bottomBar = { - - androidx.compose.material3.BottomAppBar( - - modifier = Modifier - .height(55.dp) - .fillMaxWidth() - .clip(RoundedCornerShape(topEnd = 5.dp, topStart = 5.dp)), - containerColor = MaterialTheme.colors.background, - actions = { - Spacer(modifier = Modifier.width(25.dp)) - Text( - message, - style = androidx.compose.material3.MaterialTheme.typography.titleMedium - ) - }, - ) - }, + Box( + Modifier + .background(Color.Transparent) + .fillMaxSize() + .clickable { + Log.d("debug", "DISMISS BOX") + exit() + } ) { - + Row( + Modifier + .align(BottomCenter) + .height(55.dp) + .fillMaxWidth() + .clip(RoundedCornerShape(topEnd = 5.dp, topStart = 5.dp)) + .background(MaterialTheme.colors.background) + .padding(start = 25.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Text( + message, + style = androidx.compose.material3.MaterialTheme.typography.titleMedium + ) + } } LaunchedEffect(saveState) { @@ -134,25 +130,30 @@ class SaveSheetActivity : AppCompatActivity() { } } - private fun WorkManager.enqueueSaveWorker(context: Context, url: String) { + private fun WorkManager.enqueueSaveWorker(url: String) { val saveData = workDataOf("url" to url) val saveWork = OneTimeWorkRequestBuilder() .setInputData(saveData) - .setConstraints(Constraints.Builder() - .setRequiredNetworkType(NetworkType.CONNECTED) - .build()) + .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) + .setConstraints( + Constraints.Builder() + .setRequiredNetworkType(NetworkType.CONNECTED) + .build() + ) .build() val syncWork = OneTimeWorkRequestBuilder() - .setConstraints(Constraints.Builder() - .setRequiredNetworkType(NetworkType.CONNECTED) - .build()) + .setConstraints( + Constraints.Builder() + .setRequiredNetworkType(NetworkType.CONNECTED) + .build() + ) .setInitialDelay(5, TimeUnit.SECONDS) .build() - WorkManager.getInstance(context) - .beginUniqueWork("saveAndSync", ExistingWorkPolicy.REPLACE, saveWork) + // using url as name because requests with a different url might not be completed yet + beginUniqueWork(url, ExistingWorkPolicy.REPLACE, saveWork) .then(syncWork) .enqueue() } diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/feature/save/SaveURLWorker.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/feature/save/SaveURLWorker.kt index 027ffd5d8..fbf869e58 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/feature/save/SaveURLWorker.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/feature/save/SaveURLWorker.kt @@ -101,4 +101,51 @@ class SaveURLWorker @AssistedInject constructor( null } } + + companion object { + const val NOTIFICATION_CHANNEL_ID = "SAVE_URL_WORKER_CHANNEL" + const val NOTIFICATION_CHANNEL_NAME = "URL Saver" + const val NOTIFICATION_ID = 1 + } + + override suspend fun getForegroundInfo(): ForegroundInfo { + val notification = createNotification() + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + ForegroundInfo( + NOTIFICATION_ID, + notification, + ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC + ) + } else { + ForegroundInfo(NOTIFICATION_ID, notification) + } + } + + private fun createNotification(): Notification { + val channelId = + createNotificationChannel() + + return NotificationCompat.Builder(applicationContext, channelId) + .setContentTitle("Saving URL") + .setContentText("Your URL is being saved in the background.") + .setSmallIcon(R.drawable.ic_notification) // Ensure this icon is valid + .setPriority(NotificationCompat.PRIORITY_HIGH) + .build() + } + + private fun createNotificationChannel(): String { + val channel = NotificationChannel( + NOTIFICATION_CHANNEL_ID, + NOTIFICATION_CHANNEL_NAME, + NotificationManager.IMPORTANCE_HIGH // Changed from LOW to HIGH + ).apply { + description = "Notification channel for URL saving" + } + + val notificationManager = + applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + notificationManager.createNotificationChannel(channel) + + return NOTIFICATION_CHANNEL_ID + } }