This commit is contained in:
zhangxiongwang 2019-06-25 12:38:02 +08:00
parent bc023a25c8
commit 69e5e3b24d
15 changed files with 2367 additions and 0 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

87
CHANGELOG.md Normal file
View File

@ -0,0 +1,87 @@
## ChangeLog
#### Version 0.7.2 (02.02.2017)
- Fixed app freeze on iOS using wkwebview-engine
- Websocket sample in SampleApp
#### Version 0.7.1 (30.01.2017)
- Bug fixes for iOS9 and Android
- Allow app to be excluded from recent list on Android
#### Version 0.7.0 (27.01.2017)
- __Features__
- Support for tAmazon FireOS
- Support for the browser platform
- Ability to configure icon and color on Android
- Allow app to move to foreground on Android
- Allow app to move to background on Android
- Allow app to override back button behaviour on Android
- New events for when the mode has been enabled/disabled
- __Improvements__
- Various enhancements and bug fixes for all platforms
- Support for latest platform and OS versions
- Multi line text on Android
- Multiple listeners for same event
- Compatibility with cordova-plugin-geolocation
- Compatibility with cordova-plugin-crosswalk-webview
- Compatibility with cordova-plugin-wkwebview-engine
- New sample app
- __Fixes__
- Silent mode issues on Android
- Lock screen issues on Android
- Callback not called on Android
- Notification shows app info with cordova-android@6
- Other apps audio interruption on iOS
- __Changes__
- Deprecate event callbacks
- Notification not visible by default on lock screen
- Remove ticker property on Android
- Remove unexpected back button handler
- Remove support for wp8 platform
#### Version 0.6.5 (29.02.2016)
- Published on npm
- Updated dependency ID for the device plug-in
#### Version 0.6.4 (03.03.2015)
- Resolve possibly dependency conflict
#### Version 0.6.3 (01.01.2015)
- [feature:] Silent mode for Android
#### Version 0.6.2 (14.12.2014)
- [bugfix:] Type error
- [bugfix:] Wrong default values for `isEnabled` and `isActive`.
#### Version 0.6.1 (14.12.2014)
- [enhancement:] Set default settings through `setDefaults`.
- [enhancement:] New method `isEnabled` to receive if mode is enabled.
- [enhancement:] New method `isActive` to receive if mode is active.
- [bugfix:] Events caused thread collision.
#### Version 0.6.0 (14.12.2014)
- [feature:] Android support
- [feature:] Change Android notification through `configure`.
- [feature:] `onactivate`, `ondeactivate` and `onfailure` callbacks.
- [___change___:] Disabled by default
- [enhancement:] Get default settings through `getDefaults`.
- [enhancement:] iOS does not require user permissions, internet connection and geo location anymore.
#### Version 0.5.0 (13.02.2014)
- __retired__
#### Version 0.4.1 (13.02.2014)
- Release under the Apache 2.0 license.
- [enhancement:] Location tracking is only activated on WP8 if the location service is available.
- [bigfix:] Nullpointer exception on WP8.
#### Version 0.4.0 (10.10.2013)
- Added WP8 support<br>
The plugin turns the app into an location tracking app *(for the time it runs in the background)*.
#### Version 0.2.1 (09.10.2013)
- Added js interface to manually enable/disable the background mode.
#### Version 0.2.0 (08.10.2013)
- Added iOS (>= 5) support<br>
The plugin turns the app into an location tracking app for the time it runs in the background.

202
LICENSE Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2013 appPlant GmbH
Licensed 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.

59
package.json Normal file
View File

@ -0,0 +1,59 @@
{
"_from": "cordova-plugin-background-mode",
"_id": "cordova-plugin-background-mode@0.7.2",
"_inBundle": false,
"_integrity": "sha1-6vVjnAZahyMbPs91em4osQHQMy8=",
"_location": "/cordova-plugin-background-mode",
"_phantomChildren": {},
"_requested": {
"type": "tag",
"registry": true,
"raw": "cordova-plugin-background-mode",
"name": "cordova-plugin-background-mode",
"escapedName": "cordova-plugin-background-mode",
"rawSpec": "",
"saveSpec": null,
"fetchSpec": "latest"
},
"_requiredBy": [
"#USER",
"/"
],
"_resolved": "https://registry.npm.taobao.org/cordova-plugin-background-mode/download/cordova-plugin-background-mode-0.7.2.tgz",
"_shasum": "eaf5639c065a87231b3ecf757a6e28b101d0332f",
"_spec": "cordova-plugin-background-mode",
"_where": "/Users/zhangxiongwang/Documents/workspace/laoting",
"author": {
"name": "Sebastián Katzer"
},
"bugs": {
"url": "https://github.com/katzer/cordova-plugin-background-mode/issues"
},
"bundleDependencies": false,
"deprecated": false,
"description": "Prevent app from going to sleep in background.",
"engines": [
{
"name": "cordova",
"version": ">=6.0.0"
}
],
"homepage": "https://github.com/katzer/cordova-plugin-background-mode#readme",
"keywords": [
"appplant",
"background",
"cordova",
"ecosystem:cordova"
],
"license": "Apache 2.0",
"name": "cordova-plugin-background-mode",
"platforms": [
"ios",
"android"
],
"repository": {
"type": "git",
"url": "git+https://github.com/katzer/cordova-plugin-background-mode.git"
},
"version": "0.7.2"
}

132
plugin.xml Normal file
View File

