From 5fa96556ae01334a48caf9ac68ce25a4e55fc1f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Katzer?= Date: Sun, 14 Dec 2014 00:04:12 +0100 Subject: [PATCH] Onactivate, ondeactivate & onfailure callbacks --- CHANGELOG.md | 1 + README.md | 57 ++++++++-- plugin.xml | 7 +- src/android/BackgroundMode.java | 51 +++++++-- src/ios/APPBackgroundMode.m | 25 +++++ src/wp8/BackgroundMode.cs | 183 ++++++++++++++++++++++---------- www/background-mode.js | 18 ++++ 7 files changed, 269 insertions(+), 73 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b5fb091..8bdc56f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## ChangeLog #### Version 0.6.0 (not yet released) - [feature:] Android support +- [feature:] `onactivate`, `ondeactivate` and `onfailure` callbacks. - [___change___:] Disabled by default - [enhancement:] iOS does not require user permissions, internet connection and geo location anymore. diff --git a/README.md b/README.md index 8fd42eb..b787543 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ The plugin focuses on enterprise-only distribution and may not compliant with al 1. [Supported Platforms](#supported-platforms) 2. [Installation](#installation) 3. [ChangeLog](#changelog) -4. [Using the plugin](#using-the-plugin) +4. [Usage](#usage) 5. [Examples](#examples) 6. [Platform specifics](#platform-specifics) @@ -65,6 +65,7 @@ More informations can be found [here][PGB_plugin]. ## ChangeLog #### Version 0.6.0 (not yet released) - [feature:] Android support +- [feature:] `onactivate`, `ondeactivate` and `onfailure` callbacks. - [___change___:] Disabled by default - [enhancement:] iOS does not require user permissions, internet connection and geo location anymore. @@ -72,13 +73,19 @@ More informations can be found [here][PGB_plugin]. - The former `plugin.backgroundMode` namespace has been deprecated and will be removed with the next major release. - See [CHANGELOG.md][changelog] to get the full changelog for the plugin. +#### Known issues +- Plug-in is broken on Windows Phone 8.1 platform. -## Using the plugin + +## Usage The plugin creates the object ```cordova.plugins.backgroundMode``` with the following methods: 1. [backgroundMode.enable][enable] 2. [backgroundMode.disable][disable] 2. [backgroundMode.configure][configure] +3. [backgroundMode.onactivate][onactivate] +4. [backgroundMode.ondeactivate][ondeactivate] +5. [backgroundMode.onfailure][onfailure] ### Plugin initialization The plugin and its methods are not available before the *deviceready* event has been fired. @@ -110,16 +117,49 @@ The background mode can be disabled through the `backgroundMode.disable` interfa cordova.plugins.backgroundMode.disable(); ``` +### Get informed when the background mode has been activated +The `backgroundMode.onactivate` interface can be used to get notified when the background mode has been activated. + +```javascript +cordova.plugins.backgroundMode.onactivate = function() {}; +``` + +### Get informed when the background mode has been deactivated +The `backgroundMode.ondeactivate` interface can be used to get notified when the background mode has been deactivated. + +#### Further informations +- Once the mode has been deactivated the app will be paused soon after the callback has been fired. + +```javascript +cordova.plugins.backgroundMode.ondeactivate = function() {}; +``` + +### Get informed when the background mode could not been activated +The `backgroundMode.onfailure` interface can be used to get notified when the background mode could not been activated. + +The listener has to be a function and takes the following arguments: + - errorCode: Error code which describes the error + +```javascript +cordova.plugins.backgroundMode.onfailure = function(errorCode) {}; +``` + ## Examples The following example demonstrates how to enable the background mode after device is ready. The mode itself will be activated when the app has entered the background. ```javascript document.addEventListener('deviceready', function () { - // Enable background mode - cordova.plugins.backgroundMode.enable(); // Android customization cordova.plugins.backgroundMode.configure({ text:'Doing heavy tasks.'}); + // Enable background mode + cordova.plugins.backgroundMode.enable(); + // Called when background mode has been activated + cordova.plugins.backgroundMode.onactivate = function () { + setInterval(function () { + console.log('App is running in background'); + }); + } }, false); ``` @@ -170,8 +210,11 @@ This software is released under the [Apache 2.0 License][apache2_license]. [PGB]: http://docs.build.phonegap.com/en_US/index.html [PGB_plugin]: https://build.phonegap.com/plugins/490 [changelog]: CHANGELOG.md -[enable]: #prevent-the-app-from-going-to-sleep-in-background -[disable]: #pause-the-app-while-in-background -[configure]: #android-customization +[enable]: #prevent_the_app_from_going_to_sleep_in_background +[disable]: #pause_the_app_while_in_background +[configure]: #android_customization +[onactivate]: #get_informed_when_the_background_mode_has_been_activated +[ondeactivate]: #get_informed_when_the_background_mode_has_been_deactivated +[onfailure]: #get_informed_when_the_background_mode_could_not_been_activated [apache2_license]: http://opensource.org/licenses/Apache-2.0 [appplant]: http://appplant.de diff --git a/plugin.xml b/plugin.xml index 99537d0..dd9f4cc 100644 --- a/plugin.xml +++ b/plugin.xml @@ -75,9 +75,7 @@ * it to be something the user is actively aware of and thus not a * candidate for killing when low on memory. --> - + @@ -95,8 +93,7 @@ - - + diff --git a/src/android/BackgroundMode.java b/src/android/BackgroundMode.java index 3b4fac6..3c4fe34 100644 --- a/src/android/BackgroundMode.java +++ b/src/android/BackgroundMode.java @@ -37,6 +37,11 @@ import android.util.Log; public class BackgroundMode extends CordovaPlugin { + // Event types for callbacks + private enum Event { + ACTIVATE, DEACTIVATE, FAILURE + } + // Flag indicates if the app is in background or foreground private boolean inBackground = false; @@ -55,12 +60,11 @@ public class BackgroundMode extends CordovaPlugin { @Override public void onServiceConnected(ComponentName name, IBinder binder) { // Nothing to do here - Log.d("BackgroundMode", "Service connected"); } @Override public void onServiceDisconnected(ComponentName name) { - Log.w("BackgroundMode", "Service disrupted"); + // Nothing to do here } }; @@ -79,7 +83,7 @@ public class BackgroundMode extends CordovaPlugin { */ @Override public boolean execute (String action, JSONArray args, - CallbackContext callback) throws JSONException { + CallbackContext callback) throws JSONException { if (action.equalsIgnoreCase("configure")) { setSettings(args.getJSONObject(0)); @@ -188,10 +192,16 @@ public class BackgroundMode extends CordovaPlugin { return; } - context.bindService( - intent, connection, Context.BIND_AUTO_CREATE); + try { + context.bindService( + intent, connection, Context.BIND_AUTO_CREATE); - context.startService(intent); + context.startService(intent); + + fireEvent(Event.ACTIVATE, null); + } catch (Exception e) { + fireEvent(Event.FAILURE, e.getMessage()); + } isBind = true; } @@ -207,6 +217,7 @@ public class BackgroundMode extends CordovaPlugin { context, ForegroundService.class); if (isBind) { + fireEvent(Event.DEACTIVATE, null); context.unbindService(connection); } @@ -214,4 +225,32 @@ public class BackgroundMode extends CordovaPlugin { 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 js = String.format("setTimeout('cordova.plugins.backgroundMode" + + ".on%s(%s)',0)", + eventName, params); + + webView.loadUrl("javascript:" + js); + } } diff --git a/src/ios/APPBackgroundMode.m b/src/ios/APPBackgroundMode.m index 91e6640..b0429a1 100644 --- a/src/ios/APPBackgroundMode.m +++ b/src/ios/APPBackgroundMode.m @@ -23,6 +23,11 @@ @implementation APPBackgroundMode +NSString *const kAPPBackgroundModeNamespace = @"cordova.plugins.backgroundMode"; +NSString *const kAPPBackgroundModeActivateEvent = @"activate"; +NSString *const kAPPBackgroundModeDeactivateEvent = @"deactivate"; +NSString *const kAPPBackgroundModeFailureEvent = @"failure"; + #pragma mark - #pragma mark Initialization methods @@ -99,6 +104,7 @@ - (void) keepAwake { if (enabled) { [audioPlayer play]; + [self fireEvent:kAPPBackgroundModeActivateEvent withParams:NULL]; } } @@ -111,6 +117,10 @@ NSLog(@"BackgroundMode: On simulator apps never pause in background!"); } + if (audioPlayer.isPlaying) { + [self fireEvent:kAPPBackgroundModeDeactivateEvent withParams:NULL]; + } + [audioPlayer pause]; } @@ -149,11 +159,26 @@ [session setActive:YES error:NULL]; }; +#pragma mark - +#pragma mark Helper methods + /** * Restart playing sound when interrupted by phone calls. */ - (void) handleAudioSessionInterruption:(NSNotification*)notification { + [self fireEvent:kAPPBackgroundModeDeactivateEvent withParams:NULL]; [self keepAwake]; } +/** + * Method to fire an event with some parameters in the browser. + */ +- (void) fireEvent:(NSString*)event withParams:(NSString*)params +{ + NSString* js = [NSString stringWithFormat:@"setTimeout('%@.on%@(%@)',0)", + kAPPBackgroundModeNamespace, event, params]; + + [self.commandDelegate evalJs:js]; +} + @end diff --git a/src/wp8/BackgroundMode.cs b/src/wp8/BackgroundMode.cs index 6eeb9e0..81d3df8 100644 --- a/src/wp8/BackgroundMode.cs +++ b/src/wp8/BackgroundMode.cs @@ -21,6 +21,9 @@ using WPCordovaClassLib.Cordova.Commands; using Windows.Devices.Geolocation; +using Microsoft.Phone.Shell; +using System; +using WPCordovaClassLib.Cordova; namespace Cordova.Extension.Commands { @@ -30,97 +33,167 @@ namespace Cordova.Extension.Commands public class BackgroundMode : BaseCommand { /// - /// Flag gibt an, ob die App im Hintergrund wach gehalten werden soll + /// Event types for callbacks /// - private static bool IsEnabled = true; + enum Event { + ACTIVATE, DEACTIVATE, FAILURE + } + + #region Instance variables /// - /// Lokalisiert das Smartphone + /// Flag indicates if the plugin is enabled or disabled + /// + private bool IsDisabled = true; + + /// + /// Geolocator to monitor location changes /// private static Geolocator Geolocator { get; set; } - /// - /// Registriert die Listener für die (sleep/resume) Events und startet - /// bzw. stoppt die Geo-Lokalisierung - /// - public BackgroundMode () - { - Activate(); - } + #endregion + + #region Interface methods /// - /// @js-interface - /// Setzt den Flag, dass die App im Hintergrund wach gehalten werden soll + /// Enable the mode to stay awake when switching + /// to background for the next time. /// public void enable (string args) { - Enable(); + IsDisabled = false; } /// - /// @js-interface - /// Entfernt den Flag, sodass die App im Hintergrund pausiert wird + /// Disable the background mode and stop + /// being active in background. /// public void disable (string args) { - Disable(); - } - - /// - /// Setzt den Flag, dass die App im Hintergrund wach gehalten werden soll - /// - public static void Enable () - { - IsEnabled = true; - } - - /// - /// Entfernt den Flag, sodass die App im Hintergrund pausiert wird - /// - public static void Disable () - { - IsEnabled = false; + IsDisabled = true; Deactivate(); } - /// - /// Startet das Aktualisieren des Standpunktes - /// - public static void Activate () - { - if (Geolocator == null && IsEnabled && IsServiceAvailable()) - { - Geolocator = new Geolocator(); + #endregion - Geolocator.DesiredAccuracy = PositionAccuracy.Default; - Geolocator.MovementThreshold = 100000; - Geolocator.PositionChanged += geolocator_PositionChanged; + #region Core methods + + /// + /// Keep the app awake by tracking + /// for position changes. + /// + private void Activate() + { + if (IsDisabled || Geolocator != null) + return; + + if (!IsServiceAvailable()) + { + FireEvent(Event.FAILURE, null); + return; } + + Geolocator = new Geolocator(); + + Geolocator.DesiredAccuracy = PositionAccuracy.Default; + Geolocator.MovementThreshold = 100000; + Geolocator.PositionChanged += geolocator_PositionChanged; + + FireEvent(Event.ACTIVATE, null); } /// - /// Beendet das Aktualisieren des Standpunktes + /// Let the app going to sleep. /// - public static void Deactivate () + private void Deactivate () { - if (Geolocator != null) - { - Geolocator.PositionChanged -= geolocator_PositionChanged; - Geolocator = null; - } + if (Geolocator == null) + return; + + FireEvent(Event.DEACTIVATE, null); + + Geolocator.PositionChanged -= geolocator_PositionChanged; + Geolocator = null; } - private static void geolocator_PositionChanged (Geolocator sender, PositionChangedEventArgs args) {} + #endregion + + #region Helper methods /// - /// Gibt an, ob der Lokalisierungsdienst verfügbar ist + /// Determine if location service is available and enabled. /// - private static bool IsServiceAvailable() + private bool IsServiceAvailable() { Geolocator geolocator = (Geolocator == null) ? new Geolocator() : Geolocator; - return !(geolocator.LocationStatus == PositionStatus.Disabled || geolocator.LocationStatus == PositionStatus.NotAvailable); + PositionStatus status = geolocator.LocationStatus; + + if (status == PositionStatus.Disabled) + return false; + + if (status == PositionStatus.NotAvailable) + return false; + + return true; } + + /// + /// Fires the given event. + /// + private void FireEvent(Event Event, string Param) + { + string EventName; + + switch (Event) { + case Event.ACTIVATE: + EventName = "activate"; break; + case Event.DEACTIVATE: + EventName = "deactivate"; break; + default: + EventName = "failure"; break; + } + + string js = String.Format("cordova.plugins.backgroundMode.on{0}({1})", EventName, Param); + + PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, js); + + pluginResult.KeepCallback = true; + + DispatchCommandResult(pluginResult); + } + + #endregion + + #region Delegate methods + + private void geolocator_PositionChanged(Geolocator sender, PositionChangedEventArgs args) + { + // Nothing to do here + } + + #endregion + + #region Lifecycle methods + + /// + /// Occurs when the application is being deactivated. + /// + public override void OnPause(object sender, DeactivatedEventArgs e) + { + Activate(); + } + + /// + /// Occurs when the application is being made active after previously being put + /// into a dormant state or tombstoned. + /// + public override void OnResume(object sender, ActivatedEventArgs e) + { + Deactivate(); + } + + #endregion } } diff --git a/www/background-mode.js b/www/background-mode.js index e11e2ed..7e38375 100644 --- a/www/background-mode.js +++ b/www/background-mode.js @@ -87,6 +87,24 @@ exports.configure = function (options) { } }; +/** + * Called when the background mode has been activated. + */ +exports.onactivate = function () {}; + +/** + * Called when the background mode has been deaktivated. + */ +exports.ondeactivate = function () {}; + +/** + * Called when the background mode could not been activated. + * + * @param {Integer} errorCode + * Error code which describes the error + */ +exports.onfailure = function () {}; + /** * @private *