Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP][MOBILE-2196] Draft PR for background isolates #84

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,9 @@
<meta-data android:name="com.urbanairship.autopilot"
android:value="com.airship.flutter.FlutterAutopilot"/>

<service android:name="com.airship.flutter.EventService"
android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="true"/>

</application>
</manifest>
44 changes: 34 additions & 10 deletions android/src/main/kotlin/com/airship/flutter/AirshipPlugin.kt
Original file line number Diff line number Diff line change
@@ -1,25 +1,31 @@
package com.airship.flutter

import android.graphics.Bitmap
import android.webkit.WebView
import EventPlugin
import android.app.NotificationManager
import android.content.Context
import android.view.View
import android.content.Intent
import android.graphics.Bitmap
import android.os.Build
import android.util.Log
import android.view.View
import android.webkit.WebView
import androidx.core.app.NotificationManagerCompat
import com.urbanairship.UAirship
import com.urbanairship.analytics.CustomEvent
import com.urbanairship.automation.InAppAutomation
import com.urbanairship.channel.AttributeEditor
import com.urbanairship.channel.TagGroupsEditor
import com.urbanairship.json.JsonMap
import com.urbanairship.json.JsonValue
import com.urbanairship.channel.TagGroupsEditor
import com.urbanairship.channel.AttributeEditor
import com.urbanairship.messagecenter.MessageCenter
import com.urbanairship.util.DateUtils
import com.urbanairship.util.UAStringUtil
import com.urbanairship.messagecenter.webkit.MessageWebView
import com.urbanairship.messagecenter.webkit.MessageWebViewClient
import java.lang.NumberFormatException
import com.urbanairship.util.DateUtils
import com.urbanairship.util.UAStringUtil
import io.flutter.FlutterInjector
import io.flutter.embedding.engine.loader.FlutterLoader
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
Expand All @@ -28,9 +34,8 @@ import io.flutter.plugin.common.PluginRegistry.Registrar
import io.flutter.plugin.common.StandardMessageCodec
import io.flutter.plugin.platform.PlatformView
import io.flutter.plugin.platform.PlatformViewFactory
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.platform.PlatformViewRegistry
import io.flutter.view.FlutterMain

private const val TAG_OPERATION_GROUP_NAME = "group"
private const val TAG_OPERATION_TYPE = "operationType"
Expand Down Expand Up @@ -174,11 +179,30 @@ class AirshipPlugin : MethodCallHandler, FlutterPlugin {
"getPushTokenRegistrationEnabled" -> getPushTokenRegistrationEnabled(result)
"setDataCollectionEnabled" -> setDataCollectionEnabled(call, result)
"setPushTokenRegistrationEnabled" -> setPushTokenRegistrationEnabled(call, result)
"performAction" -> performAction(call, result)

else -> result.notImplemented()
}
}

private fun performAction(call: MethodCall, result: Result) {
val args = call.arguments<ArrayList<*>>()
EventPlugin.saveCallBackHandle(context, args)

val flutterLoader = FlutterInjector.instance().flutterLoader()
flutterLoader.startInitialization(UAirship.getApplicationContext())
flutterLoader.ensureInitializationComplete(UAirship.getApplicationContext(), null)
var intent = Intent()
intent.putExtra("payload", args!![1] as String)
EventService.enqueueWork(UAirship.getApplicationContext(), intent)

for (progress in 0..1000) {
Log.d("UALibUlrich", "Main Isolate progress : $progress")
}

result.success(true)
}

private fun getInboxMessages(result: Result) {
val messages = MessageCenter.shared().inbox.messages
.map { message ->
Expand Down
85 changes: 85 additions & 0 deletions android/src/main/kotlin/com/airship/flutter/EventPlugin.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import android.content.Context
import android.content.Intent
import android.util.Log
import com.airship.flutter.EventService
import com.urbanairship.UAirship
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.Result
import io.flutter.view.FlutterMain

class EventPlugin : FlutterPlugin, MethodChannel.MethodCallHandler {

private var mContext : Context? = null

companion object {

@JvmStatic
val EVENT_PREFERENCES_KEY = "event_plugin_cache"

@JvmStatic
val CALLBACK_DISPATCHER_HANDLE_KEY = "callback_dispatch_handler"

@JvmStatic
fun saveCallBackHandle(context: Context, args: ArrayList<*>?) {
val callbackHandle = args!![0] as Long

context.getSharedPreferences(EVENT_PREFERENCES_KEY, Context.MODE_PRIVATE)
.edit()
.putLong(CALLBACK_DISPATCHER_HANDLE_KEY, callbackHandle)
.apply()
}

/*@JvmStatic
private fun performAction(call: MethodCall, result: Result) {
val args = call.arguments<ArrayList<*>>()
//EventPlugin.saveCallBackHandle(context, args)

Log.d("UALibUlrich", "test Ulrich")
Log.d("UALibUlrich", "payload: " + "oh oh")

FlutterMain.startInitialization(UAirship.getApplicationContext())
FlutterMain.ensureInitializationComplete(UAirship.getApplicationContext(), null)
var intent = Intent()
intent.putExtra("payload", args!![1] as String)
EventService.enqueueWork(UAirship.getApplicationContext(), intent)

result.success(true)
}*/

@JvmStatic
fun test(payload: String) {
Log.d("UALibUlrich", "test Ulrich")
Log.d("UALibUlrich", "payload: $payload")
}
}



override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
mContext = binding.getApplicationContext()

val channel = MethodChannel(binding.binaryMessenger, "com.airship.flutter/event_plugin")
channel.setMethodCallHandler(this)
}

override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
mContext = null
}

override fun onMethodCall(call: MethodCall, result: Result) {
/*val args = call.arguments<ArrayList<*>>()
when(call.method) {
"EventPlugin.performAction" -> {
saveCallBackHandle(mContext!!, args)
performAction(call, result)
}
else -> {
print("Unknown method.")
result.notImplemented()
}
}*/
}

}
117 changes: 117 additions & 0 deletions android/src/main/kotlin/com/airship/flutter/EventService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package com.airship.flutter