@ -0,0 +1,132 @@
<?xml version="1.0" encoding="UTF-8"?>
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
xmlns:android="http://schemas.android.com/apk/res/android"
id="cordova-plugin-background-mode"
version="0.7.2">
<name>BackgroundMode</name>
<description>Prevent apps from going to sleep in background.</description>
<repo>https://github.com/katzer/cordova-plugin-background-mode.git</repo>
<keywords>appplant, background</keywords>
<license>Apache 2.0</license>
<author>Sebastián Katzer</author>
<!-- dependencies -->
<dependency id="cordova-plugin-device" />
<!-- cordova -->
<engines>
<engine name="cordova" version="&gt;=3.0.0" />
<engine name="android-sdk" version="&gt;=16" />
<engine name="windows-sdk" version="&gt;=10.0.14393.0" />
</engines>
<!-- js -->
<js-module src="www/background-mode.js" name="BackgroundMode">
<clobbers target="cordova.plugins.backgroundMode" />
<clobbers target="plugin.backgroundMode" />
</js-module>
<!-- ios -->
<platform name="ios">
<config-file target="config.xml" parent="/*">
<feature name="BackgroundMode">
<param name="ios-package" value="APPBackgroundMode" />
</feature>
</config-file>
<config-file target="*-Info.plist" parent="UIBackgroundModes">
<array>
<string>audio</string>
</array>
</config-file>
<resource-file src="appbeep.wav" />
<header-file src="src/ios/APPBackgroundMode.h" />
<source-file src="src/ios/APPBackgroundMode.m" />
<header-file src="src/ios/APPMethodMagic.h" />
<source-file src="src/ios/APPMethodMagic.m" />
</platform>
<!-- android -->
<platform name="android">
<config-file target="res/xml/config.xml" parent="/*">
<feature name="BackgroundMode" >
<param name="android-package"
value="de.appplant.cordova.plugin.background.BackgroundMode"/>
</feature>
</config-file>
<config-file target="res/xml/config.xml" parent="/*">
<preference name="KeepRunning" value="true" />
<preference name="AndroidLaunchMode" value="singleInstance"/>
</config-file>
<config-file target="AndroidManifest.xml" parent="/manifest/application">
<service android:name="de.appplant.cordova.plugin.background.ForegroundService" />
</config-file>
<config-file target="AndroidManifest.xml" parent="/manifest">
<uses-permission android:name="android.permission.WAKE_LOCK" />
</config-file>
<source-file
src="src/android/BackgroundMode.java"
target-dir="src/de/appplant/cordova/plugin/background" />
<source-file
src="src/android/BackgroundExt.java"
target-dir="src/de/appplant/cordova/plugin/background" />
<source-file
src="src/android/ForegroundService.java"
target-dir="src/de/appplant/cordova/plugin/background" />
</platform>
<!-- windows
<platform name="windows">
<config-file target="config.xml" parent="/*">
<feature name="BackgroundMode" >
<param name="windows-package" value="BackgroundMode"/>
</feature>
</config-file>
<config-file target="package.appxmanifest" parent="/Package/Capabilities" device-target="windows">
<Capability Name="backgroundMediaPlayback" />
</config-file>
<config-file target="config.xml" parent="/*">
<preference name="windows-target-version" value="UAP" />
<preference name="uap-target-min-version" value="10.0.14393.0" />
<preference name="Windows.Universal-MinVersion" value="10.0.14393.0" />
<preference name="Windows.Universal" value="10.0.14393.0" />
</config-file>
<resource-file src="appbeep.wma" target="appbeep.wma" />
<js-module src="src/windows/BackgroundModeProxy.js" name="BackgroundMode.Proxy">
<runs />
</js-module>
</platform> -->
<!-- browser -->
<platform name="browser">
<config-file target="config.xml" parent="/*">
<feature name="BackgroundMode">
<param name="browser-package" value="BackgroundMode"/>
</feature>
</config-file>
<js-module src="src/browser/BackgroundModeProxy.js" name="BackgroundMode.Proxy">
<runs />
</js-module>
</platform>
</plugin>

View File

@ -0,0 +1,178 @@
/*
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.
*/
package de.appplant.cordova.plugin.background;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManager.AppTask;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.view.View;
import org.apache.cordova.CordovaInterface;
import org.apache.cordova.CordovaWebView;
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.util.List;
class BackgroundExt {
// Weak reference to the cordova interface passed by the plugin
private final WeakReference<CordovaInterface> cordova;
// Weak reference to the cordova web view passed by the plugin
private final WeakReference<CordovaWebView> webView;
/**
* Initialize the extension to perform non-background related tasks.
*
* @param cordova The cordova interface.
* @param webView The cordova web view.
*/
private BackgroundExt(CordovaInterface cordova, CordovaWebView webView) {
this.cordova = new WeakReference<CordovaInterface>(cordova);
this.webView = new WeakReference<CordovaWebView>(webView);
}
/**
* Executes the request.
*
* @param action The action to execute.
* @param cordova The cordova interface.
* @param webView The cordova web view.
*/
static void execute(String action, CordovaInterface cordova,
CordovaWebView webView) {
BackgroundExt ext = new BackgroundExt(cordova, webView);
if (action.equalsIgnoreCase("optimizations")) {
ext.disableWebViewOptimizations();
}
if (action.equalsIgnoreCase("background")) {
ext.moveToBackground();
}
if (action.equalsIgnoreCase("foreground")) {
ext.moveToForeground();
}
if (action.equalsIgnoreCase("tasklist")) {
ext.excludeFromTaskList();
}
}
/**
* Move app to background.
*/
private void moveToBackground() {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
getActivity().startActivity(intent);
}
/**
* Move app to foreground.
*/
private void moveToForeground() {
Activity app = getActivity();
String pkgName = app.getPackageName();
Intent intent = app
.getPackageManager()
.getLaunchIntentForPackage(pkgName);
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);
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
View view = webView.get().getEngine().getView();
try {
Class<?> xWalkCls = Class.forName(
"org.crosswalk.engine.XWalkCordovaView");
Method onShowMethod =
xWalkCls.getMethod("onShow");
onShowMethod.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) getActivity()
.getSystemService(Context.ACTIVITY_SERVICE);
if (am == null || Build.VERSION.SDK_INT < 21)
return;
List<AppTask> tasks = am.getAppTasks();
if (tasks == null || tasks.isEmpty())
return;
tasks.get(0).setExcludeFromRecents(true);
}
/**
* The activity referenced by cordova.
*
* @return The main activity of the app.
*/
Activity getActivity() {
return cordova.get().getActivity();
}
}

