mirror of
https://github.com/shuto-cn/cordova-plugin-background-mode.git
synced 2024-10-05 16:22:09 +08:00
更新
This commit is contained in:
parent
bc023a25c8
commit
69e5e3b24d
87
CHANGELOG.md
Normal file
87
CHANGELOG.md
Normal 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
202
LICENSE
Normal 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
59
package.json
Normal 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
132
plugin.xml
Normal 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=">=3.0.0" />
|
||||
<engine name="android-sdk" version=">=16" />
|
||||
<engine name="windows-sdk" version=">=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>
|
178
src/android/BackgroundExt.java
Normal file
178
src/android/BackgroundExt.java
Normal 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();
|
||||
}
|
||||
|
||||
}
|
301
src/android/BackgroundMode.java
Normal file
301
src/android/BackgroundMode.java
Normal 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
323
src/android/ForegroundService.java
Normal file
323
src/android/ForegroundService.java
Normal 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);
|
||||
}
|
||||
}
|
49
src/browser/BackgroundModeProxy.js
Normal file
49
src/browser/BackgroundModeProxy.js
Normal 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);
|
35
src/ios/APPBackgroundMode.h
Normal file
35
src/ios/APPBackgroundMode.h
Normal 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
277
src/ios/APPBackgroundMode.m
Normal 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
93
src/ios/APPMethodMagic.h
Normal 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
105
src/ios/APPMethodMagic.m
Normal 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);
|
||||
}
|
123
src/windows/BackgroundModeProxy.js
Normal file
123
src/windows/BackgroundModeProxy.js
Normal 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
403
www/background-mode.js
Normal 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');
|
||||
}
|
||||
});
|
Loading…
Reference in New Issue
Block a user