From 89db41ccf92d5fee5cdc0dfa441bf110e318e6fc Mon Sep 17 00:00:00 2001 From: kitolog Date: Fri, 7 Dec 2018 17:18:56 +0300 Subject: [PATCH 1/7] Updated package.json --- CHANGELOG.md | 4 +--- README.md | 3 ++- package.json | 8 ++++---- plugin.xml | 6 +++--- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 16414726..cbd2082c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,6 @@ ## ChangeLog #### Version 0.7.3 (not yet released) -- Check if screen is off on Android -- Wake-up device on Android -- Unlock device on Android +- fixed iOS crashes #### Version 0.7.2 (02.02.2017) - Fixed app freeze on iOS using wkwebview-engine diff --git a/README.md b/README.md index bde896a8..f62be5c8 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,9 @@ SAMPLE APP :point_right:

-Cordova Background Plugin [![npm version](https://badge.fury.io/js/cordova-plugin-background-mode.svg)](http://badge.fury.io/js/cordova-plugin-background-mode) [![Build Status](https://travis-ci.org/katzer/cordova-plugin-background-mode.svg?branch=master)](https://travis-ci.org/katzer/cordova-plugin-background-mode) [![codebeat badge](https://codebeat.co/badges/49709283-b313-4ced-8630-f520baaec7b5)](https://codebeat.co/projects/github-com-katzer-cordova-plugin-background-mode) +Cordova Background Plugin iOS Fix [![npm version](https://badge.fury.io/js/cordova-plugin-background-mode.svg)](http://badge.fury.io/js/cordova-plugin-background-mode) [![Build Status](https://travis-ci.org/katzer/cordova-plugin-background-mode.svg?branch=master)](https://travis-ci.org/katzer/cordova-plugin-background-mode) [![codebeat badge](https://codebeat.co/badges/49709283-b313-4ced-8630-f520baaec7b5)](https://codebeat.co/projects/github-com-katzer-cordova-plugin-background-mode) ========================= +[cordova-plugin-background-mode](https://github.com/katzer/cordova-plugin-background-mode) fork. Fixed iOS crashes Plugin for the [Cordova][cordova] framework to perform infinite background execution. diff --git a/package.json b/package.json index ebbedf34..9589a4df 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,9 @@ { - "name": "cordova-plugin-background-mode", - "version": "0.7.2", - "description": "Prevent apps from going to sleep in background.", + "name": "cordova-plugin-background-mode-fixed", + "version": "0.7.3", + "description": "Prevent apps from going to sleep in background. iOS fix.", "cordova": { - "id": "cordova-plugin-background-mode", + "id": "cordova-plugin-background-mode-fixed", "platforms": [ "ios", "android", diff --git a/plugin.xml b/plugin.xml index 52bd90e9..718c2ab4 100644 --- a/plugin.xml +++ b/plugin.xml @@ -2,12 +2,12 @@ + id="cordova-plugin-background-mode-fixed" + version="0.7.3"> BackgroundMode - Prevent apps from going to sleep in background. + Prevent apps from going to sleep in background. iOS fix. https://github.com/katzer/cordova-plugin-background-mode.git From 67ab6e840314703d40faabd1a189ab8bd6c0b807 Mon Sep 17 00:00:00 2001 From: kitolog Date: Mon, 8 Apr 2019 13:56:44 +0300 Subject: [PATCH 2/7] Version 0.7.4. Merged Android updates, added iOS 12.2 fix --- .gitignore | 23 ++ CHANGELOG.md | 9 +- README.md | 1 + package.json | 2 +- plugin.xml | 12 +- src/android/BackgroundExt.java | 332 --------------------- src/android/BackgroundMode.java | 173 +++++------ src/android/BackgroundModeExt.java | 445 +++++++++++++++++++++++++++++ src/android/ForegroundService.java | 122 +++++--- src/browser/BackgroundModeProxy.js | 30 +- src/ios/APPBackgroundMode.m | 2 +- src/windows/BackgroundModeProxy.js | 36 +-- www/background-mode.js | 238 ++++++++------- 13 files changed, 827 insertions(+), 598 deletions(-) create mode 100644 .gitignore delete mode 100644 src/android/BackgroundExt.java create mode 100644 src/android/BackgroundModeExt.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..8aab9a9d --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +# Specifies intentionally untracked files to ignore when using Git +# http://git-scm.com/docs/gitignore + +*~ +*.sw[mnpcod] +*.log +*.tmp +*.tmp.* +log.txt +*.sublime-project +*.sublime-workspace +.vscode/ +npm-debug.log* + +.idea/ +.sourcemaps/ +.sass-cache/ +.tmp/ +tmp/ +$RECYCLE.BIN/ + +.DS_Store +Thumbs.db diff --git a/CHANGELOG.md b/CHANGELOG.md index cbd2082c..9d91858b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ ## ChangeLog -#### Version 0.7.3 (not yet released) +#### Version 0.7.4 +- Fixed iOS 12.2 crashes +- Merged original repository Android source updates + +#### Version 0.7.3 - fixed iOS crashes +- Check if screen is off on Android +- Wake-up device on Android +- Unlock device on Android #### Version 0.7.2 (02.02.2017) - Fixed app freeze on iOS using wkwebview-engine diff --git a/README.md b/README.md index f62be5c8..d1c760f1 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@

Cordova Background Plugin iOS Fix [![npm version](https://badge.fury.io/js/cordova-plugin-background-mode.svg)](http://badge.fury.io/js/cordova-plugin-background-mode) [![Build Status](https://travis-ci.org/katzer/cordova-plugin-background-mode.svg?branch=master)](https://travis-ci.org/katzer/cordova-plugin-background-mode) [![codebeat badge](https://codebeat.co/badges/49709283-b313-4ced-8630-f520baaec7b5)](https://codebeat.co/projects/github-com-katzer-cordova-plugin-background-mode) +Implemented ios 12.2 fix ========================= [cordova-plugin-background-mode](https://github.com/katzer/cordova-plugin-background-mode) fork. Fixed iOS crashes diff --git a/package.json b/package.json index 9589a4df..ef185e90 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cordova-plugin-background-mode-fixed", - "version": "0.7.3", + "version": "0.7.4", "description": "Prevent apps from going to sleep in background. iOS fix.", "cordova": { "id": "cordova-plugin-background-mode-fixed", diff --git a/plugin.xml b/plugin.xml index 718c2ab4..79654026 100644 --- a/plugin.xml +++ b/plugin.xml @@ -3,7 +3,7 @@ + version="0.7.4"> BackgroundMode @@ -59,9 +59,11 @@ - - + + + + + @@ -82,7 +84,7 @@ target-dir="src/de/appplant/cordova/plugin/background" /> cordova; - - // Weak reference to the cordova web view passed by the plugin - private final WeakReference webView; - - private PowerManager.WakeLock wakeLock; - - /** - * Initialize the extension to perform non-background related tasks. - * - * @param plugin The cordova plugin. - */ - private BackgroundExt(CordovaPlugin plugin) { - this.cordova = new WeakReference(plugin.cordova); - this.webView = new WeakReference(plugin.webView); - } - - /** - * Executes the request asynchronous. - * - * @param plugin The cordova plugin. - * @param action The action to execute. - * @param callback The callback context used when - * calling back into JavaScript. - */ - @SuppressWarnings("UnusedParameters") - static void execute (CordovaPlugin plugin, final String action, - final CallbackContext callback) { - - final BackgroundExt ext = new BackgroundExt(plugin); - - plugin.cordova.getThreadPool().execute(new Runnable() { - @Override - public void run() { - ext.execute(action, callback); - } - }); - } - - // codebeat:disable[ABC] - - /** - * Executes the request. - * - * @param action The action to execute. - * @param callback The callback context used when - * calling back into JavaScript. - */ - private void execute (String action, CallbackContext callback) { - - if (action.equalsIgnoreCase("optimizations")) { - disableWebViewOptimizations(); - } - - if (action.equalsIgnoreCase("background")) { - moveToBackground(); - } - - if (action.equalsIgnoreCase("foreground")) { - moveToForeground(); - } - - if (action.equalsIgnoreCase("tasklist")) { - excludeFromTaskList(); - } - - if (action.equalsIgnoreCase("dimmed")) { - isDimmed(callback); - } - - if (action.equalsIgnoreCase("wakeup")) { - wakeup(); - } - - if (action.equalsIgnoreCase("unlock")) { - wakeup(); - unlock(); - } - } - - // codebeat:enable[ABC] - - /** - * Move app to background. - */ - private void moveToBackground() { - Intent intent = new Intent(Intent.ACTION_MAIN); - - intent.addCategory(Intent.CATEGORY_HOME); - getApp().startActivity(intent); - } - - /** - * Move app to foreground. - */ - private void moveToForeground() { - Activity app = getApp(); - Intent intent = getLaunchIntent(); - - intent.addFlags( - Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | - Intent.FLAG_ACTIVITY_SINGLE_TOP); - - app.startActivity(intent); - } - - /** - * Enable GPS position tracking while in background. - */ - private void disableWebViewOptimizations() { - Thread thread = new Thread(){ - public void run() { - try { - Thread.sleep(1000); - getApp().runOnUiThread(new Runnable() { - @Override - public void run() { - View view = webView.get().getEngine().getView(); - - try { - Class.forName("org.crosswalk.engine.XWalkCordovaView") - .getMethod("onShow") - .invoke(view); - } catch (Exception e){ - view.dispatchWindowVisibilityChanged(View.VISIBLE); - } - } - }); - } catch (InterruptedException e) { - // do nothing - } - } - }; - - thread.start(); - } - - /** - * Exclude the app from the recent tasks list. - */ - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - private void excludeFromTaskList() { - ActivityManager am = (ActivityManager) getService(ACTIVITY_SERVICE); - - if (am == null || Build.VERSION.SDK_INT < 21) - return; - - List tasks = am.getAppTasks(); - - if (tasks == null || tasks.isEmpty()) - return; - - tasks.get(0).setExcludeFromRecents(true); - } - - /** - * Invoke the callback with information if the screen is on. - * - * @param callback The callback to invoke. - */ - @SuppressWarnings("deprecation") - private void isDimmed(CallbackContext callback) { - PluginResult result = new PluginResult(Status.OK, isDimmed()); - callback.sendPluginResult(result); - } - - /** - * If the screen is active. - */ - @SuppressWarnings("deprecation") - private boolean isDimmed() { - PowerManager pm = (PowerManager) getService(POWER_SERVICE); - - if (Build.VERSION.SDK_INT < 20) { - return !pm.isScreenOn(); - } - - return !pm.isInteractive(); - } - - /** - * Wakes up the device if the screen isn't still on. - */ - private void wakeup() { - try { - acquireWakeLock(); - } catch (Exception e) { - releaseWakeLock(); - } - } - - /** - * Unlocks the device even with password protection. - */ - private void unlock() { - Intent intent = getLaunchIntent(); - getApp().startActivity(intent); - } - - /** - * Acquire a wake lock to wake up the device. - */ - private void acquireWakeLock() { - PowerManager pm = (PowerManager) getService(POWER_SERVICE); - - releaseWakeLock(); - - if (!isDimmed()) { - return; - } - - int level = PowerManager.SCREEN_DIM_WAKE_LOCK | - PowerManager.ACQUIRE_CAUSES_WAKEUP; - - wakeLock = pm.newWakeLock(level, "BackgroundModeExt"); - wakeLock.setReferenceCounted(false); - wakeLock.acquire(1000); - } - - /** - * Releases the previously acquire wake lock. - */ - private void releaseWakeLock() { - if (wakeLock != null && wakeLock.isHeld()) { - wakeLock.release(); - wakeLock = null; - } - } - - /** - * Add required flags to the window to unlock/wakeup the device. - */ - static void addWindowFlags(Activity app) { - final Window window = app.getWindow(); - - app.runOnUiThread(new Runnable() { - public void run() { - window.addFlags( - FLAG_ALLOW_LOCK_WHILE_SCREEN_ON | - FLAG_SHOW_WHEN_LOCKED | - FLAG_TURN_SCREEN_ON | - FLAG_DISMISS_KEYGUARD - ); - } - }); - } - - /** - * The activity referenced by cordova. - * - * @return The main activity of the app. - */ - Activity getApp() { - return cordova.get().getActivity(); - } - - /** - * The launch intent for the main activity. - */ - private Intent getLaunchIntent() { - Context app = getApp().getApplicationContext(); - String pkgName = app.getPackageName(); - - return app.getPackageManager().getLaunchIntentForPackage(pkgName); - } - - /** - * Get the requested system service by name. - * - * @param name The name of the service. - * - * @return The service instance. - */ - private Object getService(String name) { - return getApp().getSystemService(name); - } - -} diff --git a/src/android/BackgroundMode.java b/src/android/BackgroundMode.java index 3ade663e..cac418af 100644 --- a/src/android/BackgroundMode.java +++ b/src/android/BackgroundMode.java @@ -1,22 +1,22 @@ /* - Copyright 2013-2017 appPlant GmbH - - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. + Copyright 2013 Sebastián Katzer + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. */ package de.appplant.cordova.plugin.background; @@ -30,23 +30,20 @@ Licensed to the Apache Software Foundation (ASF) under one import org.apache.cordova.CallbackContext; import org.apache.cordova.CordovaPlugin; import org.json.JSONArray; -import org.json.JSONException; import org.json.JSONObject; import de.appplant.cordova.plugin.background.ForegroundService.ForegroundBinder; import static android.content.Context.BIND_AUTO_CREATE; +import static de.appplant.cordova.plugin.background.BackgroundModeExt.clearKeyguardFlags; public class BackgroundMode extends CordovaPlugin { // Event types for callbacks - private enum Event { - ACTIVATE, DEACTIVATE, FAILURE - } + private enum Event { ACTIVATE, DEACTIVATE, FAILURE } // Plugin namespace - private static final String JS_NAMESPACE = - "cordova.plugins.backgroundMode"; + private static final String JS_NAMESPACE = "cordova.plugins.backgroundMode"; // Flag indicates if the app is in background or foreground private boolean inBackground = false; @@ -64,26 +61,22 @@ private enum Event { private ForegroundService service; // Used to (un)bind the service to with the activity - private final ServiceConnection connection = new ServiceConnection() { + private final ServiceConnection connection = new ServiceConnection() + { @Override - public void onServiceConnected(ComponentName name, IBinder service) { + public void onServiceConnected (ComponentName name, IBinder service) + { ForegroundBinder binder = (ForegroundBinder) service; BackgroundMode.this.service = binder.getService(); } @Override - public void onServiceDisconnected(ComponentName name) { + public void onServiceDisconnected (ComponentName name) + { fireEvent(Event.FAILURE, "'service disconnected'"); } }; - @Override - protected void pluginInitialize() { - BackgroundExt.addWindowFlags(cordova.getActivity()); - } - - // codebeat:disable[ABC] - /** * Executes the request. * @@ -93,47 +86,59 @@ protected void pluginInitialize() { * calling back into JavaScript. * * @return Returning false results in a "MethodNotFound" error. - * - * @throws JSONException */ @Override public boolean execute (String action, JSONArray args, - CallbackContext callback) throws JSONException { - - if (action.equalsIgnoreCase("configure")) { - configure(args.getJSONObject(0), args.getBoolean(1)); - callback.success(); - return true; - } - - if (action.equalsIgnoreCase("enable")) { - enableMode(); - callback.success(); - return true; + CallbackContext callback) + { + boolean validAction = true; + + switch (action) + { + case "configure": + configure(args.optJSONObject(0), args.optBoolean(1)); + break; + case "enable": + enableMode(); + break; + case "disable": + disableMode(); + break; + default: + validAction = false; } - if (action.equalsIgnoreCase("disable")) { - disableMode(); + if (validAction) { callback.success(); - return true; + } else { + callback.error("Invalid action: " + action); } - BackgroundExt.execute(this, action, callback); - return true; + return validAction; } - // codebeat:enable[ABC] - /** * Called when the system is about to start resuming a previous activity. * * @param multitasking Flag indicating if multitasking is turned on for app. */ @Override - public void onPause(boolean multitasking) { - super.onPause(multitasking); - inBackground = true; - startService(); + public void onPause(boolean multitasking) + { + try { + inBackground = true; + startService(); + } finally { + clearKeyguardFlags(cordova.getActivity()); + } + } + + /** + * Called when the activity is no longer visible to the user. + */ + @Override + public void onStop () { + clearKeyguardFlags(cordova.getActivity()); } /** @@ -142,8 +147,8 @@ public void onPause(boolean multitasking) { * @param multitasking Flag indicating if multitasking is turned on for app. */ @Override - public void onResume(boolean multitasking) { - super.onResume(multitasking); + public void onResume (boolean multitasking) + { inBackground = false; stopService(); } @@ -152,16 +157,17 @@ public void onResume(boolean multitasking) { * Called when the activity will be destroyed. */ @Override - public void onDestroy() { + public void onDestroy() + { stopService(); - super.onDestroy(); android.os.Process.killProcess(android.os.Process.myPid()); } /** * Enable the background mode. */ - private void enableMode() { + private void enableMode() + { isDisabled = false; if (inBackground) { @@ -172,7 +178,8 @@ private void enableMode() { /** * Disable the background mode. */ - private void disableMode() { + private void disableMode() + { stopService(); isDisabled = true; } @@ -183,7 +190,8 @@ private void disableMode() { * @param settings The settings * @param update A truthy value means to update the running service. */ - private void configure(JSONObject settings, boolean update) { + private void configure(JSONObject settings, boolean update) + { if (update) { updateNotification(settings); } else { @@ -196,17 +204,15 @@ private void configure(JSONObject settings, boolean update) { * * @param settings The new default settings */ - private void setDefaultSettings(JSONObject settings) { + private void setDefaultSettings(JSONObject settings) + { defaultSettings = settings; } /** - * The settings for the new/updated notification. - * - * @return - * updateSettings if set or default settings + * Returns the settings for the new/updated notification. */ - protected static JSONObject getSettings() { + static JSONObject getSettings () { return defaultSettings; } @@ -215,7 +221,8 @@ protected static JSONObject getSettings() { * * @param settings The config settings */ - private void updateNotification(JSONObject settings) { + private void updateNotification(JSONObject settings) + { if (isBind) { service.updateNotification(settings); } @@ -225,7 +232,8 @@ private void updateNotification(JSONObject settings) { * Bind the activity to a background service and put them into foreground * state. */ - private void startService() { + private void startService() + { Activity context = cordova.getActivity(); if (isDisabled || isBind) @@ -248,12 +256,12 @@ private void startService() { * Bind the activity to a background service and put them into foreground * state. */ - private void stopService() { + private void stopService() + { Activity context = cordova.getActivity(); Intent intent = new Intent(context, ForegroundService.class); - if (!isBind) - return; + if (!isBind) return; fireEvent(Event.DEACTIVATE, null); context.unbindService(connection); @@ -268,7 +276,8 @@ private void stopService() { * @param event The name of the event * @param params Optional arguments for the event */ - private void fireEvent (Event event, String params) { + private void fireEvent (Event event, String params) + { String eventName = event.name().toLowerCase(); Boolean active = event == Event.ACTIVATE; @@ -283,12 +292,6 @@ private void fireEvent (Event event, String params) { final String js = str; - cordova.getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - webView.loadUrl("javascript:" + js); - } - }); + cordova.getActivity().runOnUiThread(() -> webView.loadUrl("javascript:" + js)); } - } diff --git a/src/android/BackgroundModeExt.java b/src/android/BackgroundModeExt.java new file mode 100644 index 00000000..ed765984 --- /dev/null +++ b/src/android/BackgroundModeExt.java @@ -0,0 +1,445 @@ +/* + Copyright 2013 Sebastián Katzer + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +package de.appplant.cordova.plugin.background; + +import android.annotation.SuppressLint; +import android.annotation.TargetApi; +import android.app.Activity; +import android.app.ActivityManager; +import android.app.ActivityManager.AppTask; +import android.app.AlertDialog; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Build; +import android.os.PowerManager; +import android.view.View; + +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.PluginResult; +import org.apache.cordova.PluginResult.Status; +import org.json.JSONArray; +import org.json.JSONObject; + +import java.util.Arrays; +import java.util.List; + +import static android.R.string.cancel; +import static android.R.string.ok; +import static android.R.style.Theme_DeviceDefault_Light_Dialog; +import static android.content.Context.ACTIVITY_SERVICE; +import static android.content.Context.POWER_SERVICE; +import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY; +import static android.os.Build.VERSION.SDK_INT; +import static android.os.Build.VERSION_CODES.M; +import static android.provider.Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS; +import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON; +import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD; +import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; +import static android.view.WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON; + +/** + * Implements extended functions around the main purpose + * of infinite execution in the background. + */ +public class BackgroundModeExt extends CordovaPlugin { + + // To keep the device awake + private PowerManager.WakeLock wakeLock; + + /** + * Executes the request. + * + * @param action The action to execute. + * @param args The exec() arguments. + * @param callback The callback context used when + * calling back into JavaScript. + * + * @return Returning false results in a "MethodNotFound" error. + */ + @Override + public boolean execute (String action, JSONArray args, + CallbackContext callback) + { + boolean validAction = true; + + switch (action) + { + case "battery": + disableBatteryOptimizations(); + break; + case "webview": + disableWebViewOptimizations(); + break; + case "appstart": + openAppStart(args.opt(0)); + break; + case "background": + moveToBackground(); + break; + case "foreground": + moveToForeground(); + break; + case "tasklist": + excludeFromTaskList(); + break; + case "dimmed": + isDimmed(callback); + break; + case "wakeup": + wakeup(); + break; + case "unlock": + wakeup(); + unlock(); + break; + default: + validAction = false; + } + + if (validAction) { + callback.success(); + } else { + callback.error("Invalid action: " + action); + } + + return validAction; + } + + /** + * Moves the app to the background. + */ + private void moveToBackground() + { + Intent intent = new Intent(Intent.ACTION_MAIN); + + intent.addCategory(Intent.CATEGORY_HOME); + + getApp().startActivity(intent); + } + + /** + * Moves the app to the foreground. + */ + private void moveToForeground() + { + Activity app = getApp(); + Intent intent = getLaunchIntent(); + + intent.addFlags( + Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | + Intent.FLAG_ACTIVITY_SINGLE_TOP | + Intent.FLAG_ACTIVITY_CLEAR_TOP); + + clearScreenAndKeyguardFlags(); + app.startActivity(intent); + } + + /** + * Enable GPS position tracking while in background. + */ + private void disableWebViewOptimizations() { + Thread thread = new Thread(){ + public void run() { + try { + Thread.sleep(1000); + getApp().runOnUiThread(() -> { + View view = webView.getEngine().getView(); + + try { + Class.forName("org.crosswalk.engine.XWalkCordovaView") + .getMethod("onShow") + .invoke(view); + } catch (Exception e){ + view.dispatchWindowVisibilityChanged(View.VISIBLE); + } + }); + } catch (InterruptedException e) { + // do nothing + } + } + }; + + thread.start(); + } + + /** + * Disables battery optimizations for the app. + * Requires permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS to function. + */ + @SuppressLint("BatteryLife") + private void disableBatteryOptimizations() + { + Activity activity = cordova.getActivity(); + Intent intent = new Intent(); + String pkgName = activity.getPackageName(); + PowerManager pm = (PowerManager)getService(POWER_SERVICE); + + if (SDK_INT < M) + return; + + if (pm.isIgnoringBatteryOptimizations(pkgName)) + return; + + intent.setAction(ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); + intent.setData(Uri.parse("package:" + pkgName)); + + cordova.getActivity().startActivity(intent); + } + + /** + * Opens the system settings dialog where the user can tweak or turn off any + * custom app start settings added by the manufacturer if available. + * + * @param arg Text and title for the dialog or false to skip the dialog. + */ + private void openAppStart (Object arg) + { + Activity activity = cordova.getActivity(); + PackageManager pm = activity.getPackageManager(); + + for (Intent intent : getAppStartIntents()) + { + if (pm.resolveActivity(intent, MATCH_DEFAULT_ONLY) != null) + { + JSONObject spec = (arg instanceof JSONObject) ? (JSONObject) arg : null; + + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + if (arg instanceof Boolean && !((Boolean) arg)) + { + activity.startActivity(intent); + break; + } + + AlertDialog.Builder dialog = new AlertDialog.Builder(activity, Theme_DeviceDefault_Light_Dialog); + + dialog.setPositiveButton(ok, (o, d) -> activity.startActivity(intent)); + dialog.setNegativeButton(cancel, (o, d) -> {}); + dialog.setCancelable(true); + + if (spec != null && spec.has("title")) + { + dialog.setTitle(spec.optString("title")); + } + + if (spec != null && spec.has("text")) + { + dialog.setMessage(spec.optString("text")); + } + else + { + dialog.setMessage("missing text"); + } + + activity.runOnUiThread(dialog::show); + + break; + } + } + } + + /** + * Excludes the app from the recent tasks list. + */ + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + private void excludeFromTaskList() + { + ActivityManager am = (ActivityManager) getService(ACTIVITY_SERVICE); + + if (am == null || SDK_INT < 21) + return; + + List tasks = am.getAppTasks(); + + if (tasks == null || tasks.isEmpty()) + return; + + tasks.get(0).setExcludeFromRecents(true); + } + + /** + * Invokes the callback with information if the screen is on. + * + * @param callback The callback to invoke. + */ + @SuppressWarnings("deprecation") + private void isDimmed (CallbackContext callback) + { + boolean status = isDimmed(); + PluginResult res = new PluginResult(Status.OK, status); + + callback.sendPluginResult(res); + } + + /** + * Returns if the screen is active. + */ + @SuppressWarnings("deprecation") + private boolean isDimmed() + { + PowerManager pm = (PowerManager) getService(POWER_SERVICE); + + if (SDK_INT < 20) + { + return !pm.isScreenOn(); + } + + return !pm.isInteractive(); + } + + /** + * Wakes up the device if the screen isn't still on. + */ + private void wakeup() + { + try { + acquireWakeLock(); + } catch (Exception e) { + releaseWakeLock(); + } + } + + /** + * Unlocks the device even with password protection. + */ + private void unlock() + { + addSreenAndKeyguardFlags(); + getApp().startActivity(getLaunchIntent()); + } + + /** + * Acquires a wake lock to wake up the device. + */ + @SuppressWarnings("deprecation") + private void acquireWakeLock() + { + PowerManager pm = (PowerManager) getService(POWER_SERVICE); + + releaseWakeLock(); + + if (!isDimmed()) + return; + + int level = PowerManager.SCREEN_DIM_WAKE_LOCK | + PowerManager.ACQUIRE_CAUSES_WAKEUP; + + wakeLock = pm.newWakeLock(level, "backgroundmode:wakelock"); + wakeLock.setReferenceCounted(false); + wakeLock.acquire(1000); + } + + /** + * Releases the previously acquire wake lock. + */ + private void releaseWakeLock() + { + if (wakeLock != null && wakeLock.isHeld()) { + wakeLock.release(); + wakeLock = null; + } + } + + /** + * Adds required flags to the window to unlock/wakeup the device. + */ + private void addSreenAndKeyguardFlags() + { + getApp().runOnUiThread(() -> getApp().getWindow().addFlags(FLAG_ALLOW_LOCK_WHILE_SCREEN_ON | FLAG_SHOW_WHEN_LOCKED | FLAG_TURN_SCREEN_ON | FLAG_DISMISS_KEYGUARD)); + } + + /** + * Clears required flags to the window to unlock/wakeup the device. + */ + private void clearScreenAndKeyguardFlags() + { + getApp().runOnUiThread(() -> getApp().getWindow().clearFlags(FLAG_ALLOW_LOCK_WHILE_SCREEN_ON | FLAG_SHOW_WHEN_LOCKED | FLAG_TURN_SCREEN_ON | FLAG_DISMISS_KEYGUARD)); + } + + /** + * Removes required flags to the window to unlock/wakeup the device. + */ + static void clearKeyguardFlags (Activity app) + { + app.runOnUiThread(() -> app.getWindow().clearFlags(FLAG_DISMISS_KEYGUARD)); + } + + /** + * Returns the activity referenced by cordova. + */ + Activity getApp() { + return cordova.getActivity(); + } + + /** + * Gets the launch intent for the main activity. + */ + private Intent getLaunchIntent() + { + Context app = getApp().getApplicationContext(); + String pkgName = app.getPackageName(); + + return app.getPackageManager().getLaunchIntentForPackage(pkgName); + } + + /** + * Get the requested system service by name. + * + * @param name The name of the service. + */ + private Object getService(String name) + { + return getApp().getSystemService(name); + } + + /** + * Returns list of all possible intents to present the app start settings. + */ + private List getAppStartIntents() + { + return Arrays.asList( + new Intent().setComponent(new ComponentName("com.miui.securitycenter","com.miui.permcenter.autostart.AutoStartManagementActivity")), + new Intent().setComponent(new ComponentName("com.letv.android.letvsafe", "com.letv.android.letvsafe.AutobootManageActivity")), + new Intent().setComponent(new ComponentName("com.huawei.systemmanager", "com.huawei.systemmanager.appcontrol.activity.StartupAppControlActivity")), + new Intent().setComponent(new ComponentName("com.huawei.systemmanager", "com.huawei.systemmanager.optimize.process.ProtectActivity")), + new Intent().setComponent(new ComponentName("com.coloros.safecenter", "com.coloros.safecenter.permission.startup.StartupAppListActivity")), + new Intent().setComponent(new ComponentName("com.coloros.safecenter", "com.coloros.safecenter.startupapp.StartupAppListActivity")), + new Intent().setComponent(new ComponentName("com.oppo.safe", "com.oppo.safe.permission.startup.StartupAppListActivity")), + new Intent().setComponent(new ComponentName("com.iqoo.secure", "com.iqoo.secure.ui.phoneoptimize.AddWhiteListActivity")), + new Intent().setComponent(new ComponentName("com.iqoo.secure", "com.iqoo.secure.ui.phoneoptimize.BgStartUpManager")), + new Intent().setComponent(new ComponentName("com.vivo.permissionmanager", "com.vivo.permissionmanager.activity.BgStartUpManagerActivity")), + new Intent().setComponent(new ComponentName("com.asus.mobilemanager", "com.asus.mobilemanager.autostart.AutoStartActivity")), + new Intent().setComponent(new ComponentName("com.asus.mobilemanager", "com.asus.mobilemanager.entry.FunctionActivity")).setData(android.net.Uri.parse("mobilemanager://function/entry/AutoStart")), + new Intent().setAction("com.letv.android.permissionautoboot"), + new Intent().setComponent(new ComponentName("com.samsung.android.sm_cn", "com.samsung.android.sm.ui.ram.AutoRunActivity")), + new Intent().setComponent(ComponentName.unflattenFromString("com.iqoo.secure/.MainActivity")), + new Intent().setComponent(ComponentName.unflattenFromString("com.meizu.safe/.permission.SmartBGActivity")), + new Intent().setComponent(new ComponentName("com.yulong.android.coolsafe", ".ui.activity.autorun.AutoRunListActivity")), + new Intent().setComponent(new ComponentName("cn.nubia.security2", "cn.nubia.security.appmanage.selfstart.ui.SelfStartActivity")), + new Intent().setComponent(new ComponentName("com.zui.safecenter", "com.lenovo.safecenter.MainTab.LeSafeMainActivity")) + ); + } +} diff --git a/src/android/ForegroundService.java b/src/android/ForegroundService.java index e3130821..806c9568 100644 --- a/src/android/ForegroundService.java +++ b/src/android/ForegroundService.java @@ -1,26 +1,27 @@ /* - Copyright 2013-2017 appPlant GmbH - - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. + Copyright 2013 Sebastián Katzer + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. */ package de.appplant.cordova.plugin.background; +import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.Notification; import android.app.NotificationManager; @@ -33,6 +34,7 @@ Licensed to the Apache Software Foundation (ASF) under one import android.os.Build; import android.os.IBinder; import android.os.PowerManager; +import android.app.NotificationChannel; import org.json.JSONObject; @@ -60,7 +62,7 @@ public class ForegroundService extends Service { private static final String NOTIFICATION_ICON = "icon"; // Binder given to clients - private final IBinder mBinder = new ForegroundBinder(); + private final IBinder binder = new ForegroundBinder(); // Partial wake lock to prevent the app from going to sleep when locked private PowerManager.WakeLock wakeLock; @@ -70,15 +72,17 @@ public class ForegroundService extends Service { */ @Override public IBinder onBind (Intent intent) { - return mBinder; + return binder; } /** * Class used for the client Binder. Because we know this service always * runs in the same process as its clients, we don't need to deal with IPC. */ - public class ForegroundBinder extends Binder { - ForegroundService getService() { + class ForegroundBinder extends Binder + { + ForegroundService getService() + { // Return this instance of ForegroundService // so clients can call public methods return ForegroundService.this; @@ -90,7 +94,8 @@ ForegroundService getService() { * by the OS. */ @Override - public void onCreate () { + public void onCreate() + { super.onCreate(); keepAwake(); } @@ -99,16 +104,27 @@ public void onCreate () { * No need to run headless on destroy. */ @Override - public void onDestroy() { + public void onDestroy() + { super.onDestroy(); sleepWell(); } + /** + * Prevent Android from stopping the background service automatically. + */ + @Override + public int onStartCommand (Intent intent, int flags, int startId) { + return START_STICKY; + } + /** * Put the service in a foreground state to prevent app from being killed * by the OS. */ - private void keepAwake() { + @SuppressLint("WakelockTimeout") + private void keepAwake() + { JSONObject settings = BackgroundMode.getSettings(); boolean isSilent = settings.optBoolean("silent", false); @@ -116,11 +132,10 @@ private void keepAwake() { startForeground(NOTIFICATION_ID, makeNotification()); } - PowerManager pm = (PowerManager) - getSystemService(POWER_SERVICE); + PowerManager pm = (PowerManager)getSystemService(POWER_SERVICE); wakeLock = pm.newWakeLock( - PARTIAL_WAKE_LOCK, "BackgroundMode"); + PARTIAL_WAKE_LOCK, "backgroundmode:wakelock"); wakeLock.acquire(); } @@ -128,7 +143,8 @@ private void keepAwake() { /** * Stop background mode. */ - private void sleepWell() { + private void sleepWell() + { stopForeground(true); getNotificationManager().cancel(NOTIFICATION_ID); @@ -142,7 +158,8 @@ private void sleepWell() { * Create a notification as the visible part to be able to put the service * in a foreground state by using the default settings. */ - private Notification makeNotification() { + private Notification makeNotification() + { return makeNotification(BackgroundMode.getSettings()); } @@ -152,7 +169,25 @@ private Notification makeNotification() { * * @param settings The config settings */ - private Notification makeNotification(JSONObject settings) { + private Notification makeNotification (JSONObject settings) + { + // use channelid for Oreo and higher + String CHANNEL_ID = "cordova-plugin-background-mode-id"; + if(Build.VERSION.SDK_INT >= 26){ + // The user-visible name of the channel. + CharSequence name = "cordova-plugin-background-mode"; + // The user-visible description of the channel. + String description = "cordova-plugin-background-moden notification"; + + int importance = NotificationManager.IMPORTANCE_LOW; + + NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID, name,importance); + + // Configure the notification channel. + mChannel.setDescription(description); + + getNotificationManager().createNotificationChannel(mChannel); + } String title = settings.optString("title", NOTIFICATION_TITLE); String text = settings.optString("text", NOTIFICATION_TEXT); boolean bigText = settings.optBoolean("bigText", false); @@ -168,6 +203,10 @@ private Notification makeNotification(JSONObject settings) { .setOngoing(true) .setSmallIcon(getIconResId(settings)); + if(Build.VERSION.SDK_INT >= 26){ + notification.setChannelId(CHANNEL_ID); + } + if (settings.optBoolean("hidden", true)) { notification.setPriority(Notification.PRIORITY_MIN); } @@ -197,7 +236,8 @@ private Notification makeNotification(JSONObject settings) { * * @param settings The config settings */ - protected void updateNotification (JSONObject settings) { + protected void updateNotification (JSONObject settings) + { boolean isSilent = settings.optBoolean("silent", false); if (isSilent) { @@ -207,6 +247,7 @@ protected void updateNotification (JSONObject settings) { Notification notification = makeNotification(settings); getNotificationManager().notify(NOTIFICATION_ID, notification); + } /** @@ -214,10 +255,10 @@ protected void updateNotification (JSONObject settings) { * * @param settings A JSON dict containing the icon name. */ - private int getIconResId(JSONObject settings) { + private int getIconResId (JSONObject settings) + { String icon = settings.optString("icon", NOTIFICATION_ICON); - // cordova-android 6 uses mipmaps int resId = getIconResId(icon, "mipmap"); if (resId == 0) { @@ -235,7 +276,8 @@ private int getIconResId(JSONObject settings) { * * @return The resource id or 0 if not found. */ - private int getIconResId(String icon, String type) { + private int getIconResId (String icon, String type) + { Resources res = getResources(); String pkgName = getPackageName(); @@ -255,8 +297,8 @@ private int getIconResId(String icon, String type) { * @param settings A JSON dict containing the color definition (red: FF0000) */ @TargetApi(Build.VERSION_CODES.LOLLIPOP) - private void setColor(Notification.Builder notification, - JSONObject settings) { + private void setColor (Notification.Builder notification, JSONObject settings) + { String hex = settings.optString("color", null); @@ -272,10 +314,10 @@ private void setColor(Notification.Builder notification, } /** - * Shared manager for the notification service. + * Returns the shared notification service manager. */ - private NotificationManager getNotificationManager() { + private NotificationManager getNotificationManager() + { return (NotificationManager) getSystemService(NOTIFICATION_SERVICE); } - } diff --git a/src/browser/BackgroundModeProxy.js b/src/browser/BackgroundModeProxy.js index 5ba7880a..4a880cbe 100644 --- a/src/browser/BackgroundModeProxy.js +++ b/src/browser/BackgroundModeProxy.js @@ -1,22 +1,22 @@ /* - Copyright 2013-2017 appPlant GmbH + Copyright 2013 Sebastián Katzer - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. */ /** diff --git a/src/ios/APPBackgroundMode.m b/src/ios/APPBackgroundMode.m index 20f0695b..174b6667 100644 --- a/src/ios/APPBackgroundMode.m +++ b/src/ios/APPBackgroundMode.m @@ -241,7 +241,7 @@ - (void) fireEvent:(NSString*)event */ + (NSString*) wkProperty { - NSString* str = @"X2Fsd2F5c1J1bnNBdEZvcmVncm91bmRQcmlvcml0eQ=="; + NSString* str = @"YWx3YXlzUnVuc0F0Rm9yZWdyb3VuZFByaW9yaXR5"; NSData* data = [[NSData alloc] initWithBase64EncodedString:str options:0]; return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; diff --git a/src/windows/BackgroundModeProxy.js b/src/windows/BackgroundModeProxy.js index 5542e3f7..b151ae1f 100644 --- a/src/windows/BackgroundModeProxy.js +++ b/src/windows/BackgroundModeProxy.js @@ -1,22 +1,22 @@ /* - Copyright 2013-2017 appPlant GmbH - - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. + Copyright 2013 Sebastián Katzer + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. */ var plugin = cordova.plugins.backgroundMode; diff --git a/www/background-mode.js b/www/background-mode.js index 440edc3c..fff3f8d3 100644 --- a/www/background-mode.js +++ b/www/background-mode.js @@ -22,11 +22,6 @@ var exec = require('cordova/exec'), channel = require('cordova/channel'); - -/************* - * INTERFACE * - *************/ - /** * Activates the background mode. When activated the application * will be prevented from going to sleep while in background @@ -34,14 +29,15 @@ var exec = require('cordova/exec'), * * @return [ Void ] */ -exports.enable = function() { +exports.enable = function() +{ if (this.isEnabled()) return; var fn = function() { - exports._isEnabled = true; - exports.fireEvent('enable'); - }; + exports._isEnabled = true; + exports.fireEvent('enable'); + }; cordova.exec(fn, null, 'BackgroundMode', 'enable', []); }; @@ -52,14 +48,15 @@ exports.enable = function() { * * @return [ Void ] */ -exports.disable = function() { +exports.disable = function() +{ if (!this.isEnabled()) return; var fn = function() { - exports._isEnabled = false; - exports.fireEvent('disable'); - }; + exports._isEnabled = false; + exports.fireEvent('disable'); + }; cordova.exec(fn, null, 'BackgroundMode', 'disable', []); }; @@ -71,7 +68,8 @@ exports.disable = function() { * * @return [ Void ] */ -exports.setEnabled = function (enable) { +exports.setEnabled = function (enable) +{ if (enable) { this.enable(); } else { @@ -84,7 +82,8 @@ exports.setEnabled = function (enable) { * * @return [ Object ] */ -exports.getDefaults = function() { +exports.getDefaults = function() +{ return this._defaults; }; @@ -93,7 +92,8 @@ exports.getDefaults = function() { * * @return [ Object ] */ -exports.getSettings = function() { +exports.getSettings = function() +{ return this._settings || {}; }; @@ -104,16 +104,20 @@ exports.getSettings = function() { * * @return [ Void ] */ -exports.setDefaults = function (overrides) { +exports.setDefaults = function (overrides) +{ var defaults = this.getDefaults(); - for (var key in defaults) { - if (overrides.hasOwnProperty(key)) { + for (var key in defaults) + { + if (overrides.hasOwnProperty(key)) + { defaults[key] = overrides[key]; } } - if (this._isAndroid) { + if (this._isAndroid) + { cordova.exec(null, null, 'BackgroundMode', 'configure', [defaults, false]); } }; @@ -126,14 +130,16 @@ exports.setDefaults = function (overrides) { * * @return [ Void ] */ -exports.configure = function (options) { +exports.configure = function (options) +{ var settings = this.getSettings(), defaults = this.getDefaults(); if (!this._isAndroid) return; - if (!this._isActive) { + if (!this._isActive) + { console.log('BackgroundMode is not active, skipped...'); return; } @@ -150,9 +156,41 @@ exports.configure = function (options) { * * @return [ Void ] */ -exports.disableWebViewOptimizations = function() { - if (this._isAndroid) { - cordova.exec(null, null, 'BackgroundMode', 'optimizations', []); +exports.disableWebViewOptimizations = function() +{ + if (this._isAndroid) + { + cordova.exec(null, null, 'BackgroundModeExt', 'webview', []); + } +}; + +/** + * Disables battery optimazation mode for the app. + * + * @return [ Void ] + */ +exports.disableBatteryOptimizations = function() +{ + if (this._isAndroid) + { + cordova.exec(null, null, 'BackgroundModeExt', 'battery', []); + } +}; + +/** + * Opens the system settings dialog where the user can tweak or turn off any + * custom app start settings added by the manufacturer if available. + * + * @param [ Object|Bool ] options Set to false if you dont want to display an + * alert dialog first. + * + * @return [ Void ] + */ +exports.openAppStartSettings = function (options) +{ + if (this._isAndroid) + { + cordova.exec(null, null, 'BackgroundModeExt', 'appstart', [options]); } }; @@ -161,9 +199,11 @@ exports.disableWebViewOptimizations = function() { * * @return [ Void ] */ -exports.moveToBackground = function() { - if (this._isAndroid) { - cordova.exec(null, null, 'BackgroundMode', 'background', []); +exports.moveToBackground = function() +{ + if (this._isAndroid) + { + cordova.exec(null, null, 'BackgroundModeExt', 'background', []); } }; @@ -172,9 +212,11 @@ exports.moveToBackground = function() { * * @return [ Void ] */ -exports.moveToForeground = function() { - if (this.isActive() && this._isAndroid) { - cordova.exec(null, null, 'BackgroundMode', 'foreground', []); +exports.moveToForeground = function() +{ + if (this.isActive() && this._isAndroid) + { + cordova.exec(null, null, 'BackgroundModeExt', 'foreground', []); } }; @@ -183,9 +225,11 @@ exports.moveToForeground = function() { * * @return [ Void ] */ -exports.excludeFromTaskList = function() { - if (this._isAndroid) { - cordova.exec(null, null, 'BackgroundMode', 'tasklist', []); +exports.excludeFromTaskList = function() +{ + if (this._isAndroid) + { + cordova.exec(null, null, 'BackgroundModeExt', 'tasklist', []); } }; @@ -195,7 +239,8 @@ exports.excludeFromTaskList = function() { * * @return [ Void ] */ -exports.overrideBackButton = function() { +exports.overrideBackButton = function() +{ document.addEventListener('backbutton', function() { exports.moveToBackground(); }, false); @@ -208,10 +253,14 @@ exports.overrideBackButton = function() { * * @return [ Void ] */ -exports.isScreenOff = function (fn) { - if (this._isAndroid) { - cordova.exec(fn, null, 'BackgroundMode', 'dimmed', []); - } else { +exports.isScreenOff = function (fn) +{ + if (this._isAndroid) + { + cordova.exec(fn, null, 'BackgroundModeExt', 'dimmed', []); + } + else + { fn(undefined); } }; @@ -221,9 +270,11 @@ exports.isScreenOff = function (fn) { * * @return [ Void ] */ -exports.wakeUp = function() { - if (this._isAndroid) { - cordova.exec(null, null, 'BackgroundMode', 'wakeup', []); +exports.wakeUp = function() +{ + if (this._isAndroid) + { + cordova.exec(null, null, 'BackgroundModeExt', 'wakeup', []); } }; @@ -232,9 +283,11 @@ exports.wakeUp = function() { * * @return [ Void ] */ -exports.unlock = function() { - if (this._isAndroid) { - cordova.exec(null, null, 'BackgroundMode', 'unlock', []); +exports.unlock = function() +{ + if (this._isAndroid) + { + cordova.exec(null, null, 'BackgroundModeExt', 'unlock', []); } }; @@ -243,7 +296,8 @@ exports.unlock = function() { * * @return [ Boolean ] */ -exports.isEnabled = function() { +exports.isEnabled = function() +{ return this._isEnabled !== false; }; @@ -252,15 +306,11 @@ exports.isEnabled = function() { * * @return [ Boolean ] */ -exports.isActive = function() { +exports.isActive = function() +{ return this._isActive !== false; }; - -/********** - * EVENTS * - **********/ - exports._listener = {}; /** @@ -271,14 +321,16 @@ exports._listener = {}; * * @return [ Void ] */ -exports.fireEvent = function (event) { +exports.fireEvent = function (event) +{ var args = Array.apply(null, arguments).slice(1), listener = this._listener[event]; if (!listener) return; - for (var i = 0; i < listener.length; i++) { + for (var i = 0; i < listener.length; i++) + { var fn = listener[i][0], scope = listener[i][1]; @@ -295,12 +347,13 @@ exports.fireEvent = function (event) { * * @return [ Void ] */ -exports.on = function (event, callback, scope) { - +exports.on = function (event, callback, scope) +{ if (typeof callback !== "function") return; - if (!this._listener[event]) { + if (!this._listener[event]) + { this._listener[event] = []; } @@ -317,51 +370,25 @@ exports.on = function (event, callback, scope) { * * @return [ Void ] */ -exports.un = function (event, callback) { +exports.un = function (event, callback) +{ var listener = this._listener[event]; if (!listener) return; - for (var i = 0; i < listener.length; i++) { + for (var i = 0; i < listener.length; i++) + { var fn = listener[i][0]; - if (fn == callback) { + if (fn == callback) + { listener.splice(i, 1); break; } } }; -/** - * @deprecated - * - * Called when the background mode has been activated. - */ -exports.onactivate = function() {}; - -/** - * @deprecated - * - * Called when the background mode has been deaktivated. - */ -exports.ondeactivate = function() {}; - -/** - * @deprecated - * - * Called when the background mode could not been activated. - * - * @param {Integer} errorCode - * Error code which describes the error - */ -exports.onfailure = function() {}; - - -/*********** - * PRIVATE * - ***********/ - /** * @private * @@ -381,7 +408,8 @@ exports._isActive = false; * * Default values of all available options. */ -exports._defaults = { +exports._defaults = +{ title: 'App is running in background', text: 'Doing heavy tasks.', bigText: false, @@ -402,9 +430,12 @@ exports._defaults = { * * @return [ Object ] Default values merged with custom values. */ -exports._mergeObjects = function (options, toMergeIn) { - for (var key in toMergeIn) { - if (!options.hasOwnProperty(key)) { +exports._mergeObjects = function (options, toMergeIn) +{ + for (var key in toMergeIn) + { + if (!options.hasOwnProperty(key)) + { options[key] = toMergeIn[key]; continue; } @@ -423,7 +454,8 @@ exports._mergeObjects = function (options, toMergeIn) { * * @return [ Void ] */ -exports._setActive = function(value) { +exports._setActive = function(value) +{ if (this._isActive == value) return; @@ -441,11 +473,13 @@ exports._setActive = function(value) { * * @return [ Void ] */ -exports._pluginInitialize = function() { +exports._pluginInitialize = function() +{ this._isAndroid = device.platform.match(/^android|amazon/i) !== null; this.setDefaults({}); - if (device.platform == 'browser') { + if (device.platform == 'browser') + { this.enable(); this._isEnabled = true; } @@ -454,19 +488,23 @@ exports._pluginInitialize = function() { }; // Called before 'deviceready' listener will be called -channel.onCordovaReady.subscribe(function() { +channel.onCordovaReady.subscribe(function() +{ channel.onCordovaInfoReady.subscribe(function() { exports._pluginInitialize(); }); }); // Called after 'deviceready' event -channel.deviceready.subscribe(function() { - if (exports.isEnabled()) { +channel.deviceready.subscribe(function() +{ + if (exports.isEnabled()) + { exports.fireEvent('enable'); } - if (exports.isActive()) { + if (exports.isActive()) + { exports.fireEvent('activate'); } }); From b87e466387c4286b10281790878fe915941d12c0 Mon Sep 17 00:00:00 2001 From: kitolog Date: Thu, 18 Apr 2019 14:38:08 +0300 Subject: [PATCH 3/7] updated onactivate calls --- .npmrc | 23 +++++++++++++++++++++++ CHANGELOG.md | 4 ++++ package.json | 2 +- plugin.xml | 4 +++- src/android/BackgroundMode.java | 2 +- src/ios/APPBackgroundMode.m | 2 +- 6 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 .npmrc diff --git a/.npmrc b/.npmrc new file mode 100644 index 00000000..8aab9a9d --- /dev/null +++ b/.npmrc @@ -0,0 +1,23 @@ +# Specifies intentionally untracked files to ignore when using Git +# http://git-scm.com/docs/gitignore + +*~ +*.sw[mnpcod] +*.log +*.tmp +*.tmp.* +log.txt +*.sublime-project +*.sublime-workspace +.vscode/ +npm-debug.log* + +.idea/ +.sourcemaps/ +.sass-cache/ +.tmp/ +tmp/ +$RECYCLE.BIN/ + +.DS_Store +Thumbs.db diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d91858b..6c2e3708 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,8 @@ ## ChangeLog +#### Version 0.7.5 +- Fixed onactivate call +- Merged new Android permissions + #### Version 0.7.4 - Fixed iOS 12.2 crashes - Merged original repository Android source updates diff --git a/package.json b/package.json index ef185e90..2d233f63 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cordova-plugin-background-mode-fixed", - "version": "0.7.4", + "version": "0.7.5", "description": "Prevent apps from going to sleep in background. iOS fix.", "cordova": { "id": "cordova-plugin-background-mode-fixed", diff --git a/plugin.xml b/plugin.xml index 79654026..6883ddd5 100644 --- a/plugin.xml +++ b/plugin.xml @@ -44,7 +44,7 @@ - audio + remote-notification @@ -77,6 +77,8 @@ + + Date: Sun, 27 Sep 2020 13:04:22 -0400 Subject: [PATCH 4/7] updates to fix playback issue --- plugin.xml | 2 +- src/ios/APPBackgroundMode.m | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/plugin.xml b/plugin.xml index 04888784..7aecd88d 100644 --- a/plugin.xml +++ b/plugin.xml @@ -42,7 +42,7 @@ - remote-notification + audio diff --git a/src/ios/APPBackgroundMode.m b/src/ios/APPBackgroundMode.m index e0792ccc..d9ba8daa 100644 --- a/src/ios/APPBackgroundMode.m +++ b/src/ios/APPBackgroundMode.m @@ -173,6 +173,7 @@ - (void) configureAudioSession // Play music even in background and dont stop playing music // even another app starts playing sound [session setCategory:AVAudioSessionCategoryPlayback + withOptions:AVAudioSessionCategoryOptionMixWithOthers error:NULL]; // Active the audio session From 57a30e516ce64a4aa3a06c7776dc2671120524ac Mon Sep 17 00:00:00 2001 From: NMTeck Date: Sun, 27 Sep 2020 13:07:15 -0400 Subject: [PATCH 5/7] updating version number --- plugin.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin.xml b/plugin.xml index 7aecd88d..cd38b2aa 100644 --- a/plugin.xml +++ b/plugin.xml @@ -3,7 +3,7 @@ + version="0.7.8"> BackgroundMode Prevent apps from going to sleep in background. iOS fix. From 9693256b903c4539091d8839a31115c2ad2503d6 Mon Sep 17 00:00:00 2001 From: thompsd3 Date: Sun, 27 Sep 2020 15:52:31 -0400 Subject: [PATCH 6/7] Update README.md --- README.md | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index d1c760f1..ac188101 100644 --- a/README.md +++ b/README.md @@ -29,21 +29,10 @@ Use the plugin by your own risk! ## Installation The plugin can be installed via [Cordova-CLI][CLI] and is publicly available on [NPM][npm]. -Execute from the projects root folder: - - $ cordova plugin add cordova-plugin-background-mode - -Or install a specific version: - - $ cordova plugin add de.appplant.cordova.plugin.background-mode@VERSION Or install the latest head version: - $ cordova plugin add https://github.com/katzer/cordova-plugin-background-mode.git - -Or install from local source: - - $ cordova plugin add cordova-plugin-background-mode --searchpath + $ cordova plugin add https://github.com/thompsd3/cordova-plugin-background-mode.git ## Usage @@ -207,4 +196,4 @@ Made with :yum: from Leipzig [changelog]: CHANGELOG.md [apache2_license]: http://opensource.org/licenses/Apache-2.0 [appplant]: http://appplant.de -[meshfields]: http://meshfields.de \ No newline at end of file +[meshfields]: http://meshfields.de From 54507f30ad7018762c786b9b36beb60991ae0f88 Mon Sep 17 00:00:00 2001 From: thompsd3 Date: Sun, 27 Sep 2020 21:33:23 -0400 Subject: [PATCH 7/7] Update README.md Added information regarding additions to the Controller that can help with backgrounding, particularly for music apps that need to skip to the next song while in the background. --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index ac188101..2bf3b0a2 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,16 @@ cordova.plugins.backgroundMode.un('EVENT', function); ``` +## iOS specifics + +### Update MainController (hacky for now until I can integrate into plugin) +Further information can be found in this post https://stackoverflow.com/questions/9660488/ios-avaudioplayer-doesnt-continue-to-next-song-while-in-background + +You need this code in either your first view controller's init or viewDidLoad method: +```c +[[UIApplication sharedApplication] beginReceivingRemoteControlEvents]; +``` + ## Android specifics ### Transit between application states