diff --git a/.idea/flutter_background_service_android.iml b/.idea/flutter_background_service_android.iml
new file mode 100644
index 0000000..6155e93
--- /dev/null
+++ b/.idea/flutter_background_service_android.iml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Dart_Packages.xml b/.idea/libraries/Dart_Packages.xml
new file mode 100644
index 0000000..460f26e
--- /dev/null
+++ b/.idea/libraries/Dart_Packages.xml
@@ -0,0 +1,236 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Dart_SDK.xml b/.idea/libraries/Dart_SDK.xml
new file mode 100644
index 0000000..3dad229
--- /dev/null
+++ b/.idea/libraries/Dart_SDK.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Flutter_Plugins.xml b/.idea/libraries/Flutter_Plugins.xml
new file mode 100644
index 0000000..b0f6971
--- /dev/null
+++ b/.idea/libraries/Flutter_Plugins.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..aaa4363
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..5064f81
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,180 @@
+## 2.0.3
+
+ - **FIX**: wakelock not released. ([e427f3b7](https://github.com/ekasetiawans/flutter_background_service/commit/e427f3b70138ec26f9671c2617f9061f25eade6f))
+
+## 2.0.2
+
+ - **FIX**: autoStartOnBootMode #160. ([16a785a3](https://github.com/ekasetiawans/flutter_background_service/commit/16a785a3cbcb4226321ddddf681b6554196fa4db))
+
+## 2.0.1
+
+ - **FIX**: release wakelock. ([c0830250](https://github.com/ekasetiawans/flutter_background_service/commit/c0830250b90a1ba6e2543a1bb25a13fba59a56b7))
+
+## 2.0.0
+
+ - Graduate package to a stable release. See pre-releases prior to this version for changelog entries.
+
+## 2.0.0-dev.0
+
+> Note: This release has breaking changes.
+
+ - **BREAKING** **FEAT**: implement new concept. ([c8ce9c0b](https://github.com/ekasetiawans/flutter_background_service/commit/c8ce9c0bab82137dea031af124b84510286661f7))
+
+## 1.0.2
+
+ - **DOCS**: readme link. ([1479b91c](https://github.com/ekasetiawans/flutter_background_service/commit/1479b91cd80d637335de1314a528bcf51ebb7c0f))
+
+## 1.0.1
+
+ - **DOCS**: update README. ([fbf5e0ab](https://github.com/ekasetiawans/flutter_background_service/commit/fbf5e0abeeb9296ba32361b8af0a298ee9e71527))
+
+## 0.0.2
+
+ - **FEAT**: migrate to plugin platform interface. ([70e08ff0](https://github.com/ekasetiawans/flutter_background_service/commit/70e08ff03232c31946cc8eb7896f69c830f23322))
+
+## 0.0.1+3
+
+ - **FIX**: errors. ([13a6f841](https://github.com/ekasetiawans/flutter_background_service/commit/13a6f841f5d677ceb0010e8ba1bf9d7af53adbcf))
+
+## 0.0.1+2
+
+ - Update a dependency to the latest release.
+
+## 0.0.1+1
+
+ - **REFACTOR**: initialize melos.
+
+## 0.2.6
+* FIX: (Android) flutter initialization
+## 0.2.5
+* FIX: (iOS) using other plugins
+## 0.2.4
+* FIX: (Android) run service background when charger not connected and screen lock (#92)
+## 0.2.3
+* ADDED: Using `BGTaskScheduler` on iOS 13. See readme for configuration.
+## 0.2.2
+* ADDED: `autoStart` to `IosConfiguration`
+## 0.2.1
+* UPDATE README
+* UPDATE: Flutter Version Constraint
+## 0.2.0+1
+* UPDATE README
+
+## 0.2.0
+* [BREAKING]: FlutterBackgroundService.initialize renamed to FlutterBackgroundService.configure
+* [BREAKING]: use FlutterBackgroundService.start to start or restart after you call stopService.
+* [ADDED]: IOS Background fetch is now supported you have to enable background fetch from xcode.
+## 0.1.7
+
+* Fix : cannot start service on android 12
+* Fix : not started on boot completed
+## 0.1.6
+
+* Android 12 Compatibility Changes
+## 0.1.5
+
+* Rollback foreground notification importance
+## 0.1.4
+
+* fixes UnsatisfiedLinkError when running as foreground service with autostart #32
+## 0.1.3
+
+* Fix notification not showing on android 7 and prior (Issue #26)
+## 0.1.2
+
+* Open app from notification (Issue #30)
+## 0.1.1
+
+* Fix #29 (DartVM not terminated when service stop)
+
+## 0.1.0
+
+* Bump flutter 2
+
+## 0.1.0-nullsafety.2
+
+* Fix #23
+
+## 0.1.0-nullsafety.1
+
+* Added isServiceRunning on iOS (issue #19)
+
+## 0.1.0-nullsafety.0
+
+* Added support to nullsafety
+
+## 0.0.1+18
+
+* Added stopService Method(Currently Works on Android Only).
+
+## 0.0.1+17
+
+* Add preference autoStart on Boot, default is true.
+
+## 0.0.1+16
+
+* Set Foreground Mode to false will remove notification. BugFix #4.
+
+## 0.0.1+15
+
+* Add ability to change Background or Foreground mode (Android Only)
+
+## 0.0.1+14
+
+* Bugfix BootReceiver
+
+## 0.0.1+13
+
+* Update example for iOS support.
+
+## 0.0.1+12
+
+* Start service immediately after initialize
+
+## 0.0.1+11
+
+* iOS
+
+## 0.0.1+10
+
+* bug fix
+
+## 0.0.1+9
+
+* bug fix
+
+## 0.0.1+8
+
+* bug fix
+
+## 0.0.1+7
+
+* Add ability to send data from UI to Service
+
+## 0.0.1+6
+
+* Improve stability
+
+## 0.0.1+5
+
+* Add ability to send data from service to UI
+
+## 0.0.1+4
+
+* Update README
+
+## 0.0.1+3
+
+* Add ability to change notification info (Android foreground service)
+
+## 0.0.1+2
+
+* Fix android missing plugin implementation
+
+## 0.0.1+1
+
+* Fix android build
+
+## 0.0.1
+
+* TODO: Describe initial release.
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..6f3b2ac
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,25 @@
+Copyright 2017 The Chromium Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of Google Inc. nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/android/build.gradle b/android/build.gradle
new file mode 100644
index 0000000..3459b08
--- /dev/null
+++ b/android/build.gradle
@@ -0,0 +1,37 @@
+group 'id.flutter.flutter_background_service'
+version '1.0'
+
+buildscript {
+ repositories {
+ google()
+ jcenter()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:4.1.0'
+ }
+}
+
+rootProject.allprojects {
+ repositories {
+ google()
+ jcenter()
+ }
+}
+
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion 31
+
+ defaultConfig {
+ minSdkVersion 16
+ }
+ lintOptions {
+ disable 'InvalidPackage'
+ }
+}
+
+dependencies {
+ implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'
+}
\ No newline at end of file
diff --git a/android/gradle.properties b/android/gradle.properties
new file mode 100644
index 0000000..38c8d45
--- /dev/null
+++ b/android/gradle.properties
@@ -0,0 +1,4 @@
+org.gradle.jvmargs=-Xmx1536M
+android.enableR8=true
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..bc6a58a
--- /dev/null
+++ b/android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Fri Jun 23 08:50:38 CEST 2017
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
diff --git a/android/settings.gradle b/android/settings.gradle
new file mode 100644
index 0000000..1f1100c
--- /dev/null
+++ b/android/settings.gradle
@@ -0,0 +1 @@
+rootProject.name = 'flutter_background_service'
diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..e0d0392
--- /dev/null
+++ b/android/src/main/AndroidManifest.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/src/main/java/id/flutter/flutter_background_service/BackgroundService.java b/android/src/main/java/id/flutter/flutter_background_service/BackgroundService.java
new file mode 100644
index 0000000..dbe12dd
--- /dev/null
+++ b/android/src/main/java/id/flutter/flutter_background_service/BackgroundService.java
@@ -0,0 +1,327 @@
+package id.flutter.flutter_background_service;
+
+import static android.os.Build.VERSION.SDK_INT;
+
+import android.app.AlarmManager;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Build;
+import android.os.IBinder;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.util.Log;
+import androidx.annotation.NonNull;
+import androidx.core.app.AlarmManagerCompat;
+import androidx.core.app.NotificationCompat;
+import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.lang.UnsatisfiedLinkError;
+
+import io.flutter.FlutterInjector;
+import io.flutter.embedding.engine.FlutterEngine;
+import io.flutter.embedding.engine.dart.DartExecutor;
+import io.flutter.embedding.engine.loader.FlutterLoader;
+import io.flutter.plugin.common.JSONMethodCodec;
+import io.flutter.plugin.common.MethodCall;
+import io.flutter.plugin.common.MethodChannel;
+import io.flutter.view.FlutterCallbackInformation;
+
+public class BackgroundService extends Service implements MethodChannel.MethodCallHandler {
+ private static final String TAG = "BackgroundService";
+ private FlutterEngine backgroundEngine;
+ private MethodChannel methodChannel;
+ private DartExecutor.DartCallback dartCallback;
+ private boolean isManuallyStopped = false;
+
+ String notificationTitle = "短信帮手正在后台运行";
+ String notificationContent = "Running";
+ private static final String LOCK_NAME = BackgroundService.class.getName()
+ + ".Lock";
+ public static volatile WakeLock lockStatic = null; // notice static
+
+ synchronized public static PowerManager.WakeLock getLock(Context context) {
+ if (lockStatic == null) {
+ PowerManager mgr = (PowerManager) context
+ .getSystemService(Context.POWER_SERVICE);
+ lockStatic = mgr.newWakeLock(PowerManager.FULL_WAKE_LOCK,
+ LOCK_NAME);
+ lockStatic.setReferenceCounted(true);
+ }
+ return (lockStatic);
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ public static void enqueue(Context context) {
+ Intent intent = new Intent(context, WatchdogReceiver.class);
+ AlarmManager manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+
+ int flags = PendingIntent.FLAG_UPDATE_CURRENT;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ flags |= PendingIntent.FLAG_MUTABLE;
+ }
+
+ PendingIntent pIntent = PendingIntent.getBroadcast(context, 111, intent, flags);
+ AlarmManagerCompat.setAndAllowWhileIdle(manager, AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 5000, pIntent);
+ }
+
+ public void setAutoStartOnBootMode(boolean value) {
+ SharedPreferences pref = getSharedPreferences("id.flutter.background_service", MODE_PRIVATE);
+ pref.edit().putBoolean("auto_start_on_boot", value).apply();
+ }
+
+ public static boolean isAutoStartOnBootMode(Context context) {
+ SharedPreferences pref = context.getSharedPreferences("id.flutter.background_service", MODE_PRIVATE);
+ return pref.getBoolean("auto_start_on_boot", true);
+ }
+
+ public void setForegroundServiceMode(boolean value) {
+ SharedPreferences pref = getSharedPreferences("id.flutter.background_service", MODE_PRIVATE);
+ pref.edit().putBoolean("is_foreground", value).apply();
+ }
+
+ public static boolean isForegroundService(Context context) {
+ SharedPreferences pref = context.getSharedPreferences("id.flutter.background_service", MODE_PRIVATE);
+ return pref.getBoolean("is_foreground", true);
+ }
+
+ public void setManuallyStopped(boolean value) {
+ SharedPreferences pref = getSharedPreferences("id.flutter.background_service", MODE_PRIVATE);
+ pref.edit().putBoolean("is_manually_stopped", value).apply();
+ }
+
+ public static boolean isManuallyStopped(Context context) {
+ SharedPreferences pref = context.getSharedPreferences("id.flutter.background_service", MODE_PRIVATE);
+ return pref.getBoolean("is_manually_stopped", false);
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ createNotificationChannel();
+ notificationContent = "点击即可查看更多选项。";
+ updateNotificationInfo();
+ }
+
+ @Override
+ public void onDestroy() {
+ if (!isManuallyStopped) {
+ enqueue(this);
+ } else {
+ setManuallyStopped(true);
+ }
+ stopForeground(true);
+ isRunning.set(false);
+
+ if (backgroundEngine != null) {
+ backgroundEngine.getServiceControlSurface().detachFromService();
+ backgroundEngine.destroy();
+ backgroundEngine = null;
+ }
+
+ methodChannel = null;
+ dartCallback = null;
+ super.onDestroy();
+ }
+
+ private void createNotificationChannel() {
+ if (SDK_INT >= Build.VERSION_CODES.O) {
+ CharSequence name = "Background Service";
+ String description = "Executing process in background";
+
+ int importance = NotificationManager.IMPORTANCE_LOW;
+ NotificationChannel channel = new NotificationChannel("FOREGROUND_DEFAULT", name, importance);
+ channel.setDescription(description);
+
+ NotificationManager notificationManager = getSystemService(NotificationManager.class);
+ notificationManager.createNotificationChannel(channel);
+ }
+ }
+
+ protected void updateNotificationInfo() {
+ if (isForegroundService(this)) {
+
+ String packageName = getApplicationContext().getPackageName();
+ Intent i = getPackageManager().getLaunchIntentForPackage(packageName);
+
+ int flags = PendingIntent.FLAG_CANCEL_CURRENT;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ flags |= PendingIntent.FLAG_MUTABLE;
+ }
+
+ PendingIntent pi = PendingIntent.getActivity(BackgroundService.this, 99778, i, flags);
+
+ NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this, "FOREGROUND_DEFAULT")
+ .setSmallIcon(R.drawable.ic_bg_service_small)
+ .setAutoCancel(true)
+ .setOngoing(true)
+ .setContentTitle(notificationTitle)
+ .setContentText(notificationContent)
+ .setContentIntent(pi);
+
+ startForeground(99778, mBuilder.build());
+ }
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ setManuallyStopped(false);
+ enqueue(this);
+ runService();
+
+ return START_STICKY;
+ }
+
+ AtomicBoolean isRunning = new AtomicBoolean(false);
+
+ private void runService() {
+ try {
+ Log.d(TAG, "runService");
+ if (isRunning.get() || (backgroundEngine != null && !backgroundEngine.getDartExecutor().isExecutingDart()))
+ return;
+
+ if (lockStatic == null){
+ getLock(getApplicationContext()).acquire(10*60*1000L /*10 minutes*/);
+ }
+
+ updateNotificationInfo();
+
+ SharedPreferences pref = getSharedPreferences("id.flutter.background_service", MODE_PRIVATE);
+ long entrypointHandle = pref.getLong("entrypoint_handle", 0);
+
+ FlutterLoader flutterLoader = FlutterInjector.instance().flutterLoader();
+ // initialize flutter if it's not initialized yet
+ if (!flutterLoader.initialized()) {
+ flutterLoader.startInitialization(getApplicationContext());
+ }
+
+ flutterLoader.ensureInitializationComplete(getApplicationContext(), null);
+ FlutterCallbackInformation callback = FlutterCallbackInformation.lookupCallbackInformation(entrypointHandle);
+ if (callback == null) {
+ Log.e(TAG, "callback handle not found");
+ return;
+ }
+
+ isRunning.set(true);
+ backgroundEngine = new FlutterEngine(this);
+ backgroundEngine.getServiceControlSurface().attachToService(BackgroundService.this, null, isForegroundService(this));
+
+ methodChannel = new MethodChannel(backgroundEngine.getDartExecutor().getBinaryMessenger(), "id.flutter/background_service_android_bg", JSONMethodCodec.INSTANCE);
+ methodChannel.setMethodCallHandler(this);
+
+ dartCallback = new DartExecutor.DartCallback(getAssets(), flutterLoader.findAppBundlePath(), callback);
+ backgroundEngine.getDartExecutor().executeDartCallback(dartCallback);
+ } catch (UnsatisfiedLinkError e) {
+ notificationContent = "Error " + e.getMessage();
+ updateNotificationInfo();
+
+ Log.w(TAG, "UnsatisfiedLinkError: After a reboot this may happen for a short period and it is ok to ignore then!" + e.getMessage());
+ }
+ }
+
+ public void receiveData(JSONObject data) {
+ if (methodChannel != null) {
+ try {
+ methodChannel.invokeMethod("onReceiveData", data);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ @Override
+ public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
+ String method = call.method;
+
+ try {
+ if (method.equalsIgnoreCase("getHandler")) {
+ SharedPreferences pref = getSharedPreferences("id.flutter.background_service", MODE_PRIVATE);
+ long backgroundHandle = pref.getLong("background_handle", 0);
+ result.success(backgroundHandle);
+
+ if (lockStatic != null) {
+ lockStatic.release();
+ lockStatic = null;
+ }
+ return;
+ }
+
+ if (method.equalsIgnoreCase("setNotificationInfo")) {
+ JSONObject arg = (JSONObject) call.arguments;
+ if (arg.has("title")) {
+ notificationTitle = arg.getString("title");
+ notificationContent = arg.getString("content");
+ updateNotificationInfo();
+ result.success(true);
+ }
+ return;
+ }
+
+ if (method.equalsIgnoreCase("setAutoStartOnBootMode")) {
+ JSONObject arg = (JSONObject) call.arguments;
+ boolean value = arg.getBoolean("value");
+ setAutoStartOnBootMode(value);
+ result.success(true);
+ return;
+ }
+
+ if (method.equalsIgnoreCase("setForegroundMode")) {
+ JSONObject arg = (JSONObject) call.arguments;
+ boolean value = arg.getBoolean("value");
+ setForegroundServiceMode(value);
+ if (value) {
+ updateNotificationInfo();
+ } else {
+ stopForeground(true);
+ }
+
+ result.success(true);
+ return;
+ }
+
+ if (method.equalsIgnoreCase("stopService")) {
+ isManuallyStopped = true;
+ Intent intent = new Intent(this, WatchdogReceiver.class);
+
+ int flags = PendingIntent.FLAG_CANCEL_CURRENT;
+ if (SDK_INT >= Build.VERSION_CODES.S) {
+ flags |= PendingIntent.FLAG_MUTABLE;
+ }
+
+ PendingIntent pi = PendingIntent.getBroadcast(getApplicationContext(), 111, intent, flags);
+
+ AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
+ alarmManager.cancel(pi);
+ stopSelf();
+ result.success(true);
+ return;
+ }
+
+ if (method.equalsIgnoreCase("sendData")) {
+ LocalBroadcastManager manager = LocalBroadcastManager.getInstance(this);
+ Intent intent = new Intent("id.flutter/background_service");
+ intent.putExtra("data", call.arguments.toString());
+ manager.sendBroadcast(intent);
+ result.success(true);
+ return;
+ }
+ } catch (JSONException e) {
+ Log.e(TAG, e.getMessage());
+ e.printStackTrace();
+ }
+
+ result.notImplemented();
+ }
+}
diff --git a/android/src/main/java/id/flutter/flutter_background_service/BootReceiver.java b/android/src/main/java/id/flutter/flutter_background_service/BootReceiver.java
new file mode 100644
index 0000000..7059485
--- /dev/null
+++ b/android/src/main/java/id/flutter/flutter_background_service/BootReceiver.java
@@ -0,0 +1,30 @@
+package id.flutter.flutter_background_service;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+
+import androidx.core.content.ContextCompat;
+
+import static android.content.Context.MODE_PRIVATE;
+
+
+public class BootReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ SharedPreferences pref = context.getSharedPreferences("id.flutter.background_service", MODE_PRIVATE);
+ boolean autoStart = pref.getBoolean("auto_start_on_boot",true);
+ if(autoStart) {
+ if (BackgroundService.lockStatic == null){
+ BackgroundService.getLock(context).acquire(10*60*1000L /*10 minutes*/);
+ }
+
+ if (BackgroundService.isForegroundService(context)) {
+ ContextCompat.startForegroundService(context, new Intent(context, BackgroundService.class));
+ } else {
+ context.startService(new Intent(context, BackgroundService.class));
+ }
+ }
+ }
+}
diff --git a/android/src/main/java/id/flutter/flutter_background_service/FlutterBackgroundServicePlugin.java b/android/src/main/java/id/flutter/flutter_background_service/FlutterBackgroundServicePlugin.java
new file mode 100644
index 0000000..cb8f4bc
--- /dev/null
+++ b/android/src/main/java/id/flutter/flutter_background_service/FlutterBackgroundServicePlugin.java
@@ -0,0 +1,183 @@
+package id.flutter.flutter_background_service;
+
+import static android.content.Context.MODE_PRIVATE;
+
+import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.core.content.ContextCompat;
+import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import io.flutter.embedding.engine.plugins.FlutterPlugin;
+import io.flutter.embedding.engine.plugins.service.ServiceAware;
+import io.flutter.embedding.engine.plugins.service.ServicePluginBinding;
+import io.flutter.plugin.common.MethodCall;
+import io.flutter.plugin.common.MethodChannel;
+import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
+import io.flutter.plugin.common.MethodChannel.Result;
+import io.flutter.plugin.common.PluginRegistry.Registrar;
+import io.flutter.plugin.common.JSONMethodCodec;
+
+/** FlutterBackgroundServicePlugin */
+public class FlutterBackgroundServicePlugin extends BroadcastReceiver implements FlutterPlugin, MethodCallHandler, ServiceAware {
+ private static final String TAG = "BackgroundServicePlugin";
+ private static final List _instances = new ArrayList<>();
+
+ public FlutterBackgroundServicePlugin() {
+ _instances.add(this);
+ }
+
+ private MethodChannel channel;
+ private Context context;
+ private BackgroundService service;
+
+ @Override
+ public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
+ this.context = flutterPluginBinding.getApplicationContext();
+ LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this.context);
+ localBroadcastManager.registerReceiver(this, new IntentFilter("id.flutter/background_service"));
+
+ channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "id.flutter/background_service_android", JSONMethodCodec.INSTANCE);
+ channel.setMethodCallHandler(this);
+ }
+
+ public static void registerWith(Registrar registrar) {
+ LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(registrar.context());
+ final FlutterBackgroundServicePlugin plugin = new FlutterBackgroundServicePlugin();
+ localBroadcastManager.registerReceiver(plugin, new IntentFilter("id.flutter/background_service"));
+
+ final MethodChannel channel = new MethodChannel(registrar.messenger(), "id.flutter/background_service_android", JSONMethodCodec.INSTANCE);
+ channel.setMethodCallHandler(plugin);
+ plugin.channel = channel;
+ }
+
+ private static void configure(Context context, long entrypointHandle, long backgroundHandle, boolean isForeground, boolean autoStartOnBoot) {
+ SharedPreferences pref = context.getSharedPreferences("id.flutter.background_service", MODE_PRIVATE);
+ pref.edit()
+ .putLong("entrypoint_handle", entrypointHandle)
+ .putLong("background_handle", backgroundHandle)
+ .putBoolean("is_foreground", isForeground)
+ .putBoolean("auto_start_on_boot", autoStartOnBoot)
+ .apply();
+ }
+
+ private void start() {
+ BackgroundService.enqueue(context);
+ boolean isForeground = BackgroundService.isForegroundService(context);
+ Intent intent = new Intent(context, BackgroundService.class);
+ if (isForeground) {
+ ContextCompat.startForegroundService(context, intent);
+ } else {
+ context.startService(intent);
+ }
+ }
+
+ @Override
+ public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
+ String method = call.method;
+ JSONObject arg = (JSONObject) call.arguments;
+
+ try {
+ if ("configure".equals(method)) {
+ long entrypointHandle = arg.getLong("entrypoint_handle");
+ long backgroundHandle = arg.getLong("background_handle");
+ boolean isForeground = arg.getBoolean("is_foreground_mode");
+ boolean autoStartOnBoot = arg.getBoolean("auto_start_on_boot");
+
+ configure(context, entrypointHandle, backgroundHandle, isForeground, autoStartOnBoot);
+ if (autoStartOnBoot) {
+ start();
+ }
+
+ result.success(true);
+ return;
+ }
+
+ if ("start".equals(method)) {
+ start();
+ result.success(true);
+ return;
+ }
+
+ if (method.equalsIgnoreCase("sendData")) {
+ for (FlutterBackgroundServicePlugin plugin : _instances) {
+ if (plugin.service != null) {
+ plugin.service.receiveData((JSONObject) call.arguments);
+ break;
+ }
+ }
+
+ result.success(true);
+ return;
+ }
+
+ if (method.equalsIgnoreCase("isServiceRunning")) {
+ ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+ for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
+ if (BackgroundService.class.getName().equals(service.service.getClassName())) {
+ result.success(true);
+ return;
+ }
+ }
+ result.success(false);
+ return;
+ }
+
+ result.notImplemented();
+ } catch (Exception e) {
+ result.error("100", "Failed read arguments", null);
+ }
+ }
+
+ @Override
+ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
+ channel.setMethodCallHandler(null);
+
+ LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this.context);
+ localBroadcastManager.unregisterReceiver(this);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction() == null) return;
+
+ if (intent.getAction().equalsIgnoreCase("id.flutter/background_service")) {
+ String data = intent.getStringExtra("data");
+ try {
+ JSONObject jData = new JSONObject(data);
+ if (channel != null) {
+ channel.invokeMethod("onReceiveData", jData);
+ }
+ } catch (JSONException e) {
+ e.printStackTrace();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ @Override
+ public void onAttachedToService(@NonNull ServicePluginBinding binding) {
+ Log.d(TAG, "onAttachedToService");
+
+ this.service = (BackgroundService) binding.getService();
+ }
+
+ @Override
+ public void onDetachedFromService() {
+ this.service = null;
+ Log.d(TAG, "onDetachedFromService");
+ }
+}
diff --git a/android/src/main/java/id/flutter/flutter_background_service/WatchdogReceiver.java b/android/src/main/java/id/flutter/flutter_background_service/WatchdogReceiver.java
new file mode 100644
index 0000000..043558a
--- /dev/null
+++ b/android/src/main/java/id/flutter/flutter_background_service/WatchdogReceiver.java
@@ -0,0 +1,20 @@
+package id.flutter.flutter_background_service;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+import androidx.core.content.ContextCompat;
+
+public class WatchdogReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if(!BackgroundService.isManuallyStopped(context)) {
+ if (BackgroundService.isForegroundService(context)) {
+ ContextCompat.startForegroundService(context, new Intent(context, BackgroundService.class));
+ } else {
+ context.startService(new Intent(context, BackgroundService.class));
+ }
+ }
+ }
+}
diff --git a/android/src/main/res/drawable-anydpi-v24/ic_bg_service_small.xml b/android/src/main/res/drawable-anydpi-v24/ic_bg_service_small.xml
new file mode 100644
index 0000000..39d15ef
--- /dev/null
+++ b/android/src/main/res/drawable-anydpi-v24/ic_bg_service_small.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
diff --git a/android/src/main/res/drawable-hdpi/ic_bg_service_small.png b/android/src/main/res/drawable-hdpi/ic_bg_service_small.png
new file mode 100644
index 0000000..4f4a5c8
Binary files /dev/null and b/android/src/main/res/drawable-hdpi/ic_bg_service_small.png differ
diff --git a/android/src/main/res/drawable-mdpi/ic_bg_service_small.png b/android/src/main/res/drawable-mdpi/ic_bg_service_small.png
new file mode 100644
index 0000000..a76c638
Binary files /dev/null and b/android/src/main/res/drawable-mdpi/ic_bg_service_small.png differ
diff --git a/android/src/main/res/drawable-xhdpi/ic_bg_service_small.png b/android/src/main/res/drawable-xhdpi/ic_bg_service_small.png
new file mode 100644
index 0000000..8e91e9c
Binary files /dev/null and b/android/src/main/res/drawable-xhdpi/ic_bg_service_small.png differ
diff --git a/android/src/main/res/drawable-xxhdpi/ic_bg_service_small.png b/android/src/main/res/drawable-xxhdpi/ic_bg_service_small.png
new file mode 100644
index 0000000..6c378c6
Binary files /dev/null and b/android/src/main/res/drawable-xxhdpi/ic_bg_service_small.png differ
diff --git a/flutter_background_service.iml b/flutter_background_service.iml
new file mode 100644
index 0000000..429df7d
--- /dev/null
+++ b/flutter_background_service.iml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lib/flutter_background_service_android.dart b/lib/flutter_background_service_android.dart
new file mode 100644
index 0000000..92d5a47
--- /dev/null
+++ b/lib/flutter_background_service_android.dart
@@ -0,0 +1,186 @@
+import 'dart:async';
+import 'dart:ui';
+
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter_background_service_platform_interface/flutter_background_service_platform_interface.dart';
+
+Future _entrypoint() async {
+ WidgetsFlutterBinding.ensureInitialized();
+ final service = AndroidServiceInstance._();
+ final int handle = await service._getHandler();
+ final callbackHandle = CallbackHandle.fromRawHandle(handle);
+ final onStart = PluginUtilities.getCallbackFromHandle(callbackHandle);
+ if (onStart != null) {
+ onStart(service);
+ }
+}
+
+class FlutterBackgroundServiceAndroid extends FlutterBackgroundServicePlatform {
+ /// Registers this class as the default instance of [FlutterBackgroundServicePlatform].
+ static void registerWith() {
+ FlutterBackgroundServicePlatform.instance =
+ FlutterBackgroundServiceAndroid();
+ }
+
+ static const MethodChannel _channel = const MethodChannel(
+ 'id.flutter/background_service_android',
+ JSONMethodCodec(),
+ );
+
+ Future _handle(MethodCall call) async {
+ switch (call.method) {
+ case "onReceiveData":
+ _controller.sink.add(call.arguments);
+ break;
+ default:
+ }
+
+ return true;
+ }
+
+ Future start() async {
+ final result = await _channel.invokeMethod('start');
+ return result ?? false;
+ }
+
+ Future configure({
+ required IosConfiguration iosConfiguration,
+ required AndroidConfiguration androidConfiguration,
+ }) async {
+ _channel.setMethodCallHandler(_handle);
+
+ final CallbackHandle? entryPointHandle =
+ PluginUtilities.getCallbackHandle(_entrypoint);
+
+ final CallbackHandle? handle =
+ PluginUtilities.getCallbackHandle(androidConfiguration.onStart);
+
+ if (entryPointHandle == null || handle == null) {
+ return false;
+ }
+
+ final result = await _channel.invokeMethod(
+ "configure",
+ {
+ "entrypoint_handle": entryPointHandle.toRawHandle(),
+ "background_handle": handle.toRawHandle(),
+ "is_foreground_mode": androidConfiguration.isForegroundMode,
+ "auto_start_on_boot": androidConfiguration.autoStart,
+ },
+ );
+
+ return result ?? false;
+ }
+
+ Future isServiceRunning() async {
+ var result = await _channel.invokeMethod("isServiceRunning");
+ return result ?? false;
+ }
+
+ final _controller = StreamController.broadcast(sync: true);
+
+ void dispose() {
+ _controller.close();
+ }
+
+ @override
+ void invoke(String method, [Map? args]) {
+ _channel.invokeMethod("sendData", {
+ 'method': method,
+ 'args': args,
+ });
+ }
+
+ @override
+ Stream