import EventPlugin
import android.content.Context
import android.content.Intent
import android.util.Log
import androidx.core.app.JobIntentService
import com.urbanairship.UAirship
import io.flutter.FlutterInjector
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.embedding.engine.dart.DartExecutor
import io.flutter.embedding.engine.loader.FlutterLoader
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.view.FlutterCallbackInformation
import io.flutter.view.FlutterMain
import java.util.*
import java.util.concurrent.atomic.AtomicBoolean

class EventService : MethodChannel.MethodCallHandler, JobIntentService() {

private lateinit var context: Context
private lateinit var mBackgroundChannel: MethodChannel
private val queue = ArrayDeque<Any>()

companion object {
@JvmStatic
private val JOB_ID = UUID.randomUUID().mostSignificantBits.toInt()

@JvmStatic
private var sBackgroundFlutterEngine: FlutterEngine? = null

@JvmStatic
private val sServiceStarted = AtomicBoolean(false)

@JvmStatic
fun enqueueWork(context: Context, work: Intent) {
enqueueWork(context, EventService::class.java, JOB_ID, work)
}
}

private fun startEventService(context: Context) {
synchronized(sServiceStarted) {
this.context = context
if (sBackgroundFlutterEngine == null) {
val callbackHandle = context.getSharedPreferences(
EventPlugin.EVENT_PREFERENCES_KEY,
Context.MODE_PRIVATE)
.getLong(EventPlugin.CALLBACK_DISPATCHER_HANDLE_KEY, 0)
if (callbackHandle == 0L) {
Log.e("UALibUlrich", "Fatal: no callback registered")
return
}
val callbackInfo = FlutterCallbackInformation.lookupCallbackInformation(callbackHandle)
if (callbackInfo == null) {
Log.e("UALibUlrich", "Fatal: failed to find callback")
return
}
sBackgroundFlutterEngine = FlutterEngine(context)

val args = DartExecutor.DartCallback(
context.assets,
FlutterInjector.instance().flutterLoader().findAppBundlePath(),
callbackInfo
)
// Start running callback dispatcher code in our background FlutterEngine instance.
sBackgroundFlutterEngine!!.dartExecutor.executeDartCallback(args)
}
}

mBackgroundChannel = MethodChannel(sBackgroundFlutterEngine!!.dartExecutor.binaryMessenger, "com.airship.flutter/event_plugin_background")
mBackgroundChannel.setMethodCallHandler(this)
}

override fun onCreate() {
super.onCreate()
startEventService(this)
}

override fun onHandleWork(intent: Intent) {

val payload = intent.extras?.get("payload")

for (progress in 0..1000) {
Log.d("UALibUlrich","New Isolate progress : $progress")
}

EventPlugin.test(payload as String)

synchronized(sServiceStarted) {
if (!sServiceStarted.get()) {
queue.add(payload)
} else {
// Callback method name is intentionally left blank.
mBackgroundChannel.invokeMethod("", payload)
}
}
}

override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
if (call.method == "EventService.performed") {

Log.d("UALibUlrich", "test Ulrich performed")

synchronized(sServiceStarted) {
while (!queue.isEmpty()) {
mBackgroundChannel.invokeMethod("", queue.remove())
}
sServiceStarted.set(true)
result.success(null)
}
} else {
result.notImplemented()
}
}

}
11 changes: 11 additions & 0 deletions example/ios/Runner/AppDelegate.swift
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
import UIKit
import Flutter
import airship_flutter

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {

// func registerPlugins(_ registry: (NSObjectProtocol & FlutterPluginRegistry)?) {
// GeneratedPluginRegistrant.register(with: registry!)
// }

override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
//SwiftAirshipPlugin.setPluginRegistrantCallback(registerPlugins)
SwiftAirshipPlugin.setPluginRegistrantCallback({ registry in
SwiftAirshipPlugin.register(with: registry.registrar(forPlugin: "com.airship.flutter")!)
//GeneratedPluginRegistrant.register(with: registry)
})
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
1 change: 1 addition & 0 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
Future<void> initPlatformState() async {
Airship.onPushReceived.listen((event) {
debugPrint('Push Received $event');
Airship.performAction("fake_payload");
});

Airship.onNotificationResponse
Expand Down
19 changes: 19 additions & 0 deletions ios/Classes/EventPlugin.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// EventPlugin.h
// Pods
//
// Created by Ulrich Giberne on 17/05/2021.
//

//#ifndef EventPlugin_h
//#define EventPlugin_h
//
//#import <Flutter/Flutter.h>
//
//@interface EventPlugin : NSObject<FlutterPlugin>
//
////+ (void)startEventService:(int64_t)handle;
//
//@end
//
//#endif /* EventPlugin_h */
Loading