diff --git a/plugin.xml b/plugin.xml index 69290c1d..340a7941 100644 --- a/plugin.xml +++ b/plugin.xml @@ -35,6 +35,8 @@ + + diff --git a/src/android/UploadNotification.java b/src/android/UploadNotification.java index 092ce707..c970a751 100644 --- a/src/android/UploadNotification.java +++ b/src/android/UploadNotification.java @@ -14,8 +14,10 @@ import androidx.annotation.IntegerRes; import androidx.annotation.RequiresApi; import androidx.core.app.NotificationCompat; +import androidx.work.ForegroundInfo; import androidx.work.WorkInfo; import androidx.work.WorkManager; +import android.content.pm.ServiceInfo; import java.util.Collections; import java.util.List; @@ -82,7 +84,7 @@ public static Notification createNotification(NotificationCompat.Builder notific Notification notification = notificationBuilder.build(); notification.flags |= Notification.FLAG_NO_CLEAR; notification.flags |= Notification.FLAG_ONGOING_EVENT; - return notification; + return notification; } @RequiresApi(api = Build.VERSION_CODES.O) @@ -103,7 +105,9 @@ private static NotificationCompat.Builder getUploadNotification(final Context co PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, notificationIntent, pendingIntentFlag); // TODO: click intent open app - @SuppressLint("ResourceType") NotificationCompat.Builder uploadNotificationBuilder = new NotificationCompat.Builder(context, UploadTask.NOTIFICATION_CHANNEL_ID) + @SuppressLint("ResourceType") + NotificationCompat.Builder uploadNotificationBuilder = new NotificationCompat.Builder(context, + UploadTask.NOTIFICATION_CHANNEL_ID) .setContentTitle(notificationTitle) .setTicker(notificationTitle) .setSmallIcon(notificationIconRes) @@ -117,4 +121,13 @@ private static NotificationCompat.Builder getUploadNotification(final Context co return uploadNotificationBuilder; } + + public ForegroundInfo getForegroundInfo() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + return new ForegroundInfo(notificationId, notificationBuilder.build(), + ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC); + } + + return new ForegroundInfo(notificationId, notificationBuilder.build()); + } } diff --git a/src/android/UploadTask.java b/src/android/UploadTask.java index 9fd1c5c0..f2361fb9 100644 --- a/src/android/UploadTask.java +++ b/src/android/UploadTask.java @@ -7,6 +7,7 @@ import android.webkit.MimeTypeMap; import androidx.annotation.NonNull; +import androidx.work.ForegroundInfo; import androidx.work.Data; import androidx.work.Worker; import androidx.work.WorkerParameters; @@ -155,7 +156,7 @@ public UploadTask(@NonNull Context context, @NonNull WorkerParameters workerPara @NonNull @Override public Result doWork() { - if(!hasNetworkConnection()) { + if (!hasNetworkConnection()) { return Result.retry(); } @@ -194,9 +195,16 @@ public Result doWork() { } startTime = System.currentTimeMillis(); - // Register me - uploadForegroundNotification.progress(getId(), 0f); - handleNotification(); + // Register me and start foreground service IMMEDIATELY + // This must be done before any blocking operations to ensure uploads continue when app is killed + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { + uploadForegroundNotification.progress(getId(), 0f); + setForegroundAsync(uploadForegroundNotification.getForegroundInfo(getApplicationContext())); + } else { + // Android 12+: Still need to start foreground service for WorkManager to continue after app kill + uploadNotification.updateProgress(); + setForegroundAsync(uploadNotification.getForegroundInfo()); + } // Start call currentCall = httpClient.newCall(request); @@ -305,7 +313,10 @@ private void handleProgress(long bytesWritten, long totalBytes) { } float percent = (float) bytesWritten / (float) totalBytes; - UploadForegroundNotification.progress(getId(), percent); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { + UploadForegroundNotification.progress(getId(), percent); + } + // On Android 12+, progress is tracked via WorkManager progress data Log.i(TAG, "handleProgress: " + getId() + " Progress: " + (int) (percent * 100f)); @@ -395,12 +406,22 @@ private void handleNotification() { Log.d(TAG, "Upload Notification"); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { setForegroundAsync(uploadForegroundNotification.getForegroundInfo(getApplicationContext())); - } else { + } else { uploadNotification.updateProgress(); } Log.d(TAG, "Upload Notification Exit"); } + @NonNull + @Override + public ForegroundInfo getForegroundInfo() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { + return uploadForegroundNotification.getForegroundInfo(getApplicationContext()); + } + + return uploadNotification.getForegroundInfo(); + } + private synchronized boolean hasNetworkConnection() { ConnectivityManager connectivityManager = (ConnectivityManager) getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE); if((connectivityManager == null) || (connectivityManager.getActiveNetworkInfo() == null) || (connectivityManager.getActiveNetworkInfo().isConnectedOrConnecting() == false)) {