feat: Move over the service/background service API
This commit is contained in:
parent
42ff70a966
commit
dfbb64c8ae
@ -48,5 +48,6 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||||
implementation "androidx.activity:activity-ktx:1.7.2"
|
implementation "androidx.activity:activity-ktx:1.7.2"
|
||||||
|
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'
|
||||||
implementation "androidx.datastore:datastore-preferences:1.0.0"
|
implementation "androidx.datastore:datastore-preferences:1.0.0"
|
||||||
}
|
}
|
@ -4,7 +4,7 @@
|
|||||||
<application>
|
<application>
|
||||||
<provider
|
<provider
|
||||||
android:name="org.moxxy.moxxy_native.content.MoxxyFileProvider"
|
android:name="org.moxxy.moxxy_native.content.MoxxyFileProvider"
|
||||||
android:authorities="org.moxxy.moxxyv2.fileprovider"
|
android:authorities="org.moxxy.moxxyv2.fileprovider2"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:grantUriPermissions="true">
|
android:grantUriPermissions="true">
|
||||||
<meta-data
|
<meta-data
|
||||||
@ -12,8 +12,36 @@
|
|||||||
android:resource="@xml/file_paths" />
|
android:resource="@xml/file_paths" />
|
||||||
</provider>
|
</provider>
|
||||||
|
|
||||||
|
<service
|
||||||
|
android:enabled="true"
|
||||||
|
android:exported="true"
|
||||||
|
android:name="org.moxxy.moxxy_native.service.BackgroundService"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<receiver
|
||||||
|
android:name="org.moxxy.moxxy_native.service.WatchdogReceiver"
|
||||||
|
android:enabled="true"
|
||||||
|
android:exported="true"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<receiver android:name="org.moxxy.moxxy_native.service.BootReceiver"
|
||||||
|
android:enabled="true"
|
||||||
|
android:exported="true">
|
||||||
|
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.BOOT_COMPLETED"/>
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
|
|
||||||
<receiver android:name="org.moxxy.moxxy_native.notifications.NotificationReceiver" />
|
<receiver android:name="org.moxxy.moxxy_native.notifications.NotificationReceiver" />
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
|
<!-- Foreground service -->
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||||
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
|
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
||||||
|
|
||||||
|
<!-- Notifications -->
|
||||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
</manifest>
|
</manifest>
|
||||||
|
@ -37,3 +37,16 @@ const val SHARED_PREFERENCES_AVATAR_KEY = "avatar_path"
|
|||||||
const val PICK_FILE_REQUEST = 42
|
const val PICK_FILE_REQUEST = 42
|
||||||
const val PICK_FILES_REQUEST = 43
|
const val PICK_FILES_REQUEST = 43
|
||||||
const val PICK_FILE_WITH_DATA_REQUEST = 44
|
const val PICK_FILE_WITH_DATA_REQUEST = 44
|
||||||
|
|
||||||
|
// Service
|
||||||
|
const val SERVICE_SHARED_PREFERENCES_KEY = "me.polynom.moxplatform_android"
|
||||||
|
const val SERVICE_ENTRYPOINT_KEY = "entrypoint_handle"
|
||||||
|
const val SERVICE_EXTRA_DATA_KEY = "extra_data"
|
||||||
|
const val SERVICE_START_AT_BOOT_KEY = "auto_start_at_boot"
|
||||||
|
const val SERVICE_MANUALLY_STOPPED_KEY = "manually_stopped"
|
||||||
|
|
||||||
|
// https://github.com/ekasetiawans/flutter_background_service/blob/e427f3b70138ec26f9671c2617f9061f25eade6f/packages/flutter_background_service_android/android/src/main/java/id/flutter/flutter_background_service/BootReceiver.java#L20
|
||||||
|
const val SERVICE_WAKELOCK_DURATION = 10 * 60 * 1000L
|
||||||
|
const val SERVICE_DEFAULT_TITLE = "Moxxy"
|
||||||
|
const val SERVICE_DEFAULT_BODY = "Preparing..."
|
||||||
|
const val SERVICE_BACKGROUND_METHOD_CHANNEL_KEY = "org.moxxy.moxxy_native/background"
|
||||||
|
@ -10,6 +10,8 @@ import androidx.annotation.NonNull
|
|||||||
import io.flutter.embedding.engine.plugins.FlutterPlugin
|
import io.flutter.embedding.engine.plugins.FlutterPlugin
|
||||||
import io.flutter.embedding.engine.plugins.activity.ActivityAware
|
import io.flutter.embedding.engine.plugins.activity.ActivityAware
|
||||||
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
|
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
|
||||||
|
import io.flutter.embedding.engine.plugins.service.ServiceAware
|
||||||
|
import io.flutter.embedding.engine.plugins.service.ServicePluginBinding
|
||||||
import io.flutter.plugin.common.EventChannel
|
import io.flutter.plugin.common.EventChannel
|
||||||
import org.moxxy.moxxy_native.contacts.ContactsImplementation
|
import org.moxxy.moxxy_native.contacts.ContactsImplementation
|
||||||
import org.moxxy.moxxy_native.contacts.MoxxyContactsApi
|
import org.moxxy.moxxy_native.contacts.MoxxyContactsApi
|
||||||
@ -25,6 +27,10 @@ import org.moxxy.moxxy_native.picker.MoxxyPickerApi
|
|||||||
import org.moxxy.moxxy_native.picker.PickerResultListener
|
import org.moxxy.moxxy_native.picker.PickerResultListener
|
||||||
import org.moxxy.moxxy_native.platform.MoxxyPlatformApi
|
import org.moxxy.moxxy_native.platform.MoxxyPlatformApi
|
||||||
import org.moxxy.moxxy_native.platform.PlatformImplementation
|
import org.moxxy.moxxy_native.platform.PlatformImplementation
|
||||||
|
import org.moxxy.moxxy_native.service.BackgroundService
|
||||||
|
import org.moxxy.moxxy_native.service.MoxxyServiceApi
|
||||||
|
import org.moxxy.moxxy_native.service.PluginTracker
|
||||||
|
import org.moxxy.moxxy_native.service.ServiceImplementation
|
||||||
|
|
||||||
object MoxxyEventChannels {
|
object MoxxyEventChannels {
|
||||||
var notificationChannel: EventChannel? = null
|
var notificationChannel: EventChannel? = null
|
||||||
@ -50,7 +56,7 @@ object NotificationCache {
|
|||||||
var lastEvent: NotificationEvent? = null
|
var lastEvent: NotificationEvent? = null
|
||||||
}
|
}
|
||||||
|
|
||||||
class MoxxyNativePlugin : FlutterPlugin, ActivityAware, MoxxyPickerApi {
|
class MoxxyNativePlugin : FlutterPlugin, ActivityAware, ServiceAware, MoxxyPickerApi {
|
||||||
private var context: Context? = null
|
private var context: Context? = null
|
||||||
private var activity: Activity? = null
|
private var activity: Activity? = null
|
||||||
private lateinit var pickerListener: PickerResultListener
|
private lateinit var pickerListener: PickerResultListener
|
||||||
@ -59,12 +65,20 @@ class MoxxyNativePlugin : FlutterPlugin, ActivityAware, MoxxyPickerApi {
|
|||||||
private lateinit var platformImplementation: PlatformImplementation
|
private lateinit var platformImplementation: PlatformImplementation
|
||||||
private val mediaImplementation = MediaImplementation()
|
private val mediaImplementation = MediaImplementation()
|
||||||
private lateinit var notificationsImplementation: NotificationsImplementation
|
private lateinit var notificationsImplementation: NotificationsImplementation
|
||||||
|
private lateinit var serviceImplementation: ServiceImplementation
|
||||||
|
|
||||||
|
var service: BackgroundService? = null
|
||||||
|
|
||||||
|
init {
|
||||||
|
PluginTracker.instances.add(this)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
|
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
|
||||||
context = flutterPluginBinding.applicationContext
|
context = flutterPluginBinding.applicationContext
|
||||||
contactsImplementation = ContactsImplementation(context!!)
|
contactsImplementation = ContactsImplementation(context!!)
|
||||||
platformImplementation = PlatformImplementation(context!!)
|
platformImplementation = PlatformImplementation(context!!)
|
||||||
notificationsImplementation = NotificationsImplementation(context!!)
|
notificationsImplementation = NotificationsImplementation(context!!)
|
||||||
|
serviceImplementation = ServiceImplementation(context!!)
|
||||||
|
|
||||||
// Register the pigeon handlers
|
// Register the pigeon handlers
|
||||||
MoxxyPickerApi.setUp(flutterPluginBinding.binaryMessenger, this)
|
MoxxyPickerApi.setUp(flutterPluginBinding.binaryMessenger, this)
|
||||||
@ -73,6 +87,7 @@ class MoxxyNativePlugin : FlutterPlugin, ActivityAware, MoxxyPickerApi {
|
|||||||
MoxxyContactsApi.setUp(flutterPluginBinding.binaryMessenger, contactsImplementation)
|
MoxxyContactsApi.setUp(flutterPluginBinding.binaryMessenger, contactsImplementation)
|
||||||
MoxxyPlatformApi.setUp(flutterPluginBinding.binaryMessenger, platformImplementation)
|
MoxxyPlatformApi.setUp(flutterPluginBinding.binaryMessenger, platformImplementation)
|
||||||
MoxxyMediaApi.setUp(flutterPluginBinding.binaryMessenger, mediaImplementation)
|
MoxxyMediaApi.setUp(flutterPluginBinding.binaryMessenger, mediaImplementation)
|
||||||
|
MoxxyServiceApi.setUp(flutterPluginBinding.binaryMessenger, serviceImplementation)
|
||||||
|
|
||||||
// Register the picker handler
|
// Register the picker handler
|
||||||
pickerListener = PickerResultListener(context!!)
|
pickerListener = PickerResultListener(context!!)
|
||||||
@ -103,6 +118,16 @@ class MoxxyNativePlugin : FlutterPlugin, ActivityAware, MoxxyPickerApi {
|
|||||||
Log.d(TAG, "Detached from activity")
|
Log.d(TAG, "Detached from activity")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onAttachedToService(binding: ServicePluginBinding) {
|
||||||
|
Log.d(TAG, "Attached to service")
|
||||||
|
service = binding.getService() as BackgroundService
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDetachedFromService() {
|
||||||
|
Log.d(TAG, "Detached from service")
|
||||||
|
service = null
|
||||||
|
}
|
||||||
|
|
||||||
override fun pickFiles(
|
override fun pickFiles(
|
||||||
type: FilePickerType,
|
type: FilePickerType,
|
||||||
multiple: Boolean,
|
multiple: Boolean,
|
||||||
|
@ -0,0 +1,280 @@
|
|||||||
|
package org.moxxy.moxxy_native.service
|
||||||
|
|
||||||
|
import android.app.AlarmManager
|
||||||
|
import android.app.PendingIntent
|
||||||
|
import android.app.Service
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.IBinder
|
||||||
|
import android.os.PowerManager
|
||||||
|
import android.os.PowerManager.WakeLock
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.core.app.AlarmManagerCompat
|
||||||
|
import androidx.core.app.NotificationCompat
|
||||||
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||||
|
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 org.moxxy.moxxy_native.R
|
||||||
|
import org.moxxy.moxxy_native.SERVICE_BACKGROUND_METHOD_CHANNEL_KEY
|
||||||
|
import org.moxxy.moxxy_native.SERVICE_DEFAULT_BODY
|
||||||
|
import org.moxxy.moxxy_native.SERVICE_DEFAULT_TITLE
|
||||||
|
import org.moxxy.moxxy_native.SERVICE_ENTRYPOINT_KEY
|
||||||
|
import org.moxxy.moxxy_native.SERVICE_EXTRA_DATA_KEY
|
||||||
|
import org.moxxy.moxxy_native.SERVICE_MANUALLY_STOPPED_KEY
|
||||||
|
import org.moxxy.moxxy_native.SERVICE_SHARED_PREFERENCES_KEY
|
||||||
|
import org.moxxy.moxxy_native.SERVICE_START_AT_BOOT_KEY
|
||||||
|
import org.moxxy.moxxy_native.SERVICE_WAKELOCK_DURATION
|
||||||
|
import org.moxxy.moxxy_native.TAG
|
||||||
|
import org.moxxy.moxxy_native.service.background.MoxxyBackgroundServiceApi
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
|
||||||
|
object BackgroundServiceStatic {
|
||||||
|
@Volatile
|
||||||
|
var wakeLock: WakeLock? = null
|
||||||
|
|
||||||
|
fun acquireWakeLock(context: Context): WakeLock {
|
||||||
|
if (wakeLock == null) {
|
||||||
|
val manager = context.getSystemService(Context.POWER_SERVICE) as PowerManager
|
||||||
|
wakeLock =
|
||||||
|
manager.newWakeLock(PowerManager.FULL_WAKE_LOCK, "${this.javaClass.name}.class")
|
||||||
|
wakeLock!!.setReferenceCounted(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return wakeLock!!
|
||||||
|
}
|
||||||
|
|
||||||
|
fun enqueue(context: Context) {
|
||||||
|
val mutable =
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) PendingIntent.FLAG_MUTABLE else 0
|
||||||
|
val pendingIntent = PendingIntent.getBroadcast(
|
||||||
|
context,
|
||||||
|
111,
|
||||||
|
Intent(context, WatchdogReceiver::class.java),
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or mutable,
|
||||||
|
)
|
||||||
|
|
||||||
|
AlarmManagerCompat.setAndAllowWhileIdle(
|
||||||
|
context.getSystemService(Context.ALARM_SERVICE) as AlarmManager,
|
||||||
|
AlarmManager.RTC_WAKEUP,
|
||||||
|
System.currentTimeMillis() + 5000,
|
||||||
|
pendingIntent,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getStartAtBoot(context: Context): Boolean {
|
||||||
|
return context.getSharedPreferences(SERVICE_SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE)
|
||||||
|
.getBoolean(
|
||||||
|
SERVICE_START_AT_BOOT_KEY,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setStartAtBoot(context: Context, value: Boolean) {
|
||||||
|
context.getSharedPreferences(SERVICE_SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE).edit()
|
||||||
|
.putBoolean(SERVICE_START_AT_BOOT_KEY, value)
|
||||||
|
.apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getManuallyStopped(context: Context): Boolean {
|
||||||
|
return context.getSharedPreferences(SERVICE_SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE)
|
||||||
|
.getBoolean(
|
||||||
|
SERVICE_MANUALLY_STOPPED_KEY,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BackgroundService : Service(), MoxxyBackgroundServiceApi {
|
||||||
|
|
||||||
|
// Indicates whether the background service is running or not
|
||||||
|
private var isRunning = AtomicBoolean(false)
|
||||||
|
|
||||||
|
// Indicates whether the service was stopped manually
|
||||||
|
private var isManuallyStopped = false
|
||||||
|
|
||||||
|
// If non-null, the Flutter Engine that is running the background service's code
|
||||||
|
private var engine: FlutterEngine? = null
|
||||||
|
|
||||||
|
// The callback for Dart to start execution at
|
||||||
|
private var dartCallback: DartExecutor.DartCallback? = null
|
||||||
|
|
||||||
|
// Method channel for Java -> Dart
|
||||||
|
private var methodChannel: MethodChannel? = null
|
||||||
|
|
||||||
|
// Data for the notification
|
||||||
|
private var notificationTitle: String = SERVICE_DEFAULT_TITLE
|
||||||
|
private var notificationBody: String = SERVICE_DEFAULT_BODY
|
||||||
|
|
||||||
|
private fun setManuallyStopped(context: Context, value: Boolean) {
|
||||||
|
context.getSharedPreferences(SERVICE_SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE).edit()
|
||||||
|
.putBoolean(SERVICE_MANUALLY_STOPPED_KEY, value)
|
||||||
|
.apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getHandle(): Long {
|
||||||
|
return getSharedPreferences(SERVICE_SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE).getLong(
|
||||||
|
SERVICE_ENTRYPOINT_KEY,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateNotificationInfo() {
|
||||||
|
val mutable =
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) PendingIntent.FLAG_MUTABLE else 0
|
||||||
|
val pendingIntent = PendingIntent.getActivity(
|
||||||
|
this,
|
||||||
|
99778,
|
||||||
|
packageManager.getLaunchIntentForPackage(applicationContext.packageName),
|
||||||
|
PendingIntent.FLAG_CANCEL_CURRENT or mutable,
|
||||||
|
)
|
||||||
|
|
||||||
|
val notification = NotificationCompat.Builder(this, "foreground_service").apply {
|
||||||
|
setSmallIcon(R.drawable.ic_service)
|
||||||
|
setAutoCancel(false)
|
||||||
|
setOngoing(true)
|
||||||
|
setContentTitle(notificationTitle)
|
||||||
|
setContentText(notificationBody)
|
||||||
|
setContentIntent(pendingIntent)
|
||||||
|
}.build()
|
||||||
|
startForeground(99778, notification)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun runService() {
|
||||||
|
try {
|
||||||
|
if (isRunning.get() || (engine?.getDartExecutor()?.isExecutingDart() ?: false)) return
|
||||||
|
|
||||||
|
if (BackgroundServiceStatic.wakeLock == null) {
|
||||||
|
Log.d(TAG, "WakeLock is null. Acquiring and grabbing WakeLock...")
|
||||||
|
BackgroundServiceStatic.acquireWakeLock(applicationContext)
|
||||||
|
.acquire(SERVICE_WAKELOCK_DURATION)
|
||||||
|
Log.d(TAG, "WakeLock grabbed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the notification
|
||||||
|
updateNotificationInfo()
|
||||||
|
|
||||||
|
// Set-up the Flutter Engine, if it's not already set up
|
||||||
|
if (!FlutterInjector.instance().flutterLoader().initialized()) {
|
||||||
|
FlutterInjector.instance().flutterLoader().startInitialization(applicationContext)
|
||||||
|
}
|
||||||
|
FlutterInjector.instance().flutterLoader().ensureInitializationComplete(
|
||||||
|
applicationContext,
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
val callback: FlutterCallbackInformation =
|
||||||
|
FlutterCallbackInformation.lookupCallbackInformation(getHandle())
|
||||||
|
if (callback == null) {
|
||||||
|
Log.e(TAG, "Callback handle not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
isRunning.set(true)
|
||||||
|
engine = FlutterEngine(this)
|
||||||
|
engine!!.getServiceControlSurface().attachToService(this, null, true)
|
||||||
|
methodChannel = MethodChannel(
|
||||||
|
engine!!.getDartExecutor()!!.getBinaryMessenger(),
|
||||||
|
SERVICE_BACKGROUND_METHOD_CHANNEL_KEY,
|
||||||
|
)
|
||||||
|
|
||||||
|
MoxxyBackgroundServiceApi.setUp(engine!!.getDartExecutor()!!.getBinaryMessenger(), this)
|
||||||
|
Log.d(TAG, "MoxxyBackgroundServiceApi ready")
|
||||||
|
|
||||||
|
dartCallback = DartExecutor.DartCallback(
|
||||||
|
assets,
|
||||||
|
FlutterInjector.instance().flutterLoader().findAppBundlePath(),
|
||||||
|
callback,
|
||||||
|
)
|
||||||
|
engine!!.getDartExecutor().executeDartCallback(dartCallback!!)
|
||||||
|
} catch (ex: UnsatisfiedLinkError) {
|
||||||
|
Log.e(TAG, "Failed to set up background service: ${ex.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBind(intent: Intent): IBinder? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate() {
|
||||||
|
super.onCreate()
|
||||||
|
|
||||||
|
notificationBody = SERVICE_DEFAULT_BODY
|
||||||
|
updateNotificationInfo()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
if (!isManuallyStopped) {
|
||||||
|
BackgroundServiceStatic.enqueue(this)
|
||||||
|
} else {
|
||||||
|
setManuallyStopped(applicationContext, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dispose of the engine
|
||||||
|
engine?.apply {
|
||||||
|
getServiceControlSurface().detachFromService()
|
||||||
|
destroy()
|
||||||
|
}
|
||||||
|
engine = null
|
||||||
|
dartCallback = null
|
||||||
|
|
||||||
|
// Stop the service
|
||||||
|
stopForeground(true)
|
||||||
|
isRunning.set(false)
|
||||||
|
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun receiveData(data: String) {
|
||||||
|
methodChannel?.invokeMethod("dataReceived", data)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
|
||||||
|
setManuallyStopped(this, false)
|
||||||
|
BackgroundServiceStatic.enqueue(this)
|
||||||
|
runService()
|
||||||
|
|
||||||
|
return START_STICKY
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getHandler(): Long {
|
||||||
|
return getHandle()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getExtraData(): String {
|
||||||
|
return getSharedPreferences(SERVICE_SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE).getString(
|
||||||
|
SERVICE_EXTRA_DATA_KEY,
|
||||||
|
"",
|
||||||
|
)!!
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setNotificationBody(body: String) {
|
||||||
|
notificationBody = body
|
||||||
|
updateNotificationInfo()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun sendData(data: String) {
|
||||||
|
LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(
|
||||||
|
Intent(SERVICE_BACKGROUND_METHOD_CHANNEL_KEY).apply {
|
||||||
|
putExtra("data", data)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun stop() {
|
||||||
|
isManuallyStopped = true
|
||||||
|
val mutable =
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) PendingIntent.FLAG_MUTABLE else 0
|
||||||
|
val pendingIntent = PendingIntent.getBroadcast(
|
||||||
|
applicationContext,
|
||||||
|
111,
|
||||||
|
Intent(this, WatchdogReceiver::class.java),
|
||||||
|
PendingIntent.FLAG_CANCEL_CURRENT or mutable,
|
||||||
|
)
|
||||||
|
val stopManager = getSystemService(ALARM_SERVICE) as AlarmManager
|
||||||
|
stopManager.cancel(pendingIntent)
|
||||||
|
stopSelf()
|
||||||
|
BackgroundServiceStatic.setStartAtBoot(applicationContext, false)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package org.moxxy.moxxy_native.service
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import org.moxxy.moxxy_native.TAG
|
||||||
|
|
||||||
|
class BootReceiver : BroadcastReceiver() {
|
||||||
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
|
if (BackgroundServiceStatic.getStartAtBoot(context)) {
|
||||||
|
if (BackgroundServiceStatic.wakeLock == null) {
|
||||||
|
Log.d(TAG, "WakeLock is null. Acquiring it...")
|
||||||
|
BackgroundServiceStatic.acquireWakeLock(context)
|
||||||
|
Log.d(TAG, "WakeLock acquired")
|
||||||
|
}
|
||||||
|
|
||||||
|
ContextCompat.startForegroundService(
|
||||||
|
context,
|
||||||
|
Intent(context, BackgroundService::class.java),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,134 @@
|
|||||||
|
// Autogenerated from Pigeon (v11.0.1), do not edit directly.
|
||||||
|
// See also: https://pub.dev/packages/pigeon
|
||||||
|
|
||||||
|
package org.moxxy.moxxy_native.service
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import io.flutter.plugin.common.BasicMessageChannel
|
||||||
|
import io.flutter.plugin.common.BinaryMessenger
|
||||||
|
import io.flutter.plugin.common.MessageCodec
|
||||||
|
import io.flutter.plugin.common.StandardMessageCodec
|
||||||
|
|
||||||
|
private fun wrapResult(result: Any?): List<Any?> {
|
||||||
|
return listOf(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun wrapError(exception: Throwable): List<Any?> {
|
||||||
|
if (exception is FlutterError) {
|
||||||
|
return listOf(
|
||||||
|
exception.code,
|
||||||
|
exception.message,
|
||||||
|
exception.details,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return listOf(
|
||||||
|
exception.javaClass.simpleName,
|
||||||
|
exception.toString(),
|
||||||
|
"Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error class for passing custom error details to Flutter via a thrown PlatformException.
|
||||||
|
* @property code The error code.
|
||||||
|
* @property message The error message.
|
||||||
|
* @property details The error details. Must be a datatype supported by the api codec.
|
||||||
|
*/
|
||||||
|
class FlutterError(
|
||||||
|
val code: String,
|
||||||
|
override val message: String? = null,
|
||||||
|
val details: Any? = null,
|
||||||
|
) : Throwable()
|
||||||
|
|
||||||
|
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
|
||||||
|
interface MoxxyServiceApi {
|
||||||
|
fun configure(handle: Long, extraData: String)
|
||||||
|
fun isRunning(): Boolean
|
||||||
|
fun start()
|
||||||
|
fun sendData(data: String)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/** The codec used by MoxxyServiceApi. */
|
||||||
|
val codec: MessageCodec<Any?> by lazy {
|
||||||
|
StandardMessageCodec()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Sets up an instance of `MoxxyServiceApi` to handle messages through the `binaryMessenger`. */
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
fun setUp(binaryMessenger: BinaryMessenger, api: MoxxyServiceApi?) {
|
||||||
|
run {
|
||||||
|
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.moxxy_native.MoxxyServiceApi.configure", codec)
|
||||||
|
if (api != null) {
|
||||||
|
channel.setMessageHandler { message, reply ->
|
||||||
|
val args = message as List<Any?>
|
||||||
|
val handleArg = args[0].let { if (it is Int) it.toLong() else it as Long }
|
||||||
|
val extraDataArg = args[1] as String
|
||||||
|
var wrapped: List<Any?>
|
||||||
|
try {
|
||||||
|
api.configure(handleArg, extraDataArg)
|
||||||
|
wrapped = listOf<Any?>(null)
|
||||||
|
} catch (exception: Throwable) {
|
||||||
|
wrapped = wrapError(exception)
|
||||||
|
}
|
||||||
|
reply.reply(wrapped)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
channel.setMessageHandler(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
run {
|
||||||
|
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.moxxy_native.MoxxyServiceApi.isRunning", codec)
|
||||||
|
if (api != null) {
|
||||||
|
channel.setMessageHandler { _, reply ->
|
||||||
|
var wrapped: List<Any?>
|
||||||
|
try {
|
||||||
|
wrapped = listOf<Any?>(api.isRunning())
|
||||||
|
} catch (exception: Throwable) {
|
||||||
|
wrapped = wrapError(exception)
|
||||||
|
}
|
||||||
|
reply.reply(wrapped)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
channel.setMessageHandler(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
run {
|
||||||
|
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.moxxy_native.MoxxyServiceApi.start", codec)
|
||||||
|
if (api != null) {
|
||||||
|
channel.setMessageHandler { _, reply ->
|
||||||
|
var wrapped: List<Any?>
|
||||||
|
try {
|
||||||
|
api.start()
|
||||||
|
wrapped = listOf<Any?>(null)
|
||||||
|
} catch (exception: Throwable) {
|
||||||
|
wrapped = wrapError(exception)
|
||||||
|
}
|
||||||
|
reply.reply(wrapped)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
channel.setMessageHandler(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
run {
|
||||||
|
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.moxxy_native.MoxxyServiceApi.sendData", codec)
|
||||||
|
if (api != null) {
|
||||||
|
channel.setMessageHandler { message, reply ->
|
||||||
|
val args = message as List<Any?>
|
||||||
|
val dataArg = args[0] as String
|
||||||
|
var wrapped: List<Any?>
|
||||||
|
try {
|
||||||
|
api.sendData(dataArg)
|
||||||
|
wrapped = listOf<Any?>(null)
|
||||||
|
} catch (exception: Throwable) {
|
||||||
|
wrapped = wrapError(exception)
|
||||||
|
}
|
||||||
|
reply.reply(wrapped)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
channel.setMessageHandler(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
package org.moxxy.moxxy_native.service
|
||||||
|
|
||||||
|
import android.app.ActivityManager
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import org.moxxy.moxxy_native.MoxxyNativePlugin
|
||||||
|
import org.moxxy.moxxy_native.SERVICE_ENTRYPOINT_KEY
|
||||||
|
import org.moxxy.moxxy_native.SERVICE_EXTRA_DATA_KEY
|
||||||
|
import org.moxxy.moxxy_native.SERVICE_SHARED_PREFERENCES_KEY
|
||||||
|
import org.moxxy.moxxy_native.TAG
|
||||||
|
import org.moxxy.moxxy_native.service.BackgroundServiceStatic.setStartAtBoot
|
||||||
|
|
||||||
|
object PluginTracker {
|
||||||
|
var instances: MutableList<MoxxyNativePlugin> = mutableListOf()
|
||||||
|
}
|
||||||
|
|
||||||
|
class ServiceImplementation(private val context: Context) : MoxxyServiceApi {
|
||||||
|
override fun configure(handle: Long, extraData: String) {
|
||||||
|
context.getSharedPreferences(SERVICE_SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE).edit()
|
||||||
|
.putLong(SERVICE_ENTRYPOINT_KEY, handle)
|
||||||
|
.putString(SERVICE_EXTRA_DATA_KEY, extraData)
|
||||||
|
.apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isRunning(): Boolean {
|
||||||
|
val manager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
|
||||||
|
for (info in manager.getRunningServices(Int.MAX_VALUE)) {
|
||||||
|
if (BackgroundService::class.java.name == info.service.className) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun start() {
|
||||||
|
setStartAtBoot(context, true)
|
||||||
|
BackgroundServiceStatic.enqueue(context)
|
||||||
|
ContextCompat.startForegroundService(
|
||||||
|
context,
|
||||||
|
Intent(context, BackgroundService::class.java),
|
||||||
|
)
|
||||||
|
Log.d(TAG, "Background service started")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun sendData(data: String) {
|
||||||
|
for (plugin in PluginTracker.instances) {
|
||||||
|
val service = plugin.service
|
||||||
|
if (service != null) {
|
||||||
|
service.receiveData(data)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package org.moxxy.moxxy_native.service
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import org.moxxy.moxxy_native.service.BackgroundServiceStatic.getManuallyStopped
|
||||||
|
|
||||||
|
class WatchdogReceiver : BroadcastReceiver() {
|
||||||
|
override fun onReceive(context: Context, intent: Intent?) {
|
||||||
|
if (!getManuallyStopped(context)) {
|
||||||
|
ContextCompat.startForegroundService(
|
||||||
|
context,
|
||||||
|
Intent(context, BackgroundService::class.java),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,150 @@
|
|||||||
|
// Autogenerated from Pigeon (v11.0.1), do not edit directly.
|
||||||
|
// See also: https://pub.dev/packages/pigeon
|
||||||
|
|
||||||
|
package org.moxxy.moxxy_native.service.background
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import io.flutter.plugin.common.BasicMessageChannel
|
||||||
|
import io.flutter.plugin.common.BinaryMessenger
|
||||||
|
import io.flutter.plugin.common.MessageCodec
|
||||||
|
import io.flutter.plugin.common.StandardMessageCodec
|
||||||
|
|
||||||
|
private fun wrapResult(result: Any?): List<Any?> {
|
||||||
|
return listOf(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun wrapError(exception: Throwable): List<Any?> {
|
||||||
|
if (exception is FlutterError) {
|
||||||
|
return listOf(
|
||||||
|
exception.code,
|
||||||
|
exception.message,
|
||||||
|
exception.details,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return listOf(
|
||||||
|
exception.javaClass.simpleName,
|
||||||
|
exception.toString(),
|
||||||
|
"Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error class for passing custom error details to Flutter via a thrown PlatformException.
|
||||||
|
* @property code The error code.
|
||||||
|
* @property message The error message.
|
||||||
|
* @property details The error details. Must be a datatype supported by the api codec.
|
||||||
|
*/
|
||||||
|
class FlutterError(
|
||||||
|
val code: String,
|
||||||
|
override val message: String? = null,
|
||||||
|
val details: Any? = null,
|
||||||
|
) : Throwable()
|
||||||
|
|
||||||
|
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
|
||||||
|
interface MoxxyBackgroundServiceApi {
|
||||||
|
fun getHandler(): Long
|
||||||
|
fun getExtraData(): String
|
||||||
|
fun setNotificationBody(body: String)
|
||||||
|
fun sendData(data: String)
|
||||||
|
fun stop()
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/** The codec used by MoxxyBackgroundServiceApi. */
|
||||||
|
val codec: MessageCodec<Any?> by lazy {
|
||||||
|
StandardMessageCodec()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Sets up an instance of `MoxxyBackgroundServiceApi` to handle messages through the `binaryMessenger`. */
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
fun setUp(binaryMessenger: BinaryMessenger, api: MoxxyBackgroundServiceApi?) {
|
||||||
|
run {
|
||||||
|
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.moxxy_native.MoxxyBackgroundServiceApi.getHandler", codec)
|
||||||
|
if (api != null) {
|
||||||
|
channel.setMessageHandler { _, reply ->
|
||||||
|
var wrapped: List<Any?>
|
||||||
|
try {
|
||||||
|
wrapped = listOf<Any?>(api.getHandler())
|
||||||
|
} catch (exception: Throwable) {
|
||||||
|
wrapped = wrapError(exception)
|
||||||
|
}
|
||||||
|
reply.reply(wrapped)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
channel.setMessageHandler(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
run {
|
||||||
|
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.moxxy_native.MoxxyBackgroundServiceApi.getExtraData", codec)
|
||||||
|
if (api != null) {
|
||||||
|
channel.setMessageHandler { _, reply ->
|
||||||
|
var wrapped: List<Any?>
|
||||||
|
try {
|
||||||
|
wrapped = listOf<Any?>(api.getExtraData())
|
||||||
|
} catch (exception: Throwable) {
|
||||||
|
wrapped = wrapError(exception)
|
||||||
|
}
|
||||||
|
reply.reply(wrapped)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
channel.setMessageHandler(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
run {
|
||||||
|
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.moxxy_native.MoxxyBackgroundServiceApi.setNotificationBody", codec)
|
||||||
|
if (api != null) {
|
||||||
|
channel.setMessageHandler { message, reply ->
|
||||||
|
val args = message as List<Any?>
|
||||||
|
val bodyArg = args[0] as String
|
||||||
|
var wrapped: List<Any?>
|
||||||
|
try {
|
||||||
|
api.setNotificationBody(bodyArg)
|
||||||
|
wrapped = listOf<Any?>(null)
|
||||||
|
} catch (exception: Throwable) {
|
||||||
|
wrapped = wrapError(exception)
|
||||||
|
}
|
||||||
|
reply.reply(wrapped)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
channel.setMessageHandler(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
run {
|
||||||
|
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.moxxy_native.MoxxyBackgroundServiceApi.sendData", codec)
|
||||||
|
if (api != null) {
|
||||||
|
channel.setMessageHandler { message, reply ->
|
||||||
|
val args = message as List<Any?>
|
||||||
|
val dataArg = args[0] as String
|
||||||
|
var wrapped: List<Any?>
|
||||||
|
try {
|
||||||
|
api.sendData(dataArg)
|
||||||
|
wrapped = listOf<Any?>(null)
|
||||||
|
} catch (exception: Throwable) {
|
||||||
|
wrapped = wrapError(exception)
|
||||||
|
}
|
||||||
|
reply.reply(wrapped)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
channel.setMessageHandler(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
run {
|
||||||
|
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.moxxy_native.MoxxyBackgroundServiceApi.stop", codec)
|
||||||
|
if (api != null) {
|
||||||
|
channel.setMessageHandler { _, reply ->
|
||||||
|
var wrapped: List<Any?>
|
||||||
|
try {
|
||||||
|
api.stop()
|
||||||
|
wrapped = listOf<Any?>(null)
|
||||||
|
} catch (exception: Throwable) {
|
||||||
|
wrapped = wrapError(exception)
|
||||||
|
}
|
||||||
|
reply.reply(wrapped)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
channel.setMessageHandler(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,18 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
import 'dart:ui';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:moxxy_native/moxxy_native.dart';
|
import 'package:moxxy_native/moxxy_native.dart';
|
||||||
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
|
|
||||||
|
@pragma('vm:entry-point')
|
||||||
|
Future<void> entrypoint() async {
|
||||||
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
|
print('CALLED FROM NEW FLUTTERENGINE');
|
||||||
|
final extra = await MoxxyBackgroundServiceApi().getExtraData();
|
||||||
|
print('EXTRA DATA: $extra');
|
||||||
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
runApp(const MyApp());
|
runApp(const MyApp());
|
||||||
@ -96,6 +107,32 @@ class MyAppState extends State<MyApp> {
|
|||||||
child: const Text('Test cryptography'),
|
child: const Text('Test cryptography'),
|
||||||
),
|
),
|
||||||
if (imagePath != null) Image.file(File(imagePath!)),
|
if (imagePath != null) Image.file(File(imagePath!)),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
// Create channel
|
||||||
|
await MoxxyNotificationsApi().createNotificationChannels(
|
||||||
|
[
|
||||||
|
NotificationChannel(
|
||||||
|
id: 'foreground_service',
|
||||||
|
title: 'Foreground service',
|
||||||
|
description: 'lol',
|
||||||
|
importance: NotificationChannelImportance.MIN,
|
||||||
|
showBadge: false,
|
||||||
|
vibration: false,
|
||||||
|
enableLights: false,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
await Permission.notification.request();
|
||||||
|
|
||||||
|
final handle = PluginUtilities.getCallbackHandle(entrypoint)!
|
||||||
|
.toRawHandle();
|
||||||
|
final api = MoxxyServiceApi();
|
||||||
|
await api.configure(handle, 'lol');
|
||||||
|
await api.start();
|
||||||
|
},
|
||||||
|
child: const Text('Start foreground service')),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -130,6 +130,54 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.2"
|
version: "1.8.2"
|
||||||
|
permission_handler:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: permission_handler
|
||||||
|
sha256: bc56bfe9d3f44c3c612d8d393bd9b174eb796d706759f9b495ac254e4294baa5
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "10.4.5"
|
||||||
|
permission_handler_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: permission_handler_android
|
||||||
|
sha256: "59c6322171c29df93a22d150ad95f3aa19ed86542eaec409ab2691b8f35f9a47"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "10.3.6"
|
||||||
|
permission_handler_apple:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: permission_handler_apple
|
||||||
|
sha256: "99e220bce3f8877c78e4ace901082fb29fa1b4ebde529ad0932d8d664b34f3f5"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "9.1.4"
|
||||||
|
permission_handler_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: permission_handler_platform_interface
|
||||||
|
sha256: f2343e9fa9c22ae4fd92d4732755bfe452214e7189afcc097380950cf567b4b2
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.11.5"
|
||||||
|
permission_handler_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: permission_handler_windows
|
||||||
|
sha256: cc074aace208760f1eee6aa4fae766b45d947df85bc831cde77009cdb4720098
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.3"
|
||||||
|
plugin_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: plugin_platform_interface
|
||||||
|
sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.6"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
@ -193,4 +241,4 @@ packages:
|
|||||||
version: "2.1.4"
|
version: "2.1.4"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.19.6 <3.0.0"
|
dart: ">=2.19.6 <3.0.0"
|
||||||
flutter: ">=2.5.0"
|
flutter: ">=2.8.0"
|
||||||
|
@ -28,6 +28,7 @@ dependencies:
|
|||||||
# The following adds the Cupertino Icons font to your application.
|
# The following adds the Cupertino Icons font to your application.
|
||||||
# Use with the CupertinoIcons class for iOS style icons.
|
# Use with the CupertinoIcons class for iOS style icons.
|
||||||
cupertino_icons: ^1.0.2
|
cupertino_icons: ^1.0.2
|
||||||
|
permission_handler: ^10.4.5
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
14
flake.nix
14
flake.nix
@ -66,5 +66,19 @@
|
|||||||
# an used parameter.
|
# an used parameter.
|
||||||
GRADLE_OPTS = "-Dorg.gradle.project.android.aapt2FromMavenOverride=${sdk}/share/android-sdk/build-tools/34.0.0/aapt2";
|
GRADLE_OPTS = "-Dorg.gradle.project.android.aapt2FromMavenOverride=${sdk}/share/android-sdk/build-tools/34.0.0/aapt2";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
apps = {
|
||||||
|
androidLint = let
|
||||||
|
script = pkgs.writeShellScript "lint-android.sh" ''
|
||||||
|
${pkgs.ktlint}/bin/ktlint \
|
||||||
|
--format \
|
||||||
|
--disabled_rules=standard:package-name \
|
||||||
|
android/src/main/kotlin/org/moxxy/moxxy_native/
|
||||||
|
'';
|
||||||
|
in {
|
||||||
|
program = "${script}";
|
||||||
|
type = "app";
|
||||||
|
};
|
||||||
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
export 'pigeon/background_service.g.dart';
|
||||||
export 'pigeon/contacts.g.dart';
|
export 'pigeon/contacts.g.dart';
|
||||||
export 'pigeon/cryptography.g.dart';
|
export 'pigeon/cryptography.g.dart';
|
||||||
export 'pigeon/media.g.dart';
|
export 'pigeon/media.g.dart';
|
||||||
export 'pigeon/notifications.g.dart';
|
export 'pigeon/notifications.g.dart';
|
||||||
export 'pigeon/picker.g.dart';
|
export 'pigeon/picker.g.dart';
|
||||||
export 'pigeon/platform.g.dart';
|
export 'pigeon/platform.g.dart';
|
||||||
|
export 'pigeon/service.g.dart';
|
||||||
|
140
lib/pigeon/background_service.g.dart
Normal file
140
lib/pigeon/background_service.g.dart
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
// Autogenerated from Pigeon (v11.0.1), do not edit directly.
|
||||||
|
// See also: https://pub.dev/packages/pigeon
|
||||||
|
// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List;
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer;
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
class MoxxyBackgroundServiceApi {
|
||||||
|
/// Constructor for [MoxxyBackgroundServiceApi]. The [binaryMessenger] named argument is
|
||||||
|
/// available for dependency injection. If it is left null, the default
|
||||||
|
/// BinaryMessenger will be used which routes to the host platform.
|
||||||
|
MoxxyBackgroundServiceApi({BinaryMessenger? binaryMessenger})
|
||||||
|
: _binaryMessenger = binaryMessenger;
|
||||||
|
final BinaryMessenger? _binaryMessenger;
|
||||||
|
|
||||||
|
static const MessageCodec<Object?> codec = StandardMessageCodec();
|
||||||
|
|
||||||
|
Future<int> getHandler() async {
|
||||||
|
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||||
|
'dev.flutter.pigeon.moxxy_native.MoxxyBackgroundServiceApi.getHandler', codec,
|
||||||
|
binaryMessenger: _binaryMessenger);
|
||||||
|
final List<Object?>? replyList =
|
||||||
|
await channel.send(null) as List<Object?>?;
|
||||||
|
if (replyList == null) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'channel-error',
|
||||||
|
message: 'Unable to establish connection on channel.',
|
||||||
|
);
|
||||||
|
} else if (replyList.length > 1) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: replyList[0]! as String,
|
||||||
|
message: replyList[1] as String?,
|
||||||
|
details: replyList[2],
|
||||||
|
);
|
||||||
|
} else if (replyList[0] == null) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'null-error',
|
||||||
|
message: 'Host platform returned null value for non-null return value.',
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (replyList[0] as int?)!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> getExtraData() async {
|
||||||
|
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||||
|
'dev.flutter.pigeon.moxxy_native.MoxxyBackgroundServiceApi.getExtraData', codec,
|
||||||
|
binaryMessenger: _binaryMessenger);
|
||||||
|
final List<Object?>? replyList =
|
||||||
|
await channel.send(null) as List<Object?>?;
|
||||||
|
if (replyList == null) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'channel-error',
|
||||||
|
message: 'Unable to establish connection on channel.',
|
||||||
|
);
|
||||||
|
} else if (replyList.length > 1) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: replyList[0]! as String,
|
||||||
|
message: replyList[1] as String?,
|
||||||
|
details: replyList[2],
|
||||||
|
);
|
||||||
|
} else if (replyList[0] == null) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'null-error',
|
||||||
|
message: 'Host platform returned null value for non-null return value.',
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (replyList[0] as String?)!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> setNotificationBody(String arg_body) async {
|
||||||
|
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||||
|
'dev.flutter.pigeon.moxxy_native.MoxxyBackgroundServiceApi.setNotificationBody', codec,
|
||||||
|
binaryMessenger: _binaryMessenger);
|
||||||
|
final List<Object?>? replyList =
|
||||||
|
await channel.send(<Object?>[arg_body]) as List<Object?>?;
|
||||||
|
if (replyList == null) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'channel-error',
|
||||||
|
message: 'Unable to establish connection on channel.',
|
||||||
|
);
|
||||||
|
} else if (replyList.length > 1) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: replyList[0]! as String,
|
||||||
|
message: replyList[1] as String?,
|
||||||
|
details: replyList[2],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> sendData(String arg_data) async {
|
||||||
|
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||||
|
'dev.flutter.pigeon.moxxy_native.MoxxyBackgroundServiceApi.sendData', codec,
|
||||||
|
binaryMessenger: _binaryMessenger);
|
||||||
|
final List<Object?>? replyList =
|
||||||
|
await channel.send(<Object?>[arg_data]) as List<Object?>?;
|
||||||
|
if (replyList == null) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'channel-error',
|
||||||
|
message: 'Unable to establish connection on channel.',
|
||||||
|
);
|
||||||
|
} else if (replyList.length > 1) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: replyList[0]! as String,
|
||||||
|
message: replyList[1] as String?,
|
||||||
|
details: replyList[2],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> stop() async {
|
||||||
|
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||||
|
'dev.flutter.pigeon.moxxy_native.MoxxyBackgroundServiceApi.stop', codec,
|
||||||
|
binaryMessenger: _binaryMessenger);
|
||||||
|
final List<Object?>? replyList =
|
||||||
|
await channel.send(null) as List<Object?>?;
|
||||||
|
if (replyList == null) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'channel-error',
|
||||||
|
message: 'Unable to establish connection on channel.',
|
||||||
|
);
|
||||||
|
} else if (replyList.length > 1) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: replyList[0]! as String,
|
||||||
|
message: replyList[1] as String?,
|
||||||
|
details: replyList[2],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
113
lib/pigeon/service.g.dart
Normal file
113
lib/pigeon/service.g.dart
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
// Autogenerated from Pigeon (v11.0.1), do not edit directly.
|
||||||
|
// See also: https://pub.dev/packages/pigeon
|
||||||
|
// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List;
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer;
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
class MoxxyServiceApi {
|
||||||
|
/// Constructor for [MoxxyServiceApi]. The [binaryMessenger] named argument is
|
||||||
|
/// available for dependency injection. If it is left null, the default
|
||||||
|
/// BinaryMessenger will be used which routes to the host platform.
|
||||||
|
MoxxyServiceApi({BinaryMessenger? binaryMessenger})
|
||||||
|
: _binaryMessenger = binaryMessenger;
|
||||||
|
final BinaryMessenger? _binaryMessenger;
|
||||||
|
|
||||||
|
static const MessageCodec<Object?> codec = StandardMessageCodec();
|
||||||
|
|
||||||
|
Future<void> configure(int arg_handle, String arg_extraData) async {
|
||||||
|
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||||
|
'dev.flutter.pigeon.moxxy_native.MoxxyServiceApi.configure', codec,
|
||||||
|
binaryMessenger: _binaryMessenger);
|
||||||
|
final List<Object?>? replyList =
|
||||||
|
await channel.send(<Object?>[arg_handle, arg_extraData]) as List<Object?>?;
|
||||||
|
if (replyList == null) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'channel-error',
|
||||||
|
message: 'Unable to establish connection on channel.',
|
||||||
|
);
|
||||||
|
} else if (replyList.length > 1) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: replyList[0]! as String,
|
||||||
|
message: replyList[1] as String?,
|
||||||
|
details: replyList[2],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> isRunning() async {
|
||||||
|
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||||
|
'dev.flutter.pigeon.moxxy_native.MoxxyServiceApi.isRunning', codec,
|
||||||
|
binaryMessenger: _binaryMessenger);
|
||||||
|
final List<Object?>? replyList =
|
||||||
|
await channel.send(null) as List<Object?>?;
|
||||||
|
if (replyList == null) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'channel-error',
|
||||||
|
message: 'Unable to establish connection on channel.',
|
||||||
|
);
|
||||||
|
} else if (replyList.length > 1) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: replyList[0]! as String,
|
||||||
|
message: replyList[1] as String?,
|
||||||
|
details: replyList[2],
|
||||||
|
);
|
||||||
|
} else if (replyList[0] == null) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'null-error',
|
||||||
|
message: 'Host platform returned null value for non-null return value.',
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (replyList[0] as bool?)!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> start() async {
|
||||||
|
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||||
|
'dev.flutter.pigeon.moxxy_native.MoxxyServiceApi.start', codec,
|
||||||
|
binaryMessenger: _binaryMessenger);
|
||||||
|
final List<Object?>? replyList =
|
||||||
|
await channel.send(null) as List<Object?>?;
|
||||||
|
if (replyList == null) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'channel-error',
|
||||||
|
message: 'Unable to establish connection on channel.',
|
||||||
|
);
|
||||||
|
} else if (replyList.length > 1) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: replyList[0]! as String,
|
||||||
|
message: replyList[1] as String?,
|
||||||
|
details: replyList[2],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> sendData(String arg_data) async {
|
||||||
|
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||||
|
'dev.flutter.pigeon.moxxy_native.MoxxyServiceApi.sendData', codec,
|
||||||
|
binaryMessenger: _binaryMessenger);
|
||||||
|
final List<Object?>? replyList =
|
||||||
|
await channel.send(<Object?>[arg_data]) as List<Object?>?;
|
||||||
|
if (replyList == null) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'channel-error',
|
||||||
|
message: 'Unable to establish connection on channel.',
|
||||||
|
);
|
||||||
|
} else if (replyList.length > 1) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: replyList[0]! as String,
|
||||||
|
message: replyList[1] as String?,
|
||||||
|
details: replyList[2],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
25
pigeon/background_service.dart
Normal file
25
pigeon/background_service.dart
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import 'package:pigeon/pigeon.dart';
|
||||||
|
|
||||||
|
@ConfigurePigeon(
|
||||||
|
PigeonOptions(
|
||||||
|
dartOut: 'lib/pigeon/background_service.g.dart',
|
||||||
|
kotlinOut:
|
||||||
|
'android/src/main/kotlin/org/moxxy/moxxy_native/service/background/BackgroundServiceApi.kt',
|
||||||
|
kotlinOptions: KotlinOptions(
|
||||||
|
package: 'org.moxxy.moxxy_native.service.background',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
@HostApi()
|
||||||
|
abstract class MoxxyBackgroundServiceApi {
|
||||||
|
int getHandler();
|
||||||
|
|
||||||
|
String getExtraData();
|
||||||
|
|
||||||
|
void setNotificationBody(String body);
|
||||||
|
|
||||||
|
void sendData(String data);
|
||||||
|
|
||||||
|
void stop();
|
||||||
|
}
|
23
pigeon/service.dart
Normal file
23
pigeon/service.dart
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import 'package:pigeon/pigeon.dart';
|
||||||
|
|
||||||
|
@ConfigurePigeon(
|
||||||
|
PigeonOptions(
|
||||||
|
dartOut: 'lib/pigeon/service.g.dart',
|
||||||
|
kotlinOut:
|
||||||
|
'android/src/main/kotlin/org/moxxy/moxxy_native/service/ServiceApi.kt',
|
||||||
|
kotlinOptions: KotlinOptions(
|
||||||
|
package: 'org.moxxy.moxxy_native.service',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
@HostApi()
|
||||||
|
abstract class MoxxyServiceApi {
|
||||||
|
void configure(int handle, String extraData);
|
||||||
|
|
||||||
|
bool isRunning();
|
||||||
|
|
||||||
|
void start();
|
||||||
|
|
||||||
|
void sendData(String data);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user