Skip to content

Commit 4ad948d

Browse files
Merge remote-tracking branch 'flutter_workmanager/main'
2 parents 82b9e6f + e068c54 commit 4ad948d

File tree

5 files changed

+288
-90
lines changed

5 files changed

+288
-90
lines changed

workmanager/android/src/main/kotlin/dev/fluttercommunity/workmanager/BackgroundWorker.kt

Lines changed: 78 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
package dev.fluttercommunity.workmanager
22

3+
import android.app.NotificationChannel
4+
import android.app.NotificationManager
35
import android.content.Context
6+
import android.os.Build
47
import android.os.Handler
58
import android.os.Looper
69
import android.util.Log
710
import androidx.concurrent.futures.CallbackToFutureAdapter
11+
import androidx.core.app.NotificationCompat
12+
import androidx.work.ForegroundInfo
813
import androidx.work.ListenableWorker
914
import androidx.work.WorkerParameters
1015
import com.google.common.util.concurrent.ListenableFuture
@@ -14,7 +19,8 @@ import io.flutter.embedding.engine.loader.FlutterLoader
1419
import io.flutter.plugin.common.MethodCall
1520
import io.flutter.plugin.common.MethodChannel
1621
import io.flutter.view.FlutterCallbackInformation
17-
import java.util.Random
22+
import java.util.*
23+
1824

1925
/***
2026
* A simple worker that will post your input back to your Flutter application.
@@ -37,6 +43,7 @@ class BackgroundWorker(
3743
const val BACKGROUND_CHANNEL_NAME =
3844
"be.tramckrijte.workmanager/background_channel_work_manager"
3945
const val BACKGROUND_CHANNEL_INITIALIZED = "backgroundChannelInitialized"
46+
const val SET_FOREGROUND = "setForeground"
4047

4148
private val flutterLoader = FlutterLoader()
4249
}
@@ -63,6 +70,42 @@ class BackgroundWorker(
6370
null
6471
}
6572

73+
private fun createForegroundInfo(
74+
setForegroundOptions: SetForeground
75+
): ForegroundInfo {
76+
// Create a Notification channel if necessary
77+
createNotificationChannel(
78+
setForegroundOptions.notificationChannelId,
79+
setForegroundOptions.notificationChannelName,
80+
setForegroundOptions.notificationChannelDescription,
81+
setForegroundOptions.notificationChannelImportance
82+
)
83+
val notification = NotificationCompat.Builder(applicationContext, setForegroundOptions.notificationChannelId)
84+
.setContentTitle(setForegroundOptions.notificationTitle)
85+
.setTicker(setForegroundOptions.notificationTitle)
86+
.setContentText(setForegroundOptions.notificationDescription)
87+
.setOngoing(true)
88+
.setSmallIcon(android.R.drawable.ic_dialog_info)
89+
.build()
90+
91+
return ForegroundInfo(
92+
setForegroundOptions.notificationId,
93+
notification,
94+
setForegroundOptions.foregroundServiceType
95+
)
96+
}
97+
98+
private fun createNotificationChannel(id: String, name: String, description: String, importance: Int) {
99+
// Create a Notification channel
100+
// Notification channels are only available in OREO and higher.
101+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
102+
val mChannel = NotificationChannel(id, name, importance)
103+
mChannel.description = description
104+
val notificationManager = applicationContext.getSystemService(NotificationManager::class.java)
105+
notificationManager.createNotificationChannel(mChannel)
106+
}
107+
}
108+
66109
override fun startWork(): ListenableFuture<Result> {
67110
startTime = System.currentTimeMillis()
68111

@@ -141,35 +184,46 @@ class BackgroundWorker(
141184
}
142185
}
143186

187+
private fun onBackgroundChannelInitialized() {
188+
backgroundChannel.invokeMethod(
189+
"onResultSend",
190+
mapOf(DART_TASK_KEY to dartTask, PAYLOAD_KEY to payload),
191+
object : MethodChannel.Result {
192+
override fun notImplemented() {
193+
stopEngine(Result.failure())
194+
}
195+
196+
override fun error(
197+
errorCode: String,
198+
errorMessage: String?,
199+
errorDetails: Any?,
200+
) {
201+
Log.e(TAG, "errorCode: $errorCode, errorMessage: $errorMessage")
202+
stopEngine(Result.failure())
203+
}
204+
205+
override fun success(receivedResult: Any?) {
206+
val wasSuccessFul = receivedResult?.let { it as Boolean? } == true
207+
stopEngine(if (wasSuccessFul) Result.success() else Result.retry())
208+
}
209+
},
210+
)
211+
}
212+
213+
private fun onSetForeground(setForegroundOptions: SetForeground) {
214+
setForegroundAsync(createForegroundInfo(setForegroundOptions))
215+
}
216+
144217
override fun onMethodCall(
145218
call: MethodCall,
146219
r: MethodChannel.Result,
147220
) {
148221
when (call.method) {
149222
BACKGROUND_CHANNEL_INITIALIZED ->
150-
backgroundChannel.invokeMethod(
151-
"onResultSend",
152-
mapOf(DART_TASK_KEY to dartTask, PAYLOAD_KEY to payload),
153-
object : MethodChannel.Result {
154-
override fun notImplemented() {
155-
stopEngine(Result.failure())
156-
}
157-
158-
override fun error(
159-
errorCode: String,
160-
errorMessage: String?,
161-
errorDetails: Any?,
162-
) {
163-
Log.e(TAG, "errorCode: $errorCode, errorMessage: $errorMessage")
164-
stopEngine(Result.failure())
165-
}
166-
167-
override fun success(receivedResult: Any?) {
168-
val wasSuccessFul = receivedResult?.let { it as Boolean? } == true
169-
stopEngine(if (wasSuccessFul) Result.success() else Result.retry())
170-
}
171-
},
172-
)
223+
onBackgroundChannelInitialized()
224+
225+
SET_FOREGROUND -> onSetForeground(Extractor.parseSetForegroundCall(call))
173226
}
227+
r.success(null)
174228
}
175229
}

workmanager/android/src/main/kotlin/dev/fluttercommunity/workmanager/Extractor.kt

Lines changed: 62 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,7 @@ package dev.fluttercommunity.workmanager
22

33
import android.os.Build
44
import androidx.annotation.VisibleForTesting
5-
import androidx.work.BackoffPolicy
6-
import androidx.work.Constraints
7-
import androidx.work.ExistingPeriodicWorkPolicy
8-
import androidx.work.ExistingWorkPolicy
9-
import androidx.work.NetworkType
10-
import androidx.work.OutOfQuotaPolicy
11-
import androidx.work.PeriodicWorkRequest
12-
import androidx.work.WorkRequest
5+
import androidx.work.*
136
import dev.fluttercommunity.workmanager.WorkManagerCall.CancelTask.ByTag.KEYS.UNREGISTER_TASK_TAG_KEY
147
import dev.fluttercommunity.workmanager.WorkManagerCall.CancelTask.ByUniqueName.KEYS.UNREGISTER_TASK_UNIQUE_NAME_KEY
158
import dev.fluttercommunity.workmanager.WorkManagerCall.Initialize.KEYS.INITIALIZE_TASK_CALL_HANDLE_KEY
@@ -159,7 +152,27 @@ sealed class WorkManagerCall {
159152

160153
class Failed(val code: String) : WorkManagerCall()
161154
}
162-
155+
data class SetForeground(
156+
val foregroundServiceType: Int,
157+
val notificationId: Int,
158+
val notificationChannelId: String,
159+
val notificationChannelName: String,
160+
val notificationChannelDescription: String,
161+
val notificationChannelImportance: Int,
162+
val notificationTitle: String,
163+
val notificationDescription: String,
164+
) {
165+
companion object KEYS {
166+
const val FOREGROUND_SERVICE_TYPE_KEY = "foregroundServiceType"
167+
const val NOTIFICATION_ID_KEY = "notificationId"
168+
const val NOTIFICATION_CHANNEL_ID_KEY = "notificationChannelId"
169+
const val NOTIFICATION_CHANNEL_NAME_KEY = "notificationChannelName"
170+
const val NOTIFICATION_CHANNEL_DESCRIPTION_KEY = "notificationChannelDescription"
171+
const val NOTIFICATION_CHANNEL_IMPORTANCE_KEY = "notificationChannelImportance"
172+
const val NOTIFICATION_TITLE_KEY = "notificationTitle"
173+
const val NOTIFICATION_DESCRIPTION_KEY = "notificationDescription"
174+
}
175+
}
163176
private enum class TaskType(val minimumBackOffDelay: Long) {
164177
ONE_OFF(WorkRequest.MIN_BACKOFF_MILLIS),
165178
PERIODIC(WorkRequest.MIN_BACKOFF_MILLIS),
@@ -190,6 +203,34 @@ object Extractor {
190203
}
191204
}
192205

206+
fun parseSetForegroundCall(call: MethodCall): SetForeground {
207+
val foregroundServiceType =
208+
call.argument<Int>(SetForeground.KEYS.FOREGROUND_SERVICE_TYPE_KEY)!!
209+
val notificationId = call.argument<Int>(SetForeground.KEYS.NOTIFICATION_ID_KEY)!!
210+
val notificationChannelId =
211+
call.argument<String>(SetForeground.KEYS.NOTIFICATION_CHANNEL_ID_KEY)!!
212+
val notificationChannelName =
213+
call.argument<String>(SetForeground.KEYS.NOTIFICATION_CHANNEL_NAME_KEY)!!
214+
val notificationChannelDescription =
215+
call.argument<String>(SetForeground.KEYS.NOTIFICATION_CHANNEL_DESCRIPTION_KEY)!!
216+
val notificationChannelImportance =
217+
call.argument<Int>(SetForeground.KEYS.NOTIFICATION_CHANNEL_IMPORTANCE_KEY)!!
218+
val notificationTitle =
219+
call.argument<String>(SetForeground.KEYS.NOTIFICATION_TITLE_KEY)!!
220+
val notificationDescription =
221+
call.argument<String>(SetForeground.KEYS.NOTIFICATION_DESCRIPTION_KEY)!!
222+
return SetForeground(
223+
foregroundServiceType,
224+
notificationId,
225+
notificationChannelId,
226+
notificationChannelName,
227+
notificationChannelDescription,
228+
notificationChannelImportance,
229+
notificationTitle,
230+
notificationDescription,
231+
)
232+
}
233+
193234
fun extractWorkManagerCallFromRawMethodName(call: MethodCall): WorkManagerCall =
194235
when (PossibleWorkManagerCall.fromRawMethodName(call.method)) {
195236
PossibleWorkManagerCall.INITIALIZE -> {
@@ -202,6 +243,7 @@ object Extractor {
202243
WorkManagerCall.Initialize(handle, inDebugMode)
203244
}
204245
}
246+
205247
PossibleWorkManagerCall.REGISTER_ONE_OFF_TASK -> {
206248
WorkManagerCall.RegisterTask.OneOffTask(
207249
isInDebugMode = call.argument<Boolean>(REGISTER_TASK_IS_IN_DEBUG_MODE_KEY)!!,
@@ -213,13 +255,14 @@ object Extractor {
213255
constraintsConfig = extractConstraintConfigFromCall(call),
214256
outOfQuotaPolicy = extractOutOfQuotaPolicyFromCall(call),
215257
backoffPolicyConfig =
216-
extractBackoffPolicyConfigFromCall(
217-
call,
218-
TaskType.ONE_OFF,
219-
),
258+
extractBackoffPolicyConfigFromCall(
259+
call,
260+
TaskType.ONE_OFF,
261+
),
220262
payload = extractPayload(call),
221263
)
222264
}
265+
223266
PossibleWorkManagerCall.REGISTER_PERIODIC_TASK -> {
224267
WorkManagerCall.RegisterTask.PeriodicTask(
225268
isInDebugMode = call.argument<Boolean>(REGISTER_TASK_IS_IN_DEBUG_MODE_KEY)!!,
@@ -232,10 +275,10 @@ object Extractor {
232275
initialDelaySeconds = extractInitialDelayFromCall(call),
233276
constraintsConfig = extractConstraintConfigFromCall(call),
234277
backoffPolicyConfig =
235-
extractBackoffPolicyConfigFromCall(
236-
call,
237-
TaskType.PERIODIC,
238-
),
278+
extractBackoffPolicyConfigFromCall(
279+
call,
280+
TaskType.PERIODIC,
281+
),
239282
outOfQuotaPolicy = extractOutOfQuotaPolicyFromCall(call),
240283
payload = extractPayload(call),
241284
)
@@ -251,12 +294,14 @@ object Extractor {
251294
WorkManagerCall.CancelTask.ByUniqueName(
252295
call.argument(UNREGISTER_TASK_UNIQUE_NAME_KEY)!!,
253296
)
297+
254298
PossibleWorkManagerCall.CANCEL_TASK_BY_TAG ->
255299
WorkManagerCall.CancelTask.ByTag(
256300
call.argument(
257301
UNREGISTER_TASK_TAG_KEY,
258302
)!!,
259303
)
304+
260305
PossibleWorkManagerCall.CANCEL_ALL -> WorkManagerCall.CancelTask.All
261306

262307
PossibleWorkManagerCall.UNKNOWN -> WorkManagerCall.Unknown

workmanager/android/src/main/kotlin/dev/fluttercommunity/workmanager/WorkmanagerCallHandler.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dev.fluttercommunity.workmanager
22

33
import android.content.Context
4+
import android.content.pm.ServiceInfo
45
import androidx.work.Constraints
56
import androidx.work.Data
67
import androidx.work.ExistingPeriodicWorkPolicy

0 commit comments

Comments
 (0)