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

Fix for: FATAL EXCEPTION: java.lang.UnsatisfiedLinkError: No implementation found for io.flutter.view.FlutterCallbackInformation io.flutter.embedding.engine.FlutterJNI.nativeLookupCallbackInformation #263 #292

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,20 @@ import rekab.app.background_locator.pluggables.InitPluggable

class BackgroundLocatorPlugin
: MethodCallHandler, FlutterPlugin, PluginRegistry.NewIntentListener, ActivityAware {
private var context: Context? = null
internal var context: Context? = null
private var activity: Activity? = null

companion object {
@JvmStatic
private var channel: MethodChannel? = null

@JvmStatic
private fun sendResultWithDelay(context: Context, result: Result?, value: Boolean, delay: Long) {
private fun sendResultWithDelay(
context: Context,
result: Result?,
value: Boolean,
delay: Long
) {
context.mainLooper.let {
Handler(it).postDelayed({
result?.success(value)
Expand All @@ -42,24 +47,32 @@ class BackgroundLocatorPlugin

@SuppressLint("MissingPermission")
@JvmStatic
private fun registerLocator(context: Context,
args: Map<Any, Any>,
result: Result?) {
private fun registerLocator(
context: Context,
args: Map<Any, Any>,
result: Result?
) {
if (IsolateHolderService.isServiceRunning) {
// The service is running already
Log.d("BackgroundLocatorPlugin", "Locator service is already running")
result?.success(true)
return
}

Log.d("BackgroundLocatorPlugin",
"start locator with ${PreferencesManager.getLocationClient(context)} client")
Log.d(
"BackgroundLocatorPlugin",
"start locator with ${PreferencesManager.getLocationClient(context)} client"
)

val callbackHandle = args[Keys.ARG_CALLBACK] as Long
PreferencesManager.setCallbackHandle(context, Keys.CALLBACK_HANDLE_KEY, callbackHandle)

val notificationCallback = args[Keys.ARG_NOTIFICATION_CALLBACK] as? Long
PreferencesManager.setCallbackHandle(context, Keys.NOTIFICATION_CALLBACK_HANDLE_KEY, notificationCallback)
PreferencesManager.setCallbackHandle(
context,
Keys.NOTIFICATION_CALLBACK_HANDLE_KEY,
notificationCallback
)

// Call InitPluggable with initCallbackHandle
(args[Keys.ARG_INIT_CALLBACK] as? Long)?.let { initCallbackHandle ->
Expand All @@ -81,8 +94,9 @@ class BackgroundLocatorPlugin
val settings = args[Keys.ARG_SETTINGS] as Map<*, *>

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
context.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_DENIED) {
context.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_DENIED
) {

val msg = "'registerLocator' requires the ACCESS_FINE_LOCATION permission."
result?.error(msg, null, null)
Expand All @@ -97,34 +111,60 @@ class BackgroundLocatorPlugin
}

@JvmStatic
private fun startIsolateService(context: Context, settings: Map<*, *>) {
internal fun startIsolateService(context: Context, settings: Map<*, *>) {
Log.e("BackgroundLocatorPlugin", "startIsolateService")
val intent = Intent(context, IsolateHolderService::class.java)
intent.action = IsolateHolderService.ACTION_START
intent.putExtra(Keys.SETTINGS_ANDROID_NOTIFICATION_CHANNEL_NAME,
settings[Keys.SETTINGS_ANDROID_NOTIFICATION_CHANNEL_NAME] as String)
intent.putExtra(Keys.SETTINGS_ANDROID_NOTIFICATION_TITLE,
settings[Keys.SETTINGS_ANDROID_NOTIFICATION_TITLE] as String)
intent.putExtra(Keys.SETTINGS_ANDROID_NOTIFICATION_MSG,
settings[Keys.SETTINGS_ANDROID_NOTIFICATION_MSG] as String)
intent.putExtra(Keys.SETTINGS_ANDROID_NOTIFICATION_BIG_MSG,
settings[Keys.SETTINGS_ANDROID_NOTIFICATION_BIG_MSG] as String)
intent.putExtra(Keys.SETTINGS_ANDROID_NOTIFICATION_ICON,
settings[Keys.SETTINGS_ANDROID_NOTIFICATION_ICON] as String)
intent.putExtra(Keys.SETTINGS_ANDROID_NOTIFICATION_ICON_COLOR,
settings[Keys.SETTINGS_ANDROID_NOTIFICATION_ICON_COLOR] as Long)
intent.putExtra(Keys.SETTINGS_INTERVAL, settings[Keys.SETTINGS_INTERVAL] as Int)
intent.putExtra(Keys.SETTINGS_ACCURACY, settings[Keys.SETTINGS_ACCURACY] as Int)
intent.putExtra(Keys.SETTINGS_DISTANCE_FILTER, settings[Keys.SETTINGS_DISTANCE_FILTER] as Double)
intent.putExtra(
Keys.SETTINGS_ANDROID_NOTIFICATION_CHANNEL_NAME,
settings[Keys.SETTINGS_ANDROID_NOTIFICATION_CHANNEL_NAME] as? String
)
intent.putExtra(
Keys.SETTINGS_ANDROID_NOTIFICATION_TITLE,
settings[Keys.SETTINGS_ANDROID_NOTIFICATION_TITLE] as? String
)
intent.putExtra(
Keys.SETTINGS_ANDROID_NOTIFICATION_MSG,
settings[Keys.SETTINGS_ANDROID_NOTIFICATION_MSG] as? String
)
intent.putExtra(
Keys.SETTINGS_ANDROID_NOTIFICATION_BIG_MSG,
settings[Keys.SETTINGS_ANDROID_NOTIFICATION_BIG_MSG] as? String
)
intent.putExtra(
Keys.SETTINGS_ANDROID_NOTIFICATION_ICON,
settings[Keys.SETTINGS_ANDROID_NOTIFICATION_ICON] as? String
)
intent.putExtra(
Keys.SETTINGS_ANDROID_NOTIFICATION_ICON_COLOR,
settings[Keys.SETTINGS_ANDROID_NOTIFICATION_ICON_COLOR] as? Long
)
intent.putExtra(Keys.SETTINGS_INTERVAL, settings[Keys.SETTINGS_INTERVAL] as? Int)
intent.putExtra(Keys.SETTINGS_ACCURACY, settings[Keys.SETTINGS_ACCURACY] as? Int)
intent.putExtra(
Keys.SETTINGS_DISTANCE_FILTER,
settings[Keys.SETTINGS_DISTANCE_FILTER] as? Double
)

if (settings.containsKey(Keys.SETTINGS_ANDROID_WAKE_LOCK_TIME)) {
intent.putExtra(Keys.SETTINGS_ANDROID_WAKE_LOCK_TIME,
settings[Keys.SETTINGS_ANDROID_WAKE_LOCK_TIME] as Int)
intent.putExtra(
Keys.SETTINGS_ANDROID_WAKE_LOCK_TIME,
settings[Keys.SETTINGS_ANDROID_WAKE_LOCK_TIME] as? Int
)
}

if (PreferencesManager.getCallbackHandle(context, Keys.INIT_CALLBACK_HANDLE_KEY) != null) {
if (PreferencesManager.getCallbackHandle(
context,
Keys.INIT_CALLBACK_HANDLE_KEY
) != null
) {
intent.putExtra(Keys.SETTINGS_INIT_PLUGGABLE, true)
}
if (PreferencesManager.getCallbackHandle(context, Keys.DISPOSE_CALLBACK_HANDLE_KEY) != null) {
if (PreferencesManager.getCallbackHandle(
context,
Keys.DISPOSE_CALLBACK_HANDLE_KEY
) != null
) {
intent.putExtra(Keys.SETTINGS_DISPOSABLE_PLUGGABLE, true)
}

Expand All @@ -139,7 +179,7 @@ class BackgroundLocatorPlugin
}

@JvmStatic
private fun initializeService(context: Context, args: Map<Any, Any>) {
internal fun initializeService(context: Context, args: Map<Any, Any>) {
val callbackHandle: Long = args[Keys.ARG_CALLBACK_DISPATCHER] as Long
setCallbackDispatcherHandle(context, callbackHandle)
}
Expand Down Expand Up @@ -171,16 +211,22 @@ class BackgroundLocatorPlugin
val intent = Intent(context, IsolateHolderService::class.java)
intent.action = IsolateHolderService.ACTION_UPDATE_NOTIFICATION
if (args.containsKey(Keys.SETTINGS_ANDROID_NOTIFICATION_TITLE)) {
intent.putExtra(Keys.SETTINGS_ANDROID_NOTIFICATION_TITLE,
args[Keys.SETTINGS_ANDROID_NOTIFICATION_TITLE] as String)
intent.putExtra(
Keys.SETTINGS_ANDROID_NOTIFICATION_TITLE,
args[Keys.SETTINGS_ANDROID_NOTIFICATION_TITLE] as String
)
}
if (args.containsKey(Keys.SETTINGS_ANDROID_NOTIFICATION_MSG)) {
intent.putExtra(Keys.SETTINGS_ANDROID_NOTIFICATION_MSG,
args[Keys.SETTINGS_ANDROID_NOTIFICATION_MSG] as String)
intent.putExtra(
Keys.SETTINGS_ANDROID_NOTIFICATION_MSG,
args[Keys.SETTINGS_ANDROID_NOTIFICATION_MSG] as String
)
}
if (args.containsKey(Keys.SETTINGS_ANDROID_NOTIFICATION_BIG_MSG)) {
intent.putExtra(Keys.SETTINGS_ANDROID_NOTIFICATION_BIG_MSG,
args[Keys.SETTINGS_ANDROID_NOTIFICATION_BIG_MSG] as String)
intent.putExtra(
Keys.SETTINGS_ANDROID_NOTIFICATION_BIG_MSG,
args[Keys.SETTINGS_ANDROID_NOTIFICATION_BIG_MSG] as String
)
}

ContextCompat.startForegroundService(context, intent)
Expand All @@ -189,9 +235,9 @@ class BackgroundLocatorPlugin
@JvmStatic
private fun setCallbackDispatcherHandle(context: Context, handle: Long) {
context.getSharedPreferences(Keys.SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE)
.edit()
.putLong(Keys.CALLBACK_DISPATCHER_HANDLE_KEY, handle)
.apply()
.edit()
.putLong(Keys.CALLBACK_DISPATCHER_HANDLE_KEY, handle)
.apply()
}

@JvmStatic
Expand All @@ -202,7 +248,13 @@ class BackgroundLocatorPlugin
plugin.context = context

initializeService(context, settings)
startIsolateService(context, settings)

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
context.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED
) {
startIsolateService(context, settings)
}
}
}

Expand All @@ -223,9 +275,11 @@ class BackgroundLocatorPlugin
// save setting to use it when device reboots
PreferencesManager.saveSettings(context!!, args)

registerLocator(context!!,
args,
result)
registerLocator(
context!!,
args,
result
)
}
Keys.METHOD_PLUGIN_UN_REGISTER_LOCATION_UPDATE -> {
unRegisterPlugin(context!!, result)
Expand Down Expand Up @@ -266,16 +320,27 @@ class BackgroundLocatorPlugin
return false
}

val notificationCallback = PreferencesManager.getCallbackHandle(activity!!, Keys.NOTIFICATION_CALLBACK_HANDLE_KEY)
if (notificationCallback != null && IsolateHolderService.backgroundEngine != null) {
val backgroundChannel =
MethodChannel(IsolateHolderService.backgroundEngine?.dartExecutor?.binaryMessenger, Keys.BACKGROUND_CHANNEL_ID)
activity?.mainLooper?.let {
Handler(it)
IsolateHolderService.getBinaryMessenger(context)?.let { binaryMessenger ->
val notificationCallback =
PreferencesManager.getCallbackHandle(
activity!!,
Keys.NOTIFICATION_CALLBACK_HANDLE_KEY
)
if (notificationCallback != null && IsolateHolderService.backgroundEngine != null) {
val backgroundChannel =
MethodChannel(
binaryMessenger,
Keys.BACKGROUND_CHANNEL_ID
)
activity?.mainLooper?.let {
Handler(it)
.post {
backgroundChannel.invokeMethod(Keys.BCM_NOTIFICATION_CLICK,
hashMapOf(Keys.ARG_NOTIFICATION_CALLBACK to notificationCallback))
backgroundChannel.invokeMethod(
Keys.BCM_NOTIFICATION_CLICK,
hashMapOf(Keys.ARG_NOTIFICATION_CALLBACK to notificationCallback)
)
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
package rekab.app.background_locator

import android.Manifest
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.util.Log
import com.google.android.gms.location.LocationRequest
import io.flutter.FlutterInjector
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.embedding.engine.dart.DartExecutor
import io.flutter.plugin.common.MethodChannel
import io.flutter.view.FlutterCallbackInformation
import rekab.app.background_locator.IsolateHolderService.Companion.isServiceInitialized
import rekab.app.background_locator.provider.LocationRequestOptions
import java.lang.RuntimeException
import java.util.concurrent.atomic.AtomicBoolean

internal fun IsolateHolderService.startLocatorService(context: Context) {
Expand All @@ -18,30 +24,56 @@ internal fun IsolateHolderService.startLocatorService(context: Context) {
synchronized(serviceStarted) {
this.context = context
if (IsolateHolderService.backgroundEngine == null) {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
context.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED
) {
val callbackHandle = context.getSharedPreferences(
Keys.SHARED_PREFERENCES_KEY,
Context.MODE_PRIVATE
)
.getLong(Keys.CALLBACK_DISPATCHER_HANDLE_KEY, 0)
val callbackInfo =
FlutterCallbackInformation.lookupCallbackInformation(callbackHandle)

val callbackHandle = context.getSharedPreferences(
Keys.SHARED_PREFERENCES_KEY,
Context.MODE_PRIVATE)
.getLong(Keys.CALLBACK_DISPATCHER_HANDLE_KEY, 0)
val callbackInfo = FlutterCallbackInformation.lookupCallbackInformation(callbackHandle)
// We need flutter engine to handle callback, so if it is not available we have to create a
// Flutter engine without any view
Log.e("IsolateHolderService", "startLocatorService: Start Flutter Enginer")
IsolateHolderService.backgroundEngine = FlutterEngine(context)

// We need flutter engine to handle callback, so if it is not available we have to create a
// Flutter engine without any view
IsolateHolderService.backgroundEngine = FlutterEngine(context)
val args = DartExecutor.DartCallback(
context.assets,
FlutterInjector.instance().flutterLoader().findAppBundlePath(),
callbackInfo
)
IsolateHolderService.backgroundEngine?.dartExecutor?.executeDartCallback(args)
isServiceInitialized = true
Log.e("IsolateHolderExtension", "service initialized")
}
} catch (e: UnsatisfiedLinkError) {
e.printStackTrace()
}
}
}

val args = DartExecutor.DartCallback(
context.assets,
FlutterInjector.instance().flutterLoader().findAppBundlePath(),
callbackInfo
IsolateHolderService.getBinaryMessenger(context)?.let { binaryMessenger ->
backgroundChannel =
MethodChannel(
binaryMessenger,
Keys.BACKGROUND_CHANNEL_ID
)
IsolateHolderService.backgroundEngine?.dartExecutor?.executeDartCallback(args)
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
context.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED
) {
backgroundChannel.setMethodCallHandler(this)
}
} catch (e: RuntimeException) {
e.printStackTrace()
}
}

backgroundChannel =
MethodChannel(IsolateHolderService.backgroundEngine?.dartExecutor?.binaryMessenger,
Keys.BACKGROUND_CHANNEL_ID)
backgroundChannel.setMethodCallHandler(this)
}

fun getLocationRequest(intent: Intent): LocationRequestOptions {
Expand Down
Loading