diff --git a/.gitignore b/.gitignore index ddde1066..0747de72 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,11 @@ .idea/ .packages .pub/ +.build/ +.swiftpm/ /pubspec.lock build/ + +# Arquivos gerados pelo Flutter no example (versionados por engano) +example/ios/Flutter/ephemeral/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d8c4aa8..d1740c86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,43 @@ +## 11.0.3 + +* [**FIX-iOS**] Include the objective-c shim in the podspec source_files so it is compiled under CocoaPods + +## 11.0.2 + +* [**FIX-iOS**] Add objective-c shim for FlutterForegroundTaskPlugin + +## 11.0.1 + +* [**CHORE**] Upgrade shared_preferences from 2.5.3 to 2.5.5 +* [**DOCS**] Update README + +## 11.0.0 + +* [**CHORE**] Bump minimum supported SDK version to `Flutter 3.44/Dart 3.12` +* [**FEAT**] Migrate to built-in Kotlin (KGP) [#385](https://github.com/Dev-hwang/flutter_foreground_task/pull/385) +* [**FEAT**] Support Swift Package Manager (SPM) [#387](https://github.com/Dev-hwang/flutter_foreground_task/pull/387) +* Check [migration_documentation](./documentation/migration_documentation.md) for changes + +## 10.0.0 + +* [**CHORE**] Bump minimum supported SDK version to `Flutter 3.38.0` +* [**FEAT**] Add UIScene life cycle support for iOS [#378](https://github.com/Dev-hwang/flutter_foreground_task/pull/378) + +## 9.2.2 + +* [**FIX**] Resolved frequent ForegroundServiceDidNotStartInTime and ForegroundServiceDidNotStopInTime exceptions by avoiding redundant foreground service start contracts. [#377](https://github.com/Dev-hwang/flutter_foreground_task/pull/377) + +## 9.2.1 + +* [**FIX**] Prevent crash in onDestroy [#372](https://github.com/Dev-hwang/flutter_foreground_task/pull/372) + +## 9.2.0 + +* [**FEAT**] Add `allowAutoRestart` option, which enables the service to automatically restart if it is terminated by the system. +* [**FEAT**] Add `stopWithTask` option, to ensure service termination when the app UI is dismissed. +* [**FEAT**] Add `mediaProcessing` foreground service type. +* [**FIX**] Fix `FLAG_STOP_WITH_TASK` bitmask check error. + ## 9.1.0 * [**FEAT**] Support manual foregroundServiceType via serviceTypes in startService diff --git a/README.md b/README.md index 90c89a7b..1fe4f643 100644 --- a/README.md +++ b/README.md @@ -16,10 +16,10 @@ This plugin is used to implement a foreground service on the Android platform. ## Support version -- Flutter: `3.22.0+` -- Dart: `3.4.0+` +- Flutter: `3.44.0+` +- Dart: `3.12.0+` - Android: `5.0+ (minSdkVersion: 21)` -- iOS: `12.0+` +- iOS: `13.0+` ## Getting started @@ -27,14 +27,14 @@ To use this plugin, add `flutter_foreground_task` as a [dependency in your pubsp ```yaml dependencies: - flutter_foreground_task: ^9.1.0 + flutter_foreground_task: ^11.0.3 ``` After adding the plugin to your flutter project, we need to declare the platform-specific permissions ans service to use for this plugin to work properly. ### :baby_chick: Android -This plugin requires `Kotlin version 1.9.10+` and `Gradle version 8.6.0+`. Please refer to the migration documentation for more details. +This plugin requires `Kotlin version 2.2.20+` and `Gradle version 8.11.1+`. Please refer to the migration documentation for more details. - [project/settings.gradle](https://github.com/Dev-hwang/flutter_foreground_task/blob/master/example/android/settings.gradle) - [project/gradle-wrapper.properties](https://github.com/Dev-hwang/flutter_foreground_task/blob/master/example/android/gradle/wrapper/gradle-wrapper.properties) @@ -127,9 +127,7 @@ Open the `ios/Runner/AppDelegate.swift` file and add the commented code. ```objc #import "AppDelegate.h" #import "GeneratedPluginRegistrant.h" - -// this -#import +@import flutter_foreground_task; // this (module import) // this void registerPlugins(NSObject* registry) { @@ -156,17 +154,12 @@ void registerPlugins(NSObject* registry) { **Swift**: -Declare the import statement below in the `ios/Runner/Runner-Bridging-Header.h` file. - -```objc -#import -``` - Open the `ios/Runner/AppDelegate.swift` file and add the commented code. ```swift import UIKit import Flutter +import flutter_foreground_task // this (module import) @main @objc class AppDelegate: FlutterAppDelegate { diff --git a/android/build.gradle b/android/build.gradle index ccc66918..6d692ec2 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -2,7 +2,6 @@ group 'com.pravera.flutter_foreground_task' version '1.0-SNAPSHOT' buildscript { - ext.kotlin_version = '1.9.10' repositories { google() mavenCentral() @@ -10,7 +9,6 @@ buildscript { dependencies { classpath 'com.android.tools.build:gradle:8.6.0' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } @@ -22,7 +20,6 @@ rootProject.allprojects { } apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' android { // Conditional for compatibility with AGP <4.2. @@ -37,10 +34,6 @@ android { targetCompatibility JavaVersion.VERSION_11 } - kotlinOptions { - jvmTarget = JavaVersion.VERSION_11 - } - sourceSets { main.java.srcDirs += 'src/main/kotlin' test.java.srcDirs += 'src/test/kotlin' @@ -71,3 +64,9 @@ android { } } } + +kotlin { + compilerOptions { + jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11 + } +} diff --git a/android/src/main/kotlin/com/pravera/flutter_foreground_task/PreferencesKey.kt b/android/src/main/kotlin/com/pravera/flutter_foreground_task/PreferencesKey.kt index 555315e5..5b96f10e 100644 --- a/android/src/main/kotlin/com/pravera/flutter_foreground_task/PreferencesKey.kt +++ b/android/src/main/kotlin/com/pravera/flutter_foreground_task/PreferencesKey.kt @@ -55,6 +55,7 @@ object PreferencesKey { const val ALLOW_WAKE_LOCK = "allowWakeLock" const val ALLOW_WIFI_LOCK = "allowWifiLock" const val ALLOW_AUTO_RESTART = "allowAutoRestart" + const val STOP_WITH_TASK = "stopWithTask" // task data const val CALLBACK_HANDLE = "callbackHandle" diff --git a/android/src/main/kotlin/com/pravera/flutter_foreground_task/models/ForegroundServiceTypes.kt b/android/src/main/kotlin/com/pravera/flutter_foreground_task/models/ForegroundServiceTypes.kt index 315bca08..bea66546 100644 --- a/android/src/main/kotlin/com/pravera/flutter_foreground_task/models/ForegroundServiceTypes.kt +++ b/android/src/main/kotlin/com/pravera/flutter_foreground_task/models/ForegroundServiceTypes.kt @@ -70,6 +70,7 @@ data class ForegroundServiceTypes(val value: Int) { 10 -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) ServiceInfo.FOREGROUND_SERVICE_TYPE_SHORT_SERVICE else null 11 -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE else null 12 -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) ServiceInfo.FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED else null + 13 -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROCESSING else null else -> null } } diff --git a/android/src/main/kotlin/com/pravera/flutter_foreground_task/models/ForegroundTaskOptions.kt b/android/src/main/kotlin/com/pravera/flutter_foreground_task/models/ForegroundTaskOptions.kt index b1f1515f..cdfb3627 100644 --- a/android/src/main/kotlin/com/pravera/flutter_foreground_task/models/ForegroundTaskOptions.kt +++ b/android/src/main/kotlin/com/pravera/flutter_foreground_task/models/ForegroundTaskOptions.kt @@ -10,7 +10,8 @@ data class ForegroundTaskOptions( val autoRunOnMyPackageReplaced: Boolean, val allowWakeLock: Boolean, val allowWifiLock: Boolean, - val allowAutoRestart: Boolean + val allowAutoRestart: Boolean, + val stopWithTask: Boolean? ) { companion object { fun getData(context: Context): ForegroundTaskOptions { @@ -36,6 +37,11 @@ data class ForegroundTaskOptions( val allowWakeLock = prefs.getBoolean(PrefsKey.ALLOW_WAKE_LOCK, true) val allowWifiLock = prefs.getBoolean(PrefsKey.ALLOW_WIFI_LOCK, false) val allowAutoRestart = prefs.getBoolean(PrefsKey.ALLOW_AUTO_RESTART, false) + val stopWithTask: Boolean? = + if (prefs.contains(PrefsKey.STOP_WITH_TASK)) + prefs.getBoolean(PrefsKey.STOP_WITH_TASK, false) + else + null return ForegroundTaskOptions( eventAction = eventAction, @@ -44,6 +50,7 @@ data class ForegroundTaskOptions( allowWakeLock = allowWakeLock, allowWifiLock = allowWifiLock, allowAutoRestart = allowAutoRestart, + stopWithTask = stopWithTask, ) } @@ -62,6 +69,7 @@ data class ForegroundTaskOptions( val allowWakeLock = map?.get(PrefsKey.ALLOW_WAKE_LOCK) as? Boolean ?: true val allowWifiLock = map?.get(PrefsKey.ALLOW_WIFI_LOCK) as? Boolean ?: false val allowAutoRestart = map?.get(PrefsKey.ALLOW_AUTO_RESTART) as? Boolean ?: false + val stopWithTask = map?.get(PrefsKey.STOP_WITH_TASK) as? Boolean with(prefs.edit()) { putString(PrefsKey.TASK_EVENT_ACTION, eventActionJsonString) @@ -70,6 +78,7 @@ data class ForegroundTaskOptions( putBoolean(PrefsKey.ALLOW_WAKE_LOCK, allowWakeLock) putBoolean(PrefsKey.ALLOW_WIFI_LOCK, allowWifiLock) putBoolean(PrefsKey.ALLOW_AUTO_RESTART, allowAutoRestart) + stopWithTask?.let { putBoolean(PrefsKey.STOP_WITH_TASK, it) } ?: remove(PrefsKey.STOP_WITH_TASK) commit() } } @@ -89,6 +98,7 @@ data class ForegroundTaskOptions( val allowWakeLock = map?.get(PrefsKey.ALLOW_WAKE_LOCK) as? Boolean val allowWifiLock = map?.get(PrefsKey.ALLOW_WIFI_LOCK) as? Boolean val allowAutoRestart = map?.get(PrefsKey.ALLOW_AUTO_RESTART) as? Boolean + val stopWithTask = map?.get(PrefsKey.STOP_WITH_TASK) as? Boolean with(prefs.edit()) { eventActionJsonString?.let { putString(PrefsKey.TASK_EVENT_ACTION, it) } @@ -97,6 +107,7 @@ data class ForegroundTaskOptions( allowWakeLock?.let { putBoolean(PrefsKey.ALLOW_WAKE_LOCK, it) } allowWifiLock?.let { putBoolean(PrefsKey.ALLOW_WIFI_LOCK, it) } allowAutoRestart?.let { putBoolean(PrefsKey.ALLOW_AUTO_RESTART, it) } + stopWithTask?.let { putBoolean(PrefsKey.STOP_WITH_TASK, it) } ?: remove(PrefsKey.STOP_WITH_TASK) commit() } } @@ -111,4 +122,4 @@ data class ForegroundTaskOptions( } } } -} +} \ No newline at end of file diff --git a/android/src/main/kotlin/com/pravera/flutter_foreground_task/service/ForegroundService.kt b/android/src/main/kotlin/com/pravera/flutter_foreground_task/service/ForegroundService.kt index ab9f906d..cbe727f4 100644 --- a/android/src/main/kotlin/com/pravera/flutter_foreground_task/service/ForegroundService.kt +++ b/android/src/main/kotlin/com/pravera/flutter_foreground_task/service/ForegroundService.kt @@ -19,7 +19,8 @@ import androidx.core.content.ContextCompat import com.pravera.flutter_foreground_task.FlutterForegroundTaskLifecycleListener import com.pravera.flutter_foreground_task.RequestCode import com.pravera.flutter_foreground_task.models.* -import com.pravera.flutter_foreground_task.utils.ForegroundServiceUtils +import com.pravera.flutter_foreground_task.utils.* +import com.pravera.flutter_foreground_task.PreferencesKey as PrefsKey import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update @@ -132,6 +133,17 @@ class ForegroundService : Service() { isTimeout = false loadDataFromPreferences() + // Com stopWithTask ativo, o serviço é derrubado quando todas as activities pausam. + val prefs = getSharedPreferences(PrefsKey.FOREGROUND_TASK_OPTIONS_PREFS, Context.MODE_PRIVATE) + if (prefs.contains(PrefsKey.STOP_WITH_TASK) && prefs.getBoolean(PrefsKey.STOP_WITH_TASK, false)) { + (application as? Application)?.let { + TrackVisibilityUtils.install(it) { + // Passar true para preservar reattach de fabricante. + stopForegroundService(true) + } + } + } + var action = foregroundServiceStatus.action val isSetStopWithTaskFlag = ForegroundServiceUtils.isSetStopWithTaskFlag(this) diff --git a/android/src/main/kotlin/com/pravera/flutter_foreground_task/service/ForegroundServiceManager.kt b/android/src/main/kotlin/com/pravera/flutter_foreground_task/service/ForegroundServiceManager.kt index 18c03f80..a99712e4 100644 --- a/android/src/main/kotlin/com/pravera/flutter_foreground_task/service/ForegroundServiceManager.kt +++ b/android/src/main/kotlin/com/pravera/flutter_foreground_task/service/ForegroundServiceManager.kt @@ -62,7 +62,10 @@ class ForegroundServiceManager { ForegroundTaskOptions.updateData(context, argsMap) ForegroundTaskData.updateData(context, argsMap) NotificationContent.updateData(context, argsMap) - ContextCompat.startForegroundService(context, nIntent) + // Use startService instead of startForegroundService because the service + // is already running in the foreground and we only need to deliver the + // update command. This avoids the startForeground() contract requirement. + context.startService(nIntent) } /** Stop the foreground service. */ @@ -78,7 +81,10 @@ class ForegroundServiceManager { ForegroundTaskOptions.clearData(context) ForegroundTaskData.clearData(context) NotificationContent.clearData(context) - ContextCompat.startForegroundService(context, nIntent) + // Use startService instead of startForegroundService because the service + // is already running in the foreground and we only need to deliver the + // stop command. This avoids the startForeground() contract requirement. + context.startService(nIntent) } /** Send data to TaskHandler. */ diff --git a/android/src/main/kotlin/com/pravera/flutter_foreground_task/service/RestartReceiver.kt b/android/src/main/kotlin/com/pravera/flutter_foreground_task/service/RestartReceiver.kt index 76941530..f465a85d 100644 --- a/android/src/main/kotlin/com/pravera/flutter_foreground_task/service/RestartReceiver.kt +++ b/android/src/main/kotlin/com/pravera/flutter_foreground_task/service/RestartReceiver.kt @@ -99,4 +99,4 @@ class RestartReceiver : BroadcastReceiver() { } } } -} +} \ No newline at end of file diff --git a/android/src/main/kotlin/com/pravera/flutter_foreground_task/utils/ForegroundServiceUtils.kt b/android/src/main/kotlin/com/pravera/flutter_foreground_task/utils/ForegroundServiceUtils.kt index fcaed8a8..bf887bc9 100644 --- a/android/src/main/kotlin/com/pravera/flutter_foreground_task/utils/ForegroundServiceUtils.kt +++ b/android/src/main/kotlin/com/pravera/flutter_foreground_task/utils/ForegroundServiceUtils.kt @@ -7,24 +7,29 @@ import android.content.pm.PackageManager.NameNotFoundException import android.content.pm.ServiceInfo import android.util.Log import com.pravera.flutter_foreground_task.service.ForegroundService +import com.pravera.flutter_foreground_task.PreferencesKey as PrefsKey class ForegroundServiceUtils { companion object { private val TAG = ForegroundServiceUtils::class.java.simpleName fun isSetStopWithTaskFlag(context: Context): Boolean { - return try { + try { + val prefs = context.getSharedPreferences(PrefsKey.FOREGROUND_TASK_OPTIONS_PREFS, Context.MODE_PRIVATE) + if (prefs.contains(PrefsKey.STOP_WITH_TASK)) + return prefs.getBoolean(PrefsKey.STOP_WITH_TASK, false) + val pm = context.packageManager val cName = ComponentName(context, ForegroundService::class.java) val flags = pm.getServiceInfo(cName, PackageManager.GET_META_DATA).flags - (flags and ServiceInfo.FLAG_STOP_WITH_TASK) == 1 + return (flags and ServiceInfo.FLAG_STOP_WITH_TASK) != 0 } catch (e: NameNotFoundException) { Log.e(TAG, "isSetStopWithTaskFlag >> The service component cannot be found on the system.") - true + return true } catch (e: Exception) { Log.e(TAG, "isSetStopWithTaskFlag >> $e") - true + return true } } } -} +} \ No newline at end of file diff --git a/android/src/main/kotlin/com/pravera/flutter_foreground_task/utils/TrackVisibilityUtils.kt b/android/src/main/kotlin/com/pravera/flutter_foreground_task/utils/TrackVisibilityUtils.kt new file mode 100644 index 00000000..2e1c755f --- /dev/null +++ b/android/src/main/kotlin/com/pravera/flutter_foreground_task/utils/TrackVisibilityUtils.kt @@ -0,0 +1,35 @@ +package com.pravera.flutter_foreground_task.utils + +import android.app.* +import android.os.* + +object TrackVisibilityUtils : Application.ActivityLifecycleCallbacks { + private var installed = false + private val resumed = HashSet() + private var onAllGone: (() -> Unit)? = null + + fun install(application: Application, callback: () -> Unit) { + onAllGone = callback + if (!installed) { + installed = true + application.registerActivityLifecycleCallbacks(this) + } + } + + override fun onActivityResumed(activity: Activity) { + resumed.add(System.identityHashCode(activity)) + } + + override fun onActivityPaused(activity: Activity) { + resumed.remove(System.identityHashCode(activity)) + if (resumed.isEmpty()) { + onAllGone?.invoke() + } + } + + override fun onActivityStarted(activity: Activity) {} + override fun onActivityStopped(activity: Activity) {} + override fun onActivityCreated(a: Activity, b: Bundle?) {} + override fun onActivitySaveInstanceState(a: Activity, b: Bundle) {} + override fun onActivityDestroyed(a: Activity) {} +} \ No newline at end of file diff --git a/documentation/migration_documentation.md b/documentation/migration_documentation.md index fc11b470..c22f115f 100644 --- a/documentation/migration_documentation.md +++ b/documentation/migration_documentation.md @@ -1,5 +1,68 @@ ## Migration +### ver 11.0.0 + +- Bump minimum supported SDK version to `Flutter 3.44/Dart 3.12`. + +``` +environment: + // sdk: ^3.4.0 + // flutter: ">=3.38.0" + sdk: ^3.12.0 + flutter: ">=3.44.0" +``` + +- Migrate to built-in Kotlin (KGP). + +``` +>> android/app/build.gradle + +plugins { + // id "kotlin-android" +} + +android { + // kotlinOptions { + // jvmTarget = JavaVersion.VERSION_11 + // } +} + +kotlin { + compilerOptions { + jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11 + } +} +``` + +``` +>> android/gradle/wrapper/gradle-wrapper.properties + +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip + +----------------------------------------------------------------- + +>> android/settings.gradle + +plugins { + id "com.android.application" version "8.11.1" apply false + id "org.jetbrains.kotlin.android" version "2.2.20" apply false +} +``` + +- Support Swift Package Manager (SPM). The Obj-C bridging header import is no longer needed; use a module import in `AppDelegate.swift` instead. + +``` +>> ios/Runner/Runner-Bridging-Header.h + +// #import + +----------------------------------------------------------------- + +>> ios/Runner/AppDelegate.swift + +import flutter_foreground_task // module import +``` + ### ver 9.0.0 - chore: Bump minimum supported SDK version to `Flutter 3.22/Dart 3.4`. diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 66e580eb..3fdcdccf 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -1,6 +1,5 @@ plugins { id "com.android.application" - id "kotlin-android" id "dev.flutter.flutter-gradle-plugin" } @@ -25,7 +24,7 @@ if (flutterVersionName == null) { android { namespace "com.pravera.flutter_foreground_task_example" - compileSdk 35 + compileSdk 36 ndkVersion = flutter.ndkVersion compileOptions { @@ -33,10 +32,6 @@ android { targetCompatibility JavaVersion.VERSION_11 } - kotlinOptions { - jvmTarget = JavaVersion.VERSION_11 - } - sourceSets { main.java.srcDirs += 'src/main/kotlin' } @@ -44,7 +39,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.pravera.flutter_foreground_task_example" - minSdkVersion 21 + minSdkVersion flutter.minSdkVersion targetSdkVersion 35 versionCode flutterVersionCode.toInteger() versionName flutterVersionName @@ -72,3 +67,9 @@ flutter { } dependencies {} + +kotlin { + compilerOptions { + jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11 + } +} diff --git a/example/android/gradle.properties b/example/android/gradle.properties index 1967e2d9..9e5fb82e 100644 --- a/example/android/gradle.properties +++ b/example/android/gradle.properties @@ -2,3 +2,5 @@ org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError android.useAndroidX=true android.enableJetifier=true android.enableR8=true +android.builtInKotlin=true +android.newDsl=true diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index 7aeeb11c..1dbf2bb0 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip diff --git a/example/android/settings.gradle b/example/android/settings.gradle index bf71fb92..b6ae39a4 100644 --- a/example/android/settings.gradle +++ b/example/android/settings.gradle @@ -19,8 +19,8 @@ pluginManagement { plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version "8.6.0" apply false - id "org.jetbrains.kotlin.android" version "1.9.10" apply false + id "com.android.application" version "8.11.1" apply false + id "org.jetbrains.kotlin.android" version "2.2.20" apply false } include ":app" diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist index 7c569640..391a902b 100644 --- a/example/ios/Flutter/AppFrameworkInfo.plist +++ b/example/ios/Flutter/AppFrameworkInfo.plist @@ -20,7 +20,5 @@ ???? CFBundleVersion 1.0 - MinimumOSVersion - 12.0 diff --git a/example/ios/Flutter/Debug.xcconfig b/example/ios/Flutter/Debug.xcconfig index ec97fc6f..592ceee8 100644 --- a/example/ios/Flutter/Debug.xcconfig +++ b/example/ios/Flutter/Debug.xcconfig @@ -1,2 +1 @@ -#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" diff --git a/example/ios/Flutter/Release.xcconfig b/example/ios/Flutter/Release.xcconfig index c4855bfe..592ceee8 100644 --- a/example/ios/Flutter/Release.xcconfig +++ b/example/ios/Flutter/Release.xcconfig @@ -1,2 +1 @@ -#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Generated.xcconfig" diff --git a/example/ios/Flutter/ephemeral/flutter_lldb_helper.py b/example/ios/Flutter/ephemeral/flutter_lldb_helper.py deleted file mode 100644 index a88caf99..00000000 --- a/example/ios/Flutter/ephemeral/flutter_lldb_helper.py +++ /dev/null @@ -1,32 +0,0 @@ -# -# Generated file, do not edit. -# - -import lldb - -def handle_new_rx_page(frame: lldb.SBFrame, bp_loc, extra_args, intern_dict): - """Intercept NOTIFY_DEBUGGER_ABOUT_RX_PAGES and touch the pages.""" - base = frame.register["x0"].GetValueAsAddress() - page_len = frame.register["x1"].GetValueAsUnsigned() - - # Note: NOTIFY_DEBUGGER_ABOUT_RX_PAGES will check contents of the - # first page to see if handled it correctly. This makes diagnosing - # misconfiguration (e.g. missing breakpoint) easier. - data = bytearray(page_len) - data[0:8] = b'IHELPED!' - - error = lldb.SBError() - frame.GetThread().GetProcess().WriteMemory(base, data, error) - if not error.Success(): - print(f'Failed to write into {base}[+{page_len}]', error) - return - -def __lldb_init_module(debugger: lldb.SBDebugger, _): - target = debugger.GetDummyTarget() - # Caveat: must use BreakpointCreateByRegEx here and not - # BreakpointCreateByName. For some reasons callback function does not - # get carried over from dummy target for the later. - bp = target.BreakpointCreateByRegex("^NOTIFY_DEBUGGER_ABOUT_RX_PAGES$") - bp.SetScriptCallbackFunction('{}.handle_new_rx_page'.format(__name__)) - bp.SetAutoContinue(True) - print("-- LLDB integration loaded --") diff --git a/example/ios/Flutter/ephemeral/flutter_lldbinit b/example/ios/Flutter/ephemeral/flutter_lldbinit deleted file mode 100644 index e3ba6fbe..00000000 --- a/example/ios/Flutter/ephemeral/flutter_lldbinit +++ /dev/null @@ -1,5 +0,0 @@ -# -# Generated file, do not edit. -# - -command script import --relative-to-command-file flutter_lldb_helper.py diff --git a/example/ios/Podfile b/example/ios/Podfile deleted file mode 100644 index 279576f3..00000000 --- a/example/ios/Podfile +++ /dev/null @@ -1,41 +0,0 @@ -# Uncomment this line to define a global platform for your project -# platform :ios, '12.0' - -# CocoaPods analytics sends network stats synchronously affecting flutter build latency. -ENV['COCOAPODS_DISABLE_STATS'] = 'true' - -project 'Runner', { - 'Debug' => :debug, - 'Profile' => :release, - 'Release' => :release, -} - -def flutter_root - generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) - unless File.exist?(generated_xcode_build_settings_path) - raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" - end - - File.foreach(generated_xcode_build_settings_path) do |line| - matches = line.match(/FLUTTER_ROOT\=(.*)/) - return matches[1].strip if matches - end - raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" -end - -require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) - -flutter_ios_podfile_setup - -target 'Runner' do - use_frameworks! - use_modular_headers! - - flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) -end - -post_install do |installer| - installer.pods_project.targets.each do |target| - flutter_additional_ios_build_settings(target) - end -end diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index bb8cbb51..14a4d00c 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -8,9 +8,9 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 37B6493A4744452DDC4DEBF7 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A32D55799B66D9DF641C7ED8 /* Pods_Runner.framework */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; @@ -32,12 +32,13 @@ /* Begin PBXFileReference section */ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 2EDD805CC77857CB5704E03A /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 784666492D4C4C64000A1A5F /* FlutterFramework */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterFramework; path = Flutter/ephemeral/Packages/.packages/FlutterFramework; sourceTree = ""; }; + 78DABEA22ED26510000E7860 /* flutter_foreground_task */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = flutter_foreground_task; path = ../../ios/flutter_foreground_task; sourceTree = ""; }; + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 8BA7286763F4F5A5D4D4791F /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -45,8 +46,6 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 9DCDDA24B41ECFF3C6781683 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; - A32D55799B66D9DF641C7ED8 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -54,7 +53,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 37B6493A4744452DDC4DEBF7 /* Pods_Runner.framework in Frameworks */, + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -64,9 +63,6 @@ 2105D355AEE26F31BDDE1572 /* Pods */ = { isa = PBXGroup; children = ( - 8BA7286763F4F5A5D4D4791F /* Pods-Runner.debug.xcconfig */, - 9DCDDA24B41ECFF3C6781683 /* Pods-Runner.release.xcconfig */, - 2EDD805CC77857CB5704E03A /* Pods-Runner.profile.xcconfig */, ); path = Pods; sourceTree = ""; @@ -74,6 +70,9 @@ 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( + 78DABEA22ED26510000E7860 /* flutter_foreground_task */, + 784666492D4C4C64000A1A5F /* FlutterFramework */, + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 9740EEB21CF90195004384FC /* Debug.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, @@ -89,7 +88,6 @@ 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, 2105D355AEE26F31BDDE1572 /* Pods */, - A07F075276D23FBA8C723152 /* Frameworks */, ); sourceTree = ""; }; @@ -116,14 +114,6 @@ path = Runner; sourceTree = ""; }; - A07F075276D23FBA8C723152 /* Frameworks */ = { - isa = PBXGroup; - children = ( - A32D55799B66D9DF641C7ED8 /* Pods_Runner.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -131,20 +121,21 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - 8D396599160DECB90EDD47BE /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - 6EA6E717D2A4A7547969F4C2 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); dependencies = ( ); name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); productName = Runner; productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productType = "com.apple.product-type.application"; @@ -173,6 +164,9 @@ Base, ); mainGroup = 97C146E51CF9000F007C117D; + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, + ); productRefGroup = 97C146EF1CF9000F007C117D /* Products */; projectDirPath = ""; projectRoot = ""; @@ -213,45 +207,6 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; - 6EA6E717D2A4A7547969F4C2 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - 8D396599160DECB90EDD47BE /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -361,7 +316,7 @@ DEVELOPMENT_TEAM = 2BHAXN696Y; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -491,7 +446,7 @@ DEVELOPMENT_TEAM = 2BHAXN696Y; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -515,7 +470,7 @@ DEVELOPMENT_TEAM = 2BHAXN696Y; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -552,6 +507,20 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; } diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 67709153..d5a29ad2 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -5,6 +5,24 @@ + + + + + + + + + + diff --git a/example/ios/Runner/AppDelegate.swift b/example/ios/Runner/AppDelegate.swift index e35f9bac..b43ca61c 100644 --- a/example/ios/Runner/AppDelegate.swift +++ b/example/ios/Runner/AppDelegate.swift @@ -1,14 +1,13 @@ import UIKit import Flutter +import flutter_foreground_task @main -@objc class AppDelegate: FlutterAppDelegate { +@objc class AppDelegate: FlutterAppDelegate, FlutterImplicitEngineDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { - GeneratedPluginRegistrant.register(with: self) - SwiftFlutterForegroundTaskPlugin.setPluginRegistrantCallback { registry in GeneratedPluginRegistrant.register(with: registry) } @@ -18,4 +17,8 @@ import Flutter return super.application(application, didFinishLaunchingWithOptions: launchOptions) } + + func didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) { + GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry) + } } diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist index 8bb4e7ea..2c251415 100644 --- a/example/ios/Runner/Info.plist +++ b/example/ios/Runner/Info.plist @@ -53,5 +53,26 @@ UIViewControllerBasedStatusBarAppearance + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneClassName + UIWindowScene + UISceneDelegateClassName + FlutterSceneDelegate + UISceneConfigurationName + flutter + UISceneStoryboardFile + Main + + + + diff --git a/example/ios/Runner/Runner-Bridging-Header.h b/example/ios/Runner/Runner-Bridging-Header.h index 10e9f025..3f9454b6 100644 --- a/example/ios/Runner/Runner-Bridging-Header.h +++ b/example/ios/Runner/Runner-Bridging-Header.h @@ -1,2 +1,2 @@ #import "GeneratedPluginRegistrant.h" -#import +//#import diff --git a/example/pubspec.yaml b/example/pubspec.yaml index b0bbe590..181c3caf 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -6,8 +6,8 @@ description: Demonstrates how to use the flutter_foreground_task plugin. publish_to: 'none' # Remove this line if you wish to publish to pub.dev environment: - sdk: ^3.4.0 - flutter: ">=3.22.0" + sdk: ^3.12.0 + flutter: ">=3.44.0" dependencies: flutter: diff --git a/ios/flutter_foreground_task.podspec b/ios/flutter_foreground_task.podspec index 45788fda..bda3a883 100644 --- a/ios/flutter_foreground_task.podspec +++ b/ios/flutter_foreground_task.podspec @@ -13,9 +13,9 @@ A new Flutter plugin. s.license = { :file => '../LICENSE' } s.author = { 'Your Company' => 'email@example.com' } s.source = { :path => '.' } - s.source_files = 'Classes/**/*' + s.source_files = 'flutter_foreground_task/Sources/flutter_foreground_task/**/*.swift', 'flutter_foreground_task/Classes/**/*.{h,m}' s.dependency 'Flutter' - s.platform = :ios, '8.0' + s.platform = :ios, '12.0' # Flutter.framework does not contain a i386 slice. s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } diff --git a/ios/Assets/.gitkeep b/ios/flutter_foreground_task/Assets/.gitkeep similarity index 100% rename from ios/Assets/.gitkeep rename to ios/flutter_foreground_task/Assets/.gitkeep diff --git a/ios/Classes/FlutterForegroundTaskPlugin.h b/ios/flutter_foreground_task/Classes/FlutterForegroundTaskPlugin.h similarity index 100% rename from ios/Classes/FlutterForegroundTaskPlugin.h rename to ios/flutter_foreground_task/Classes/FlutterForegroundTaskPlugin.h diff --git a/ios/Classes/FlutterForegroundTaskPlugin.m b/ios/flutter_foreground_task/Classes/FlutterForegroundTaskPlugin.m similarity index 64% rename from ios/Classes/FlutterForegroundTaskPlugin.m rename to ios/flutter_foreground_task/Classes/FlutterForegroundTaskPlugin.m index e4e7a71f..07c7e1ba 100644 --- a/ios/Classes/FlutterForegroundTaskPlugin.m +++ b/ios/flutter_foreground_task/Classes/FlutterForegroundTaskPlugin.m @@ -10,9 +10,18 @@ @implementation FlutterForegroundTaskPlugin + (void)registerWithRegistrar:(NSObject*)registrar { - [SwiftFlutterForegroundTaskPlugin registerWithRegistrar:registrar]; + [SwiftFlutterForegroundTaskPlugin registerWithRegistrar:registrar]; } + (void)setPluginRegistrantCallback:(FlutterPluginRegistrantCallback)callback { - [SwiftFlutterForegroundTaskPlugin setPluginRegistrantCallback:callback]; + [SwiftFlutterForegroundTaskPlugin setPluginRegistrantCallback:callback]; +} +@end + +@interface FlutterForegroundTaskEarlyRegistration : NSObject +@end + +@implementation FlutterForegroundTaskEarlyRegistration ++ (void)load { + [SwiftFlutterForegroundTaskPlugin registerAppRefreshForBackgroundLaunch]; } @end diff --git a/ios/flutter_foreground_task/Package.swift b/ios/flutter_foreground_task/Package.swift new file mode 100644 index 00000000..99ac3e84 --- /dev/null +++ b/ios/flutter_foreground_task/Package.swift @@ -0,0 +1,33 @@ +// swift-tools-version: 5.9 +import PackageDescription + +let package = Package( + name: "flutter_foreground_task", + platforms: [ + .iOS("13.0") + ], + products: [ + .library( + name: "flutter-foreground-task", + targets: ["flutter_foreground_task", "flutter_foreground_task_early_registration"] + ) + ], + dependencies: [ + .package(name: "FlutterFramework", path: "../FlutterFramework") + ], + targets: [ + .target( + name: "flutter_foreground_task", + dependencies: [ + .product(name: "FlutterFramework", package: "FlutterFramework") + ] + ), + .target( + name: "flutter_foreground_task_early_registration", + dependencies: ["flutter_foreground_task"], + cSettings: [ + .headerSearchPath("include/flutter_foreground_task_early_registration") + ] + ) + ] +) diff --git a/ios/Classes/FlutterForegroundTaskLifecycleListener.swift b/ios/flutter_foreground_task/Sources/flutter_foreground_task/FlutterForegroundTaskLifecycleListener.swift similarity index 100% rename from ios/Classes/FlutterForegroundTaskLifecycleListener.swift rename to ios/flutter_foreground_task/Sources/flutter_foreground_task/FlutterForegroundTaskLifecycleListener.swift diff --git a/ios/flutter_foreground_task/Sources/flutter_foreground_task/FlutterForegroundTaskPluginSPMBridge.swift b/ios/flutter_foreground_task/Sources/flutter_foreground_task/FlutterForegroundTaskPluginSPMBridge.swift new file mode 100644 index 00000000..8d5b84e6 --- /dev/null +++ b/ios/flutter_foreground_task/Sources/flutter_foreground_task/FlutterForegroundTaskPluginSPMBridge.swift @@ -0,0 +1,23 @@ +#if SWIFT_PACKAGE + +import Flutter + +@objc(FlutterForegroundTaskPlugin) +public class FlutterForegroundTaskPlugin: NSObject, FlutterPlugin { + public static func register(with registrar: FlutterPluginRegistrar) { + SwiftFlutterForegroundTaskPlugin.register(with: registrar) + } + + public static func setPluginRegistrantCallback( + _ callback: @escaping FlutterPluginRegistrantCallback + ) { + SwiftFlutterForegroundTaskPlugin.setPluginRegistrantCallback(callback) + } +} + +@_cdecl("flutter_foreground_task_register_app_refresh") +public func flutter_foreground_task_register_app_refresh() { + SwiftFlutterForegroundTaskPlugin.registerAppRefreshForBackgroundLaunch() +} + +#endif \ No newline at end of file diff --git a/ios/Classes/FlutterForegroundTaskStarter.swift b/ios/flutter_foreground_task/Sources/flutter_foreground_task/FlutterForegroundTaskStarter.swift similarity index 100% rename from ios/Classes/FlutterForegroundTaskStarter.swift rename to ios/flutter_foreground_task/Sources/flutter_foreground_task/FlutterForegroundTaskStarter.swift diff --git a/ios/Classes/PreferencesKey.swift b/ios/flutter_foreground_task/Sources/flutter_foreground_task/PreferencesKey.swift similarity index 100% rename from ios/Classes/PreferencesKey.swift rename to ios/flutter_foreground_task/Sources/flutter_foreground_task/PreferencesKey.swift diff --git a/ios/Classes/SwiftFlutterForegroundTaskPlugin.swift b/ios/flutter_foreground_task/Sources/flutter_foreground_task/SwiftFlutterForegroundTaskPlugin.swift similarity index 88% rename from ios/Classes/SwiftFlutterForegroundTaskPlugin.swift rename to ios/flutter_foreground_task/Sources/flutter_foreground_task/SwiftFlutterForegroundTaskPlugin.swift index 8c163e66..33046730 100644 --- a/ios/Classes/SwiftFlutterForegroundTaskPlugin.swift +++ b/ios/flutter_foreground_task/Sources/flutter_foreground_task/SwiftFlutterForegroundTaskPlugin.swift @@ -16,11 +16,22 @@ public class SwiftFlutterForegroundTaskPlugin: NSObject, FlutterPlugin { instance.initServices() instance.initChannels(registrar.messenger()) registrar.addApplicationDelegate(instance) + if #available(iOS 13.0, *) { + registrar.addSceneDelegate(instance) + } } - public static func setPluginRegistrantCallback(_ callback: @escaping FlutterPluginRegistrantCallback) { + @objc public static func setPluginRegistrantCallback(_ callback: @escaping FlutterPluginRegistrantCallback) { registerPlugins = callback } + + @objc public static func registerAppRefreshForBackgroundLaunch() { + if #available(iOS 13.0, *) { + let permitted = Bundle.main.object(forInfoDictionaryKey: "BGTaskSchedulerPermittedIdentifiers") as? [String] ?? [] + guard permitted.contains(refreshIdentifier) else { return } + registerAppRefresh() + } + } public static func addTaskLifecycleListener(_ listener: FlutterForegroundTaskLifecycleListener) { BackgroundService.sharedInstance.addTaskLifecycleListener(listener) @@ -114,7 +125,7 @@ public class SwiftFlutterForegroundTaskPlugin: NSObject, FlutterPlugin { // Chance to handle onDestroy before app terminates sleep(5) } - + // ================= Service Delegate ================= @available(iOS 10.0, *) public func userNotificationCenter(_ center: UNUserNotificationCenter, @@ -132,9 +143,12 @@ public class SwiftFlutterForegroundTaskPlugin: NSObject, FlutterPlugin { // ============== Background App Refresh ============== public static var refreshIdentifier: String = "com.pravera.flutter_foreground_task.refresh" + private static var isBgTaskRegistered: Bool = false @available(iOS 13.0, *) private static func registerAppRefresh() { + guard !isBgTaskRegistered else { return } + isBgTaskRegistered = true BGTaskScheduler.shared.register(forTaskWithIdentifier: refreshIdentifier, using: nil) { task in handleAppRefresh(task: task as! BGAppRefreshTask) } @@ -189,3 +203,10 @@ class AppRefreshOperation: Operation { semaphore.wait() } } + +@available(iOS 13.0, *) +extension SwiftFlutterForegroundTaskPlugin: FlutterSceneLifeCycleDelegate { + public func sceneDidEnterBackground(_ scene: UIScene) { + SwiftFlutterForegroundTaskPlugin.scheduleAppRefresh() + } +} diff --git a/ios/Classes/errors/ServiceError.swift b/ios/flutter_foreground_task/Sources/flutter_foreground_task/errors/ServiceError.swift similarity index 100% rename from ios/Classes/errors/ServiceError.swift rename to ios/flutter_foreground_task/Sources/flutter_foreground_task/errors/ServiceError.swift diff --git a/ios/Classes/models/BackgroundServiceAction.swift b/ios/flutter_foreground_task/Sources/flutter_foreground_task/models/BackgroundServiceAction.swift similarity index 100% rename from ios/Classes/models/BackgroundServiceAction.swift rename to ios/flutter_foreground_task/Sources/flutter_foreground_task/models/BackgroundServiceAction.swift diff --git a/ios/Classes/models/BackgroundServiceStatus.swift b/ios/flutter_foreground_task/Sources/flutter_foreground_task/models/BackgroundServiceStatus.swift similarity index 100% rename from ios/Classes/models/BackgroundServiceStatus.swift rename to ios/flutter_foreground_task/Sources/flutter_foreground_task/models/BackgroundServiceStatus.swift diff --git a/ios/Classes/models/ForegroundTaskData.swift b/ios/flutter_foreground_task/Sources/flutter_foreground_task/models/ForegroundTaskData.swift similarity index 100% rename from ios/Classes/models/ForegroundTaskData.swift rename to ios/flutter_foreground_task/Sources/flutter_foreground_task/models/ForegroundTaskData.swift diff --git a/ios/Classes/models/ForegroundTaskEventAction.swift b/ios/flutter_foreground_task/Sources/flutter_foreground_task/models/ForegroundTaskEventAction.swift similarity index 100% rename from ios/Classes/models/ForegroundTaskEventAction.swift rename to ios/flutter_foreground_task/Sources/flutter_foreground_task/models/ForegroundTaskEventAction.swift diff --git a/ios/Classes/models/ForegroundTaskEventType.swift b/ios/flutter_foreground_task/Sources/flutter_foreground_task/models/ForegroundTaskEventType.swift similarity index 100% rename from ios/Classes/models/ForegroundTaskEventType.swift rename to ios/flutter_foreground_task/Sources/flutter_foreground_task/models/ForegroundTaskEventType.swift diff --git a/ios/Classes/models/ForegroundTaskOptions.swift b/ios/flutter_foreground_task/Sources/flutter_foreground_task/models/ForegroundTaskOptions.swift similarity index 100% rename from ios/Classes/models/ForegroundTaskOptions.swift rename to ios/flutter_foreground_task/Sources/flutter_foreground_task/models/ForegroundTaskOptions.swift diff --git a/ios/Classes/models/NotificationButton.swift b/ios/flutter_foreground_task/Sources/flutter_foreground_task/models/NotificationButton.swift similarity index 100% rename from ios/Classes/models/NotificationButton.swift rename to ios/flutter_foreground_task/Sources/flutter_foreground_task/models/NotificationButton.swift diff --git a/ios/Classes/models/NotificationContent.swift b/ios/flutter_foreground_task/Sources/flutter_foreground_task/models/NotificationContent.swift similarity index 100% rename from ios/Classes/models/NotificationContent.swift rename to ios/flutter_foreground_task/Sources/flutter_foreground_task/models/NotificationContent.swift diff --git a/ios/Classes/models/NotificationOptions.swift b/ios/flutter_foreground_task/Sources/flutter_foreground_task/models/NotificationOptions.swift similarity index 100% rename from ios/Classes/models/NotificationOptions.swift rename to ios/flutter_foreground_task/Sources/flutter_foreground_task/models/NotificationOptions.swift diff --git a/ios/Classes/models/NotificationPermission.swift b/ios/flutter_foreground_task/Sources/flutter_foreground_task/models/NotificationPermission.swift similarity index 100% rename from ios/Classes/models/NotificationPermission.swift rename to ios/flutter_foreground_task/Sources/flutter_foreground_task/models/NotificationPermission.swift diff --git a/ios/Classes/service/BackgroundService.swift b/ios/flutter_foreground_task/Sources/flutter_foreground_task/service/BackgroundService.swift similarity index 100% rename from ios/Classes/service/BackgroundService.swift rename to ios/flutter_foreground_task/Sources/flutter_foreground_task/service/BackgroundService.swift diff --git a/ios/Classes/service/BackgroundServiceManager.swift b/ios/flutter_foreground_task/Sources/flutter_foreground_task/service/BackgroundServiceManager.swift similarity index 100% rename from ios/Classes/service/BackgroundServiceManager.swift rename to ios/flutter_foreground_task/Sources/flutter_foreground_task/service/BackgroundServiceManager.swift diff --git a/ios/Classes/service/ForegroundTask.swift b/ios/flutter_foreground_task/Sources/flutter_foreground_task/service/ForegroundTask.swift similarity index 99% rename from ios/Classes/service/ForegroundTask.swift rename to ios/flutter_foreground_task/Sources/flutter_foreground_task/service/ForegroundTask.swift index 35ea4187..20c5b585 100644 --- a/ios/Classes/service/ForegroundTask.swift +++ b/ios/flutter_foreground_task/Sources/flutter_foreground_task/service/ForegroundTask.swift @@ -6,6 +6,7 @@ // import Foundation +import Flutter private let BG_ISOLATE_NAME = "flutter_foreground_task/backgroundIsolate" private let BG_CHANNEL_NAME = "flutter_foreground_task/background" diff --git a/ios/Classes/service/ForegroundTaskLifecycleListeners.swift b/ios/flutter_foreground_task/Sources/flutter_foreground_task/service/ForegroundTaskLifecycleListeners.swift similarity index 98% rename from ios/Classes/service/ForegroundTaskLifecycleListeners.swift rename to ios/flutter_foreground_task/Sources/flutter_foreground_task/service/ForegroundTaskLifecycleListeners.swift index 4a578534..7724b559 100644 --- a/ios/Classes/service/ForegroundTaskLifecycleListeners.swift +++ b/ios/flutter_foreground_task/Sources/flutter_foreground_task/service/ForegroundTaskLifecycleListeners.swift @@ -6,6 +6,7 @@ // import Foundation +import Flutter class ForegroundTaskLifecycleListeners : FlutterForegroundTaskLifecycleListener { private var listeners: Array = [] diff --git a/ios/Classes/service/NotificationPermissionManager.swift b/ios/flutter_foreground_task/Sources/flutter_foreground_task/service/NotificationPermissionManager.swift similarity index 97% rename from ios/Classes/service/NotificationPermissionManager.swift rename to ios/flutter_foreground_task/Sources/flutter_foreground_task/service/NotificationPermissionManager.swift index 461c9f7d..6df0b3f1 100644 --- a/ios/Classes/service/NotificationPermissionManager.swift +++ b/ios/flutter_foreground_task/Sources/flutter_foreground_task/service/NotificationPermissionManager.swift @@ -6,6 +6,7 @@ // import Foundation +import UserNotifications class NotificationPermissionManager { func checkPermission(completion: @escaping (NotificationPermission) -> Void) { diff --git a/ios/Classes/service/VibrationManager.swift b/ios/flutter_foreground_task/Sources/flutter_foreground_task/service/VibrationManager.swift similarity index 100% rename from ios/Classes/service/VibrationManager.swift rename to ios/flutter_foreground_task/Sources/flutter_foreground_task/service/VibrationManager.swift diff --git a/ios/flutter_foreground_task/Sources/flutter_foreground_task_early_registration/Flutterforegroundtaskearlyregistration.m b/ios/flutter_foreground_task/Sources/flutter_foreground_task_early_registration/Flutterforegroundtaskearlyregistration.m new file mode 100644 index 00000000..2bdf405c --- /dev/null +++ b/ios/flutter_foreground_task/Sources/flutter_foreground_task_early_registration/Flutterforegroundtaskearlyregistration.m @@ -0,0 +1,11 @@ +#import "FlutterForegroundTaskEarlyRegistration.h" +#import + +@interface FlutterForegroundTaskEarlyRegistration : NSObject +@end + +@implementation FlutterForegroundTaskEarlyRegistration ++ (void)load { + flutter_foreground_task_register_app_refresh(); +} +@end \ No newline at end of file diff --git a/ios/flutter_foreground_task/Sources/flutter_foreground_task_early_registration/include/flutter_foreground_task_early_registration/Flutterforegroundtaskearlyregistration.h b/ios/flutter_foreground_task/Sources/flutter_foreground_task_early_registration/include/flutter_foreground_task_early_registration/Flutterforegroundtaskearlyregistration.h new file mode 100644 index 00000000..3fafe7f7 --- /dev/null +++ b/ios/flutter_foreground_task/Sources/flutter_foreground_task_early_registration/include/flutter_foreground_task_early_registration/Flutterforegroundtaskearlyregistration.h @@ -0,0 +1,6 @@ +#ifndef FlutterForegroundTaskEarlyRegistration_h +#define FlutterForegroundTaskEarlyRegistration_h + +extern void flutter_foreground_task_register_app_refresh(void); + +#endif \ No newline at end of file diff --git a/lib/models/foreground_service_types.dart b/lib/models/foreground_service_types.dart index 97327dbd..a307169b 100644 --- a/lib/models/foreground_service_types.dart +++ b/lib/models/foreground_service_types.dart @@ -49,6 +49,9 @@ class ForegroundServiceTypes { /// Reserved for system applications and specific system integrations, to continue to use foreground services. static const systemExempted = ForegroundServiceTypes(12); + /// Service for performing time-consuming operations on media assets, like converting media to different formats. + static const mediaProcessing = ForegroundServiceTypes(13); + /// The raw value of [ForegroundServiceTypes]. final int rawValue; } diff --git a/lib/models/foreground_task_options.dart b/lib/models/foreground_task_options.dart index 9a10c21f..814bb16a 100644 --- a/lib/models/foreground_task_options.dart +++ b/lib/models/foreground_task_options.dart @@ -10,6 +10,7 @@ class ForegroundTaskOptions { this.allowWakeLock = true, this.allowWifiLock = false, this.allowAutoRestart = true, + this.stopWithTask, }); /// The action of onRepeatEvent in [TaskHandler]. @@ -38,6 +39,10 @@ class ForegroundTaskOptions { /// https://developer.android.com/about/versions/15/behavior-changes-15?hl=pt-br#datasync-timeout final bool allowAutoRestart; + /// Allows an application to automatically stop when the app task is removed by the system. + /// If set, overrides the service android:stopWithTask behavior. + final bool? stopWithTask; + /// Returns the data fields of [ForegroundTaskOptions] in JSON format. Map toJson() { return { @@ -47,9 +52,12 @@ class ForegroundTaskOptions { 'allowWakeLock': allowWakeLock, 'allowWifiLock': allowWifiLock, 'allowAutoRestart': allowAutoRestart, + if (stopWithTask != null) 'stopWithTask': stopWithTask, }; } + static const _unset = Object(); + /// Creates a copy of the object replaced with new values. ForegroundTaskOptions copyWith({ ForegroundTaskEventAction? eventAction, @@ -58,6 +66,7 @@ class ForegroundTaskOptions { bool? allowWakeLock, bool? allowWifiLock, bool? allowAutoRestart, + Object? stopWithTask = _unset, }) => ForegroundTaskOptions( eventAction: eventAction ?? this.eventAction, @@ -66,5 +75,6 @@ class ForegroundTaskOptions { allowWakeLock: allowWakeLock ?? this.allowWakeLock, allowWifiLock: allowWifiLock ?? this.allowWifiLock, allowAutoRestart: allowAutoRestart ?? this.allowAutoRestart, + stopWithTask: identical(stopWithTask, _unset) ? this.stopWithTask : stopWithTask as bool?, ); -} +} \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index 54431ee2..ecb8f668 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,17 +1,17 @@ name: flutter_foreground_task description: This plugin is used to implement a foreground service on the Android platform. -version: 9.1.0 +version: 11.0.3 homepage: https://github.com/Dev-hwang/flutter_foreground_task environment: - sdk: ^3.4.0 - flutter: ">=3.22.0" + sdk: ^3.12.0 + flutter: ">=3.44.0" dependencies: flutter: sdk: flutter plugin_platform_interface: ^2.1.8 - shared_preferences: ^2.5.3 + shared_preferences: ^2.5.5 platform: ^3.1.6 dev_dependencies: