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 2f98a35..af45a7a 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 @@ -53,7 +53,8 @@ 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 + // task data const val CALLBACK_HANDLE = "callbackHandle" -} +} \ No newline at end of file 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 b1f1515..cdfb362 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 7d4b3d6..a90ae09 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 @@ -17,7 +17,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 @@ -125,6 +126,15 @@ class ForegroundService : Service() { isTimeout = false loadDataFromPreferences() + 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) { + stopForegroundService() + } + } + } + var action = foregroundServiceStatus.action val isSetStopWithTaskFlag = ForegroundServiceUtils.isSetStopWithTaskFlag(this) 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 dbf1249..3c15a79 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 @@ -100,4 +100,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 54f4f85..bf887bc 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,23 +7,28 @@ 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) != 0 + 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 } } } 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 0000000..2e1c755 --- /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/lib/models/foreground_task_options.dart b/lib/models/foreground_task_options.dart index 9a10c21..814bb16 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