View File

@ -0,0 +1,301 @@
/*
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.
*/
package de.appplant.cordova.plugin.background;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaPlugin;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
public class BackgroundMode extends CordovaPlugin {
// Event types for callbacks
private enum Event {
ACTIVATE, DEACTIVATE, FAILURE
}
// Plugin namespace
private static final String JS_NAMESPACE =
"cordova.plugins.backgroundMode";
// Flag indicates if the app is in background or foreground
private boolean inBackground = false;
// Flag indicates if the plugin is enabled or disabled
private boolean isDisabled = true;
// Flag indicates if the service is bind
private boolean isBind = false;
// Default settings for the notification
private static JSONObject defaultSettings = new JSONObject();
// Service that keeps the app awake
private ForegroundService service;
// Used to (un)bind the service to with the activity
private final ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
ForegroundService.ForegroundBinder binder =
(ForegroundService.ForegroundBinder) service;
BackgroundMode.this.service = binder.getService();
}
@Override
public void onServiceDisconnected(ComponentName name) {
fireEvent(Event.FAILURE, "'service disconnected'");
}
};
/**
* 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.
*
* @throws JSONException
*/
@Override
public boolean execute (String action, JSONArray args,
CallbackContext callback) throws JSONException {
if (action.equalsIgnoreCase("configure")) {
JSONObject settings = args.getJSONObject(0);
boolean update = args.getBoolean(1);
configure(settings, update);
}
else
if (action.equalsIgnoreCase("enable")) {
enableMode();
}
else
if (action.equalsIgnoreCase("disable")) {
disableMode();
}
else {
BackgroundExt.execute(action, cordova, webView);
}
callback.success();
return true;
}
/**
* 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();
}
/**
* Called when the activity will start interacting with the user.
*
* @param multitasking Flag indicating if multitasking is turned on for app.
*/
@Override
public void onResume(boolean multitasking) {
super.onResume(multitasking);
inBackground = false;
stopService();
}
/**
* Called when the activity will be destroyed.
*/
@Override
public void onDestroy() {
super.onDestroy();
stopService();
}
/**
* Enable the background mode.
*/
private void enableMode() {
isDisabled = false;
if (inBackground) {
startService();
}
}
/**
* Disable the background mode.
*/
private void disableMode() {
stopService();
isDisabled = true;
}
/**
* Update the default settings and configure the notification.
*
* @param settings The settings
* @param update A truthy value means to update the running service.
*/
private void configure(JSONObject settings, boolean update) {
if (update) {
updateNotification(settings);
} else {
setDefaultSettings(settings);
}
}
/**
* Update the default settings for the notification.
*
* @param settings The new default settings
*/
private void setDefaultSettings(JSONObject settings) {
defaultSettings = settings;
}
/**
* The settings for the new/updated notification.
*
* @return
* updateSettings if set or default settings
*/
protected static JSONObject getSettings() {
return defaultSettings;
}
/**
* Update the notification.
*
* @param settings The config settings
*/
private void updateNotification(JSONObject settings) {
if (isBind) {
service.updateNotification(settings);
}
}
/**
* Bind the activity to a background service and put them into foreground
* state.
*/
private void startService() {
Activity context = cordova.getActivity();
if (isDisabled || isBind)
return;
Intent intent = new Intent(
context, ForegroundService.class);
try {
context.bindService(intent,
connection, Context.BIND_AUTO_CREATE);
fireEvent(Event.ACTIVATE, null);
context.startService(intent);
} catch (Exception e) {
fireEvent(Event.FAILURE, String.format("'%s'", e.getMessage()));
}
isBind = true;
}
/**
* Bind the activity to a background service and put them into foreground
* state.
*/
private void stopService() {
Activity context = cordova.getActivity();
Intent intent = new Intent(
context, ForegroundService.class);
if (!isBind)
return;
fireEvent(Event.DEACTIVATE, null);
context.unbindService(connection);
context.stopService(intent);
isBind = false;
}
/**
* Fire vent with some parameters inside the web view.
*
* @param event The name of the event
* @param params Optional arguments for the event
*/
private void fireEvent (Event event, String params) {
String eventName;
switch (event) {
case ACTIVATE:
eventName = "activate"; break;
case DEACTIVATE:
eventName = "deactivate"; break;
default:
eventName = "failure";
}
String active = event == Event.ACTIVATE ? "true" : "false";
String flag = String.format("%s._isActive=%s;",
JS_NAMESPACE, active);
String depFn = String.format("%s.on%s(%s);",
JS_NAMESPACE, eventName, params);
String fn = String.format("%s.fireEvent('%s',%s);",
JS_NAMESPACE, eventName, params);
final String js = flag + fn + depFn;
cordova.getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
webView.loadUrl("javascript:" + js);
}
});
}
}

