fjaroli

WorkManager Flutter

Mar 9th, 2023
29
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 8.51 KB | None | 0 0
  1. package be.tramckrijte.workmanager
  2.  
  3. import android.app.Notification
  4. import android.app.NotificationChannel
  5. import android.app.NotificationManager
  6. import android.content.Context
  7. import android.os.Build
  8. import android.os.Handler
  9. import android.os.Looper
  10. import android.util.Log
  11. import androidx.annotation.RequiresApi
  12. import androidx.concurrent.futures.ResolvableFuture
  13. import androidx.core.app.NotificationCompat
  14. import androidx.core.content.ContextCompat.getSystemService
  15. import androidx.work.ForegroundInfo
  16. import androidx.work.ListenableWorker
  17. import androidx.work.WorkManager
  18. import androidx.work.WorkerParameters
  19. import com.google.common.util.concurrent.ListenableFuture
  20. import io.flutter.embedding.engine.FlutterEngine
  21. import io.flutter.embedding.engine.dart.DartExecutor
  22. import io.flutter.embedding.engine.loader.FlutterLoader
  23. import io.flutter.embedding.engine.plugins.shim.ShimPluginRegistry
  24. import io.flutter.plugin.common.MethodCall
  25. import io.flutter.plugin.common.MethodChannel
  26. import io.flutter.view.FlutterCallbackInformation
  27. import java.util.*
  28.  
  29.  
  30. /***
  31. * A simple worker that will post your input back to your Flutter application.
  32. *
  33. * It will block the background thread until a value of either true or false is received back from Flutter code.
  34. */
  35. class BackgroundWorker(
  36. applicationContext: Context,
  37. private val workerParams: WorkerParameters
  38. ) : ListenableWorker(applicationContext, workerParams), MethodChannel.MethodCallHandler {
  39. private lateinit var backgroundChannel: MethodChannel
  40.  
  41. companion object {
  42. const val TAG = "BackgroundWorker"
  43.  
  44. const val PAYLOAD_KEY = "be.tramckrijte.workmanager.INPUT_DATA"
  45. const val DART_TASK_KEY = "be.tramckrijte.workmanager.DART_TASK"
  46. const val IS_IN_DEBUG_MODE_KEY = "be.tramckrijte.workmanager.IS_IN_DEBUG_MODE_KEY"
  47.  
  48. const val BACKGROUND_CHANNEL_NAME =
  49. "be.tramckrijte.workmanager/background_channel_work_manager"
  50. const val BACKGROUND_CHANNEL_INITIALIZED = "backgroundChannelInitialized"
  51.  
  52. private val flutterLoader = FlutterLoader()
  53. }
  54.  
  55. private val payload
  56. get() = workerParams.inputData.getString(PAYLOAD_KEY)
  57.  
  58. private val dartTask
  59. get() = workerParams.inputData.getString(DART_TASK_KEY)!!
  60.  
  61. private val isInDebug
  62. get() = workerParams.inputData.getBoolean(IS_IN_DEBUG_MODE_KEY, false)
  63.  
  64. private val randomThreadIdentifier = Random().nextInt()
  65. private var engine: FlutterEngine? = null
  66.  
  67. private var startTime: Long = 0
  68. private val resolvableFuture = ResolvableFuture.create<Result>()
  69.  
  70. override fun startWork(): ListenableFuture<Result> {
  71. setForegroundAsync(createForegroundInfo());
  72.  
  73. startTime = System.currentTimeMillis()
  74.  
  75. engine = FlutterEngine(applicationContext)
  76.  
  77. if (!flutterLoader.initialized()) {
  78. flutterLoader.startInitialization(applicationContext)
  79. }
  80.  
  81. flutterLoader.ensureInitializationCompleteAsync(
  82. applicationContext,
  83. null,
  84. Handler(Looper.getMainLooper())
  85. ) {
  86. val callbackHandle = SharedPreferenceHelper.getCallbackHandle(applicationContext)
  87. val callbackInfo = FlutterCallbackInformation.lookupCallbackInformation(callbackHandle)
  88. val dartBundlePath = flutterLoader.findAppBundlePath()
  89.  
  90. if (isInDebug) {
  91. DebugHelper.postTaskStarting(
  92. applicationContext,
  93. randomThreadIdentifier,
  94. dartTask,
  95. payload,
  96. callbackHandle,
  97. callbackInfo,
  98. dartBundlePath
  99. )
  100. }
  101.  
  102. // Backwards compatibility with v1. We register all the user's plugins.
  103. WorkmanagerPlugin.pluginRegistryCallback?.registerWith(ShimPluginRegistry(engine!!))
  104.  
  105. engine?.let { engine ->
  106. backgroundChannel = MethodChannel(engine.dartExecutor, BACKGROUND_CHANNEL_NAME)
  107. backgroundChannel.setMethodCallHandler(this@BackgroundWorker)
  108.  
  109. engine.dartExecutor.executeDartCallback(
  110. DartExecutor.DartCallback(
  111. applicationContext.assets,
  112. dartBundlePath,
  113. callbackInfo
  114. )
  115. )
  116. }
  117. }
  118.  
  119. return resolvableFuture
  120. }
  121.  
  122. override fun getForegroundInfoAsync(): ListenableFuture<ForegroundInfo> {
  123. val future = ResolvableFuture.create<ForegroundInfo?>()
  124. future.set(createForegroundInfo())
  125. return future
  126. }
  127.  
  128. override fun onStopped() {
  129. stopEngine(null)
  130. }
  131.  
  132. private fun stopEngine(result: Result?) {
  133. val fetchDuration = System.currentTimeMillis() - startTime
  134.  
  135. if (isInDebug) {
  136. DebugHelper.postTaskCompleteNotification(
  137. applicationContext,
  138. randomThreadIdentifier,
  139. dartTask,
  140. payload,
  141. fetchDuration,
  142. result ?: Result.failure()
  143. )
  144. }
  145.  
  146. // No result indicates we were signalled to stop by WorkManager. The result is already
  147. // STOPPED, so no need to resolve another one.
  148. if (result != null) {
  149. resolvableFuture.set(result)
  150. }
  151.  
  152. // If stopEngine is called from `onStopped`, it may not be from the main thread.
  153. Handler(Looper.getMainLooper()).post {
  154. engine?.destroy()
  155. engine = null
  156. }
  157. }
  158.  
  159. override fun onMethodCall(call: MethodCall, r: MethodChannel.Result) {
  160. when (call.method) {
  161. BACKGROUND_CHANNEL_INITIALIZED ->
  162. backgroundChannel.invokeMethod(
  163. "onResultSend",
  164. mapOf(DART_TASK_KEY to dartTask, PAYLOAD_KEY to payload),
  165. object : MethodChannel.Result {
  166. override fun notImplemented() {
  167. stopEngine(Result.failure())
  168. }
  169.  
  170. override fun error(
  171. errorCode: String,
  172. errorMessage: String?,
  173. errorDetails: Any?
  174. ) {
  175. Log.e(TAG, "errorCode: $errorCode, errorMessage: $errorMessage")
  176. stopEngine(Result.failure())
  177. }
  178.  
  179. override fun success(receivedResult: Any?) {
  180. val wasSuccessFul = receivedResult?.let { it as Boolean? } == true
  181. stopEngine(if (wasSuccessFul) Result.success() else Result.retry())
  182. }
  183. }
  184. )
  185. }
  186. }
  187.  
  188. private fun createForegroundInfo(): ForegroundInfo {
  189. // Use a different id for each Notification.
  190. val notificationId = 1
  191. val notificationn = createNotification()
  192. return ForegroundInfo(notificationId, notificationn)
  193. }
  194.  
  195. private fun createNotification(): Notification {
  196. // This PendingIntent can be used to cancel the Worker.
  197. val intent = WorkManager.getInstance(applicationContext).createCancelPendingIntent(id)
  198.  
  199. val builder = NotificationCompat.Builder(applicationContext, "background_worker")
  200. .setContentTitle("uploading post")
  201. .setTicker("title")
  202. .setOngoing(true)
  203. .setSmallIcon(android.R.drawable.stat_notify_sync)
  204. .setChannelId("background_worker")
  205. .setPriority(NotificationCompat.PRIORITY_DEFAULT)
  206. .addAction(R.drawable.notification_bg, "cancel", intent)
  207. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
  208. createNotificationChannel("background_worker", "background_worker").also {
  209. builder.setChannelId(it.id)
  210. }
  211. }
  212. return builder.build()
  213. }
  214.  
  215. @RequiresApi(Build.VERSION_CODES.O)
  216. private fun createNotificationChannel(
  217. channelId: String,
  218. name: String
  219. ): NotificationChannel {
  220. return NotificationChannel(
  221. channelId, name, NotificationManager.IMPORTANCE_LOW
  222. ).also { channel ->
  223. val mNotificationManager =
  224. applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager?
  225. mNotificationManager?.createNotificationChannel(channel)
  226. }
  227. }
  228. }
  229.  
Advertisement
Add Comment
Please, Sign In to add comment