View File

@ -0,0 +1,323 @@
/*
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;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.os.PowerManager;
import android.app.NotificationChannel;
import org.json.JSONObject;
import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
/**
* Puts the service in a foreground state, where the system considers it to be
* something the user is actively aware of and thus not a candidate for killing
* when low on memory.
*/
public class ForegroundService extends Service {
// Fixed ID for the 'foreground' notification
public static final int NOTIFICATION_ID = -574543954;
// Default title of the background notification
private static final String NOTIFICATION_TITLE =
"App is running in background";
// Default text of the background notification
private static final String NOTIFICATION_TEXT =
"Doing heavy tasks.";
// Default icon of the background notification
private static final String NOTIFICATION_ICON = "icon";
// Binder given to clients
private final IBinder binder = new ForegroundBinder();
// Partial wake lock to prevent the app from going to sleep when locked
private PowerManager.WakeLock wakeLock;
/**
* Allow clients to call on to the service.
*/
@Override
public IBinder onBind (Intent intent) {
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.
*/
class ForegroundBinder extends Binder
{
ForegroundService getService()
{
// Return this instance of ForegroundService
// so clients can call public methods
return ForegroundService.this;
}
}
/**
* Put the service in a foreground state to prevent app from being killed
* by the OS.
*/
@Override
public void onCreate()
{
super.onCreate();
keepAwake();
}
/**
* No need to run headless on destroy.
*/
@Override
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.
*/
@SuppressLint("WakelockTimeout")
private void keepAwake()
{
JSONObject settings = BackgroundMode.getSettings();
boolean isSilent = settings.optBoolean("silent", false);
if (!isSilent) {
startForeground(NOTIFICATION_ID, makeNotification());
}
PowerManager pm = (PowerManager)getSystemService(POWER_SERVICE);
wakeLock = pm.newWakeLock(
PARTIAL_WAKE_LOCK, "backgroundmode:wakelock");
wakeLock.acquire();
}
/**
* Stop background mode.
*/
private void sleepWell()
{
stopForeground(true);
getNotificationManager().cancel(NOTIFICATION_ID);
if (wakeLock != null) {
wakeLock.release();
wakeLock = null;
}
}
/**
* 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()
{
return makeNotification(BackgroundMode.getSettings());
}
/**
* Create a notification as the visible part to be able to put the service
* in a foreground state.
*
* @param settings The config 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);
Context context = getApplicationContext();
String pkgName = context.getPackageName();
Intent intent = context.getPackageManager()
.getLaunchIntentForPackage(pkgName);
Notification.Builder notification = new Notification.Builder(context)
.setContentTitle(title)
.setContentText(text)
.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);
}
if (bigText || text.contains("\n")) {
notification.setStyle(
new Notification.BigTextStyle().bigText(text));
}
setColor(notification, settings);
if (intent != null && settings.optBoolean("resume")) {
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent contentIntent = PendingIntent.getActivity(
context, NOTIFICATION_ID, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
notification.setContentIntent(contentIntent);
}
return notification.build();
}
/**
* Update the notification.
*
* @param settings The config settings
*/
protected void updateNotification (JSONObject settings)
{
boolean isSilent = settings.optBoolean("silent", false);
if (isSilent) {
stopForeground(true);
return;
}
Notification notification = makeNotification(settings);
getNotificationManager().notify(NOTIFICATION_ID, notification);
}
/**
* Retrieves the resource ID of the app icon.
*
* @param settings A JSON dict containing the icon name.
*/
private int getIconResId (JSONObject settings)
{
String icon = settings.optString("icon", NOTIFICATION_ICON);
int resId = getIconResId(icon, "mipmap");
if (resId == 0) {
resId = getIconResId(icon, "drawable");
}
return resId;
}
/**
* Retrieve resource id of the specified icon.
*
* @param icon The name of the icon.
* @param type The resource type where to look for.
*
* @return The resource id or 0 if not found.
*/
private int getIconResId (String icon, String type)
{
Resources res = getResources();
String pkgName = getPackageName();
int resId = res.getIdentifier(icon, type, pkgName);
if (resId == 0) {
resId = res.getIdentifier("icon", type, pkgName);
}
return resId;
}
/**
* Set notification color if its supported by the SDK.
*
* @param notification A Notification.Builder instance
* @param settings A JSON dict containing the color definition (red: FF0000)
*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void setColor (Notification.Builder notification, JSONObject settings)
{
String hex = settings.optString("color", null);
if (Build.VERSION.SDK_INT < 21 || hex == null)
return;
try {
int aRGB = Integer.parseInt(hex, 16) + 0xFF000000;
notification.setColor(aRGB);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Returns the shared notification service manager.
*/
private NotificationManager getNotificationManager()
{
return (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
}
}

View File

@ -0,0 +1,49 @@
/*
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.
*/
/**
* Activates the background mode. When activated the application
* will be prevented from going to sleep while in background
* for the next time.
*
* @param [ Function ] success The success callback to use.
* @param [ Function ] error The error callback to use.
*
* @return [ Void ]
*/
exports.enable = function (success, error) {
success();
};
/**
* Deactivates the background mode. When deactivated the application
* will not stay awake while in background.
*
* @param [ Function ] success The success callback to use.
* @param [ Function ] error The error callback to use.
*
* @return [ Void ]
*/
exports.disable = function (success, error) {
success();
};
cordova.commandProxy.add('BackgroundMode', exports);

View File

@ -0,0 +1,35 @@
/*
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.
*/
#import <AVFoundation/AVFoundation.h>
#import <Cordova/CDVPlugin.h>
@interface APPBackgroundMode : CDVPlugin {
AVAudioPlayer* audioPlayer;
BOOL enabled;
}
// Activate the background mode
- (void) enable:(CDVInvokedUrlCommand*)command;
// Deactivate the background mode
- (void) disable:(CDVInvokedUrlCommand*)command;
@end

277
src/ios/APPBackgroundMode.m Normal file
View File

@ -0,0 +1,277 @@
/*
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.
*/
#import "APPMethodMagic.h"
#import "APPBackgroundMode.h"
#import <Cordova/CDVAvailability.h>
@implementation APPBackgroundMode
#pragma mark -
#pragma mark Constants
NSString* const kAPPBackgroundJsNamespace = @"cordova.plugins.backgroundMode";
NSString* const kAPPBackgroundEventActivate = @"activate";
NSString* const kAPPBackgroundEventDeactivate = @"deactivate";
#pragma mark -
#pragma mark Life Cycle
/**
* Called by runtime once the Class has been loaded.
* Exchange method implementations to hook into their execution.
*/
+ (void) load
{
[self swizzleWKWebViewEngine];
}
/**
* Initialize the plugin.
*/
- (void) pluginInitialize
{
enabled = NO;
[self configureAudioPlayer];
[self configureAudioSession];
[self observeLifeCycle];
}
/**
* Register the listener for pause and resume events.
*/
- (void) observeLifeCycle
{
NSNotificationCenter* listener = [NSNotificationCenter
defaultCenter];
[listener addObserver:self
selector:@selector(keepAwake)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
[listener addObserver:self
selector:@selector(stopKeepingAwake)
name:UIApplicationWillEnterForegroundNotification
object:nil];
[listener addObserver:self
selector:@selector(handleAudioSessionInterruption:)
name:AVAudioSessionInterruptionNotification
object:nil];
}
#pragma mark -
#pragma mark Interface
/**
* Enable the mode to stay awake
* when switching to background for the next time.
*/
- (void) enable:(CDVInvokedUrlCommand*)command
{
if (enabled)
return;
enabled = YES;
[self execCallback:command];
}
/**
* Disable the background mode
* and stop being active in background.
*/
- (void) disable:(CDVInvokedUrlCommand*)command
{
if (!enabled)
return;
enabled = NO;
[self stopKeepingAwake];
[self execCallback:command];
}
#pragma mark -
#pragma mark Core
/**
* Keep the app awake.
*/
- (void) keepAwake
{
if (!enabled)
return;
[audioPlayer play];
[self fireEvent:kAPPBackgroundEventActivate];
}
/**
* Let the app going to sleep.
*/
- (void) stopKeepingAwake
{
if (TARGET_IPHONE_SIMULATOR) {
NSLog(@"BackgroundMode: On simulator apps never pause in background!");
}
if (audioPlayer.isPlaying) {
[self fireEvent:kAPPBackgroundEventDeactivate];
}
[audioPlayer pause];
}
/**
* Configure the audio player.
*/
- (void) configureAudioPlayer
{
NSString* path = [[NSBundle mainBundle]
pathForResource:@"appbeep" ofType:@"wav"];
NSURL* url = [NSURL fileURLWithPath:path];
audioPlayer = [[AVAudioPlayer alloc]
initWithContentsOfURL:url error:NULL];
audioPlayer.volume = 0;
audioPlayer.numberOfLoops = -1;
};
/**
* Configure the audio session.
*/
- (void) configureAudioSession
{
AVAudioSession* session = [AVAudioSession
sharedInstance];
// Don't activate the audio session yet
[session setActive:NO error:NULL];
// 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
[session setActive:YES error:NULL];
};
#pragma mark -
#pragma mark Helper
/**
* Simply invokes the callback without any parameter.
*/
- (void) execCallback:(CDVInvokedUrlCommand*)command
{
CDVPluginResult *result = [CDVPluginResult
resultWithStatus:CDVCommandStatus_OK];
[self.commandDelegate sendPluginResult:result
callbackId:command.callbackId];
}
/**
* Restart playing sound when interrupted by phone calls.
*/
- (void) handleAudioSessionInterruption:(NSNotification*)notification
{
[self fireEvent:kAPPBackgroundEventDeactivate];
[self keepAwake];
}
/**
* Find out if the app runs inside the webkit powered webview.
*/
+ (BOOL) isRunningWebKit
{
return IsAtLeastiOSVersion(@"8.0") && NSClassFromString(@"CDVWKWebViewEngine");
}
/**
* Method to fire an event with some parameters in the browser.
*/
- (void) fireEvent:(NSString*)event
{
NSString* active =
[event isEqualToString:kAPPBackgroundEventActivate] ? @"true" : @"false";
NSString* flag = [NSString stringWithFormat:@"%@._isActive=%@;",
kAPPBackgroundJsNamespace, active];
NSString* depFn = [NSString stringWithFormat:@"%@.on%@();",
kAPPBackgroundJsNamespace, event];
NSString* fn = [NSString stringWithFormat:@"%@.fireEvent('%@');",
kAPPBackgroundJsNamespace, event];
NSString* js = [NSString stringWithFormat:@"%@%@%@", flag, depFn, fn];
[self.commandDelegate evalJs:js];
}
#pragma mark -
#pragma mark Swizzling
/**
* Method to swizzle.
*/
+ (NSString*) wkProperty
{
NSString* str = @"YWx3YXlzUnVuc0F0Rm9yZWdyb3VuZFByaW9yaXR5";
NSData* data = [[NSData alloc] initWithBase64EncodedString:str options:0];
return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}
/**
* Swizzle some implementations of CDVWKWebViewEngine.
*/
+ (void) swizzleWKWebViewEngine
{
if (![self isRunningWebKit])
return;
Class wkWebViewEngineCls = NSClassFromString(@"CDVWKWebViewEngine");
SEL selector = NSSelectorFromString(@"createConfigurationFromSettings:");
SwizzleSelectorWithBlock_Begin(wkWebViewEngineCls, selector)
^(CDVPlugin *self, NSDictionary *settings) {
id obj = ((id (*)(id, SEL, NSDictionary*))_imp)(self, _cmd, settings);
[obj setValue:[NSNumber numberWithBool:YES]
forKey:[APPBackgroundMode wkProperty]];
[obj setValue:[NSNumber numberWithBool:NO]
forKey:@"requiresUserActionForMediaPlayback"];
return obj;
}
SwizzleSelectorWithBlock_End;
}
@end

93
src/ios/APPMethodMagic.h Normal file
View File

@ -0,0 +1,93 @@
/*
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.
*/
/**
* Code extracted from
* - http://defagos.github.io/yet_another_article_about_method_swizzling/
* - https://gist.github.com/defagos/1312fec96b48540efa5c
*/
#import <objc/runtime.h>
#import <objc/message.h>
#define SwizzleSelector(clazz, selector, newImpl, oldImpl) \
(*oldImpl) = (__typeof((*oldImpl)))class_swizzleSelector((clazz), (selector), (IMP)(newImpl))
#define SwizzleClassSelector(clazz, selector, newImpl, oldImpl) \
(*oldImpl) = (__typeof((*oldImpl)))class_swizzleClassSelector((clazz), (selector), (IMP)(newImpl))
#define SwizzleSelectorWithBlock_Begin(clazz, selector) { \
SEL _cmd = selector; \
__block IMP _imp = class_swizzleSelectorWithBlock((clazz), (selector),
#define SwizzleSelectorWithBlock_End );}
#define SwizzleClassSelectorWithBlock_Begin(clazz, selector) { \
SEL _cmd = selector; \
__block IMP _imp = class_swizzleClassSelectorWithBlock((clazz), (selector),
#define SwizzleClassSelectorWithBlock_End );}
/**
* Swizzle class method specified by class and selector
* through the provided method implementation.
*
* @param [ Class ] clazz The class containing the method.
* @param [ SEL ] selector The selector of the method.
* @param [ IMP ] newImpl The new implementation of the method.
*
* @return [ IMP ] The previous implementation of the method.
*/
IMP class_swizzleClassSelector(Class clazz, SEL selector, IMP newImpl);
/**
* Swizzle class method specified by class and selector
* through the provided code block.
*
* @param [ Class ] clazz The class containing the method.
* @param [ SEL ] selector The selector of the method.
* @param [ id ] newImplBlock The new implementation of the method.
*
* @return [ IMP ] The previous implementation of the method.
*/
IMP class_swizzleClassSelectorWithBlock(Class clazz, SEL selector, id newImplBlock);
/**
* Swizzle method specified by class and selector
* through the provided code block.
*
* @param [ Class ] clazz The class containing the method.
* @param [ SEL ] selector The selector of the method.
* @param [ id ] newImplBlock The new implementation of the method.
*
* @return [ IMP ] The previous implementation of the method.
*/
IMP class_swizzleSelectorWithBlock(Class clazz, SEL selector, id newImplBlock);
/**
* Swizzle method specified by class and selector
* through the provided method implementation.
*
* @param [ Class ] clazz The class containing the method.
* @param [ SEL ] selector The selector of the method.
* @param [ IMP ] newImpl The new implementation of the method.
*
* @return [ IMP ] The previous implementation of the method.
*/
IMP class_swizzleSelector(Class clazz, SEL selector, IMP newImpl);

105
src/ios/APPMethodMagic.m Normal file
View File

@ -0,0 +1,105 @@
/*
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.
*/
/**
* Code extracted from
* - http://defagos.github.io/yet_another_article_about_method_swizzling/
* - https://gist.github.com/defagos/1312fec96b48540efa5c
*/
#import "APPMethodMagic.h"
#import <objc/runtime.h>
#import <objc/message.h>
/**
* Swizzle class method specified by class and selector
* through the provided method implementation.
*
* @param [ Class ] clazz The class containing the method.
* @param [ SEL ] selector The selector of the method.
* @param [ IMP ] newImpl The new implementation of the method.
*
* @return [ IMP ] The previous implementation of the method.
*/
IMP class_swizzleClassSelector(Class clazz, SEL selector, IMP newImpl)
{
return class_swizzleSelector(object_getClass(clazz), selector, newImpl);
}
/**
* Swizzle class method specified by class and selector
* through the provided code block.
*
* @param [ Class ] clazz The class containing the method.
* @param [ SEL ] selector The selector of the method.
* @param [ id ] newImplBlock The new implementation of the method.
*
* @return [ IMP ] The previous implementation of the method.
*/
IMP class_swizzleClassSelectorWithBlock(Class clazz, SEL selector, id newImplBlock)
{
IMP newImpl = imp_implementationWithBlock(newImplBlock);
return class_swizzleClassSelector(clazz, selector, newImpl);
}
/**
* Swizzle method specified by class and selector
* through the provided code block.
*
* @param [ Class ] clazz The class containing the method.
* @param [ SEL ] selector The selector of the method.
* @param [ id ] newImplBlock The new implementation of the method.
*
* @return [ IMP ] The previous implementation of the method.
*/
IMP class_swizzleSelectorWithBlock(Class clazz, SEL selector, id newImplBlock)
{
IMP newImpl = imp_implementationWithBlock(newImplBlock);
return class_swizzleSelector(clazz, selector, newImpl);
}
/**
* Swizzle method specified by class and selector
* through the provided method implementation.
*
* @param [ Class ] clazz The class containing the method.
* @param [ SEL ] selector The selector of the method.
* @param [ IMP ] newImpl The new implementation of the method.
*
* @return [ IMP ] The previous implementation of the method.
*/
IMP class_swizzleSelector(Class clazz, SEL selector, IMP newImpl)
{
Method method = class_getInstanceMethod(clazz, selector);
const char *types = method_getTypeEncoding(method);
class_addMethod(clazz, selector, imp_implementationWithBlock(^(__unsafe_unretained id self, va_list argp) {
struct objc_super super = {
.receiver = self,
.super_class = class_getSuperclass(clazz)
};
id (*objc_msgSendSuper_typed)(struct objc_super*, SEL, va_list) = (void*)&objc_msgSendSuper;
return objc_msgSendSuper_typed(&super, selector, argp);
}), types);
return class_replaceMethod(clazz, selector, newImpl, types);
}

View File

@ -0,0 +1,123 @@
/*
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.
*/
var plugin = cordova.plugins.backgroundMode;
var Uri = Windows.Foundation.Uri,
MediaSource = Windows.Media.Core.MediaSource,
MediaPlaybackItem = Windows.Media.Playback.MediaPlaybackItem,
MediaPlaybackList = Windows.Media.Playback.MediaPlaybackList,
AudioCategory = Windows.Media.Playback.MediaPlayerAudioCategory,
MediaPlayer = Windows.Media.Playback.MediaPlayer,
WebUIApplication = Windows.UI.WebUI.WebUIApplication;
/**
* Activates the background mode. When activated the application
* will be prevented from going to sleep while in background
* for the next time.
*
* @param [ Function ] success The success callback to use.
* @param [ Function ] error The error callback to use.
*
* @return [ Void ]
*/
exports.enable = function (success, error) {
success();
};
/**
* Deactivates the background mode. When deactivated the application
* will not stay awake while in background.
*
* @param [ Function ] success The success callback to use.
* @param [ Function ] error The error callback to use.
*
* @return [ Void ]
*/
exports.disable = function (success, error) {
exports.stopKeepingAwake();
success();
};
/**
* Keep the app awake.
*
* @return [ Void ]
*/
exports.keepAwake = function () {
if (!plugin.isEnabled() || plugin.isActive())
return;
exports.configureAudioPlayer();
exports.audioPlayer.play();
plugin._isActive = true;
plugin.fireEvent('activate');
};
/**
* Let the app going to sleep.
*
* @return [ Void ]
*/
exports.stopKeepingAwake = function () {
if (!exports.audioPlayer)
return;
exports.audioPlayer.close();
exports.audioPlayer = null;
cordova.plugins.backgroundMode._isActive = false;
cordova.plugins.backgroundMode.fireEvent('deactivate');
};
/**
* Configure the audio player for playback in background.
*
* @return [ Void ]
*/
exports.configureAudioPlayer = function () {
if (exports.audioPlayer)
return;
var pkg = Windows.ApplicationModel.Package.current,
pkgName = pkg.id.name;
var audioPlayer = new MediaPlayer(),
audioFile = new Uri('ms-appx://' + pkgName + '/appbeep.wma'),
audioSource = MediaSource.createFromUri(audioFile),
playList = new MediaPlaybackList();
playList.items.append(new MediaPlaybackItem(audioSource));
playList.autoRepeatEnabled = true;
audioPlayer.source = playList;
audioPlayer.autoPlay = false;
audioPlayer.audioCategory = AudioCategory.soundEffects;
audioPlayer.volume = 0;
exports.audioPlayer = audioPlayer;
};
WebUIApplication.addEventListener('enteredbackground', exports.keepAwake, false);
WebUIApplication.addEventListener('leavingbackground', exports.stopKeepingAwake, false);
cordova.commandProxy.add('BackgroundMode', exports);

403
www/background-mode.js Normal file
View File

@ -0,0 +1,403 @@
/*
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.
*/
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
* for the next time.
*
* @return [ Void ]
*/
exports.enable = function () {
if (this.isEnabled())
return;
var fn = function () {
exports._isEnabled = true;
exports.fireEvent('enable');
};
cordova.exec(fn, null, 'BackgroundMode', 'enable', []);
};
/**
* Deactivates the background mode. When deactivated the application
* will not stay awake while in background.
*
* @return [ Void ]
*/
exports.disable = function () {
if (!this.isEnabled())
return;
var fn = function () {
exports._isEnabled = false;
exports.fireEvent('disable');
};
cordova.exec(fn, null, 'BackgroundMode', 'disable', []);
};
/**
* Enable or disable the background mode.
*
* @param [ Bool ] enable The status to set for.
*
* @return [ Void ]
*/
exports.setEnabled = function (enable) {
if (enable) {
this.enable();
} else {
this.disable();
}
};
/**
* List of all available options with their default value.
*
* @return [ Object ]
*/
exports.getDefaults = function () {
return this._defaults;
};
/**
* Overwrite the default settings.
*
* @param [ Object ] overrides Dict of options to be overridden.
*
* @return [ Void ]
*/
exports.setDefaults = function (overrides) {
var defaults = this.getDefaults();
for (var key in defaults) {
if (overrides.hasOwnProperty(key)) {
defaults[key] = overrides[key];
}
}
if (this._isAndroid) {
cordova.exec(null, null, 'BackgroundMode', 'configure', [defaults, false]);
}
};
/**
* Configures the notification settings for Android.
* Will be merged with the defaults.
*
* @param [ Object ] overrides Dict of options to be overridden.
*
* @return [ Void ]
*/
exports.configure = function (options) {
var settings = this.mergeWithDefaults(options);
if (this._isAndroid) {
cordova.exec(null, null, 'BackgroundMode', 'configure', [settings, true]);
}
};
/**
* Enable GPS-tracking in background (Android).
*
* @return [ Void ]
*/
exports.disableWebViewOptimizations = function () {
if (this._isAndroid) {
cordova.exec(null, null, 'BackgroundMode', 'optimizations', []);
}
};
/**
* Move app to background (Android only).
*
* @return [ Void ]
*/
exports.moveToBackground = function () {
if (this._isAndroid) {
cordova.exec(null, null, 'BackgroundMode', 'background', []);
}
};
/**
* Move app to foreground when in background (Android only).
*
* @return [ Void ]
*/
exports.moveToForeground = function () {
if (this.isActive() && this._isAndroid) {
cordova.exec(null, null, 'BackgroundMode', 'foreground', []);
}
};
/**
* Exclude the app from the recent tasks list (Android only).
*
* @return [ Void ]
*/
exports.excludeFromTaskList = function () {
if (this._isAndroid) {
cordova.exec(null, null, 'BackgroundMode', 'tasklist', []);
}
};
/**
* Override the back button on Android to go to background
* instead of closing the app.
*
* @return [ Void ]
*/
exports.overrideBackButton = function () {
document.addEventListener('backbutton', function() {
exports.moveToBackground();
}, false);
};
/**
* If the mode is enabled or disabled.
*
* @return [ Boolean ]
*/
exports.isEnabled = function () {
return this._isEnabled !== false;
};
/**
* If the mode is active.
*
* @return [ Boolean ]
*/
exports.isActive = function () {
return this._isActive !== false;
};
/**********
* EVENTS *
**********/
exports._listener = {};
/**
* Fire event with given arguments.
*
* @param [ String ] event The event's name.
* @param [ Array<Object> ] The callback's arguments.
*
* @return [ Void ]
*/
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++) {
var fn = listener[i][0],
scope = listener[i][1];
fn.apply(scope, args);
}
};
/**
* Register callback for given event.
*
* @param [ String ] event The event's name.
* @param [ Function ] callback The function to be exec as callback.
* @param [ Object ] scope The callback function's scope.
*
* @return [ Void ]
*/
exports.on = function (event, callback, scope) {
if (typeof callback !== "function")
return;
if (!this._listener[event]) {
this._listener[event] = [];
}
var item = [callback, scope || window];
this._listener[event].push(item);
};
/**
* Unregister callback for given event.
*
* @param [ String ] event The event's name.
* @param [ Function ] callback The function to be exec as callback.
*
* @return [ Void ]
*/
exports.un = function (event, callback) {
var listener = this._listener[event];
if (!listener)
return;
for (var i = 0; i < listener.length; i++) {
var fn = listener[i][0];
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 () {};
/*********
* UTILS *
*********/
/**
* @private
*
* Merge settings with default values.
*
* @param [ Object ] options The custom options.
*
* @return [ Object ] Default values merged with custom values.
*/
exports.mergeWithDefaults = function (options) {
var defaults = this.getDefaults();
for (var key in defaults) {
if (!options.hasOwnProperty(key)) {
options[key] = defaults[key];
continue;
}
}
return options;
};
/**
* @private
*
* Initialize the plugin.
*
* Method should be called after the 'deviceready' event
* but before the event listeners will be called.
*
* @return [ Void ]
*/
exports.pluginInitialize = function () {
this._isAndroid = device.platform.match(/^android|amazon/i) !== null;
this.setDefaults({});
if (device.platform == 'browser') {
this.enable();
this._isEnabled = true;
}
this._isActive = this._isActive || device.platform == 'browser';
};
/***********
* PRIVATE *
***********/
/**
* @private
*
* Flag indicates if the mode is enabled.
*/
exports._isEnabled = false;
/**
* @private
*
* Flag indicates if the mode is active.
*/
exports._isActive = false;
/**
* @private
*
* Default values of all available options.
*/
exports._defaults = {
title: 'App is running in background',
text: 'Doing heavy tasks.',
bigText: false,
resume: true,
silent: false,
hidden: true,
color: undefined,
icon: 'icon'
};
// Called before 'deviceready' listener will be called
channel.onCordovaReady.subscribe(function () {
channel.onCordovaInfoReady.subscribe(function () {
exports.pluginInitialize();
});
});
// Called after 'deviceready' event
channel.deviceready.subscribe(function () {
if (exports.isEnabled()) {
exports.fireEvent('enable');
}
if (exports.isActive()) {
exports.fireEvent('activate');
}
});