Repository: Red-Folder/bgs-core Branch: master Commit: 00aeedab7c10 Files: 13 Total size: 67.1 KB Directory structure: gitextract_sh7pmjsj/ ├── LICENSE ├── README.md ├── aidl/ │ └── android/ │ ├── BackgroundServiceApi.aidl │ └── BackgroundServiceListener.aidl ├── package.json ├── plugin.xml ├── src/ │ └── android/ │ ├── BackgroundService.java │ ├── BackgroundServicePlugin.java │ ├── BackgroundServicePluginLogic.java │ ├── BootReceiver.java │ ├── PropertyHelper.java │ └── ReflectionHelper.java └── www/ └── backgroundService.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: LICENSE ================================================ Copyright 2013 Red Folder Consultancy Ltd 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. ================================================ FILE: README.md ================================================ # Important Notice Please be aware that I'm no longer actively maintaining this plugin. I believe the problem it solved is now better handled with PWA technologies like Service Worker and Push Notifications. I would also consider technologies such as Phonegap & Cordova to largely to have been eclipsed by PWAs. Please keep the above in mind before actively using this plugin. # Background Service Plugin Core (bgs-core) ## Quick summary This repository provides the core functionality for the Background Service Plugin for Cordova. The functionality is not a complete plugin - it is intended to be extended by developers to create their own background service. An example background service is provided in the https://github.com/Red-Folder/bgs-sample repository. ## Getting started To understand how to create your own background service it is advised that you read the following articles: * [Using the MyService Sample] (https://github.com/Red-Folder/bgs-core/wiki/Using-the-MyService-Sample) * [Build your own Background Service] (https://github.com/Red-Folder/bgs-core/wiki/Build-your-own-Background-Service) * [Build your own plugin] (https://github.com/Red-Folder/bgs-core/wiki/Build-your-own-plugin) Further documentation can be found at https://github.com/Red-Folder/bgs-core/wiki ## Questions and Support If you have problems, then please log an issue against the bgs-core repository (https://github.com/Red-Folder/bgs-core/issues). As the plugin is updated (for new features or fixes) I will tweet via @FolderRed and blog http://red-folder.blogspot.co.uk/ ## Older version This version of the plugin is intended to work with Cordova 3.0.0+ The older version of the plugin is available at https://github.com/Red-Folder/Cordova-Plugin-BackgroundService. Please note however that this older repository is unlikely to be maintained going forwards. It is advised that you move to Cordova 3.x.x as soon as possible. ## Spread the love If you find the Background Service Plugin useful then spread the love. All the work I do on the Plugin is done in my spare time - time I would otherwise be spending taking my wife out for a nice meal or helping my lad find vinyl records (he's currently very much into The Smiths, Fleetwood Mac and Kate Bush). The Plugin is free and will always remain free. I will continue to develop, maintain and distribute the Plugin under the MIT License. https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=E64TCFQ3NLHZ8 ## Licence Copyright 2013 Red Folder Consultancy Ltd 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. ================================================ FILE: aidl/android/BackgroundServiceApi.aidl ================================================ package com.red_folder.phonegap.plugin.backgroundservice; import com.red_folder.phonegap.plugin.backgroundservice.BackgroundServiceListener; interface BackgroundServiceApi { String getLatestResult(); void addListener(BackgroundServiceListener listener); void removeListener(BackgroundServiceListener listener); boolean isTimerEnabled(); void enableTimer(int milliseconds); void disableTimer(); String getConfiguration(); void setConfiguration(String configuration); int getTimerMilliseconds(); void run(); } ================================================ FILE: aidl/android/BackgroundServiceListener.aidl ================================================ package com.red_folder.phonegap.plugin.backgroundservice; interface BackgroundServiceListener { void handleUpdate(); String getUniqueID(); } ================================================ FILE: package.json ================================================ { "name": "com.red_folder.phonegap.plugin.backgroundservice", "version": "2.0.0", "description": "Framework code that allows the development and operation of an Android Background Service.", "repository": { "type": "git", "url": "git+https://github.com/Red-Folder/bgs-core.git" }, "keywords": [ "background", "cordova" ], "platforms": [ "android" ], "engines": [ { "name": "cordova", "version": ">=3.0.0" } ], "author": "Red-Folder", "license": "Apache 2.0", "bugs": { "url": "https://github.com/Red-Folder/bgs-core/issues" }, "homepage": "https://github.com/Red-Folder/bgs-core#readme" } ================================================ FILE: plugin.xml ================================================ Background Service Plugin - Core logic Framework code that allows the development and operation of an Android Background Service. Apache 2.0 cordova,background ================================================ FILE: src/android/BackgroundService.java ================================================ package com.red_folder.phonegap.plugin.backgroundservice; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Timer; import java.util.TimerTask; import org.json.JSONObject; import android.app.Service; import android.content.Intent; import android.content.SharedPreferences; import android.os.IBinder; import android.os.RemoteException; import android.preference.PreferenceManager; import android.util.Log; import com.red_folder.phonegap.plugin.backgroundservice.BackgroundServiceApi; public abstract class BackgroundService extends Service { /* ************************************************************************************************ * Static values ************************************************************************************************ */ private static final String TAG = BackgroundService.class.getSimpleName(); /* ************************************************************************************************ * Fields ************************************************************************************************ */ private Boolean mServiceInitialised = false; private Timer mTimer; private final Object mResultLock = new Object(); private JSONObject mLatestResult = null; private List mListeners = new ArrayList(); private TimerTask mUpdateTask; private Date mPausedUntil = null; public void setPauseDuration(long pauseDuration) { this.mPausedUntil = new Date(new Date().getTime() + pauseDuration); // Call the onPause event onPause(); } public Boolean getEnabled() { SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); return sharedPrefs.getBoolean(this.getClass().getName() + ".Enabled", false); } public void setEnabled(Boolean enabled) { SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); SharedPreferences.Editor editor = sharedPrefs.edit(); editor.putBoolean(this.getClass().getName() + ".Enabled", enabled); editor.commit(); // Very important } public int getMilliseconds() { SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); // Should default to a minute return sharedPrefs.getInt(this.getClass().getName() + ".Milliseconds", 60000 ); } public void setMilliseconds(int milliseconds) { SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); SharedPreferences.Editor editor = sharedPrefs.edit(); editor.putInt(this.getClass().getName() + ".Milliseconds", milliseconds); editor.commit(); // Very important } protected JSONObject getLatestResult() { synchronized (mResultLock) { return mLatestResult; } } protected void setLatestResult(JSONObject value) { synchronized (mResultLock) { this.mLatestResult = value; } } public void restartTimer() { // Stop the timertask and restart for the new interval to take effect if (this.mUpdateTask != null) { this.mUpdateTask.cancel(); this.mUpdateTask = null; this.mUpdateTask = getTimerTask(); this.mTimer.schedule(this.mUpdateTask, getMilliseconds(), getMilliseconds()); } } /* ************************************************************************************************ * Overriden Methods ************************************************************************************************ */ @Override public IBinder onBind(Intent intent) { Log.i(TAG, "onBind called"); return apiEndpoint; } @Override public int onStartCommand(Intent intent, int flags, int startId) { super.onStartCommand(intent, flags, startId); Log.d(TAG, "onStartCommand run"); initialiseService(); return START_STICKY; } @Override public void onDestroy() { super.onDestroy(); Log.i(TAG, "Service destroying"); cleanupService(); } /* ************************************************************************************************ * Protected methods ************************************************************************************************ */ protected void runOnce() { // Runs the doWork once // Sets the last result & updates the listeners doWorkWrapper(); } /* ************************************************************************************************ * Private methods ************************************************************************************************ */ private BackgroundServiceApi.Stub apiEndpoint = new BackgroundServiceApi.Stub() { /* ************************************************************************************************ * Overriden Methods ************************************************************************************************ */ @Override public String getLatestResult() throws RemoteException { synchronized (mResultLock) { if (mLatestResult == null) return "{}"; else return mLatestResult.toString(); } } @Override public void addListener(BackgroundServiceListener listener) throws RemoteException { synchronized (mListeners) { if (mListeners.add(listener)) Log.d(TAG, "Listener added"); else Log.d(TAG, "Listener not added"); } } @Override public void removeListener(BackgroundServiceListener listener) throws RemoteException { synchronized (mListeners) { if (mListeners.size() > 0) { boolean removed = false; for (int i = 0; i < mListeners.size() && !removed; i++) { if (listener.getUniqueID().equals(mListeners.get(i).getUniqueID())) { mListeners.remove(i); removed = true; } } if (removed) Log.d(TAG, "Listener removed"); else Log.d(TAG, "Listener not found"); } } } @Override public void enableTimer(int milliseconds) throws RemoteException { // First stop it just to be on the safe side stopTimerTask(); // Then enable and set the milliseconds setEnabled(true); setMilliseconds(milliseconds); // Finally setup the TimerTask setupTimerTask(); } @Override public void disableTimer() throws RemoteException { // Set to disabled setEnabled(false); // Stop the timer task stopTimerTask(); } @Override public boolean isTimerEnabled() throws RemoteException { return getEnabled(); } @Override public String getConfiguration() throws RemoteException { JSONObject array = getConfig(); if (array == null) return ""; else return array.toString(); } @Override public void setConfiguration(String configuration) throws RemoteException { try { JSONObject array = null; if (configuration.length() > 0) { array = new JSONObject(configuration); } else { array = new JSONObject(); } setConfig(array); } catch (Exception ex) { throw new RemoteException(); } } @Override public int getTimerMilliseconds() throws RemoteException { return getMilliseconds(); } @Override public void run() throws RemoteException { runOnce(); } }; private void initialiseService() { if (!this.mServiceInitialised) { Log.i(TAG, "Initialising the service"); // Initialise the LatestResult object JSONObject tmp = initialiseLatestResult(); Log.i(TAG, "Syncing result"); this.setLatestResult(tmp); if (getEnabled()) this.setupTimerTask(); this.mServiceInitialised = true; } } private void cleanupService() { Log.i(TAG, "Running cleanupService"); Log.i(TAG, "Stopping timer task"); stopTimerTask(); Log.i(TAG, "Removing the timer"); if (this.mTimer != null) { Log.i(TAG, "Timer is not null"); try { this.mTimer.cancel(); Log.i(TAG, "Timer.cancel has been called"); this.mTimer = null; } catch (Exception ex) { Log.i(TAG, "Exception has occurred - " + ex.getMessage()); } } } private void setupTimerTask () { // Only create a timer if the timer is null if (this.mTimer == null) { this.mTimer = new Timer(this.getClass().getName()); } // Only create the updateTask if is null if (this.mUpdateTask == null) { this.mUpdateTask = getTimerTask(); int milliseconds = getMilliseconds(); this.mTimer.schedule(this.mUpdateTask, 1000L, milliseconds); } onTimerEnabled(); } private void stopTimerTask() { Log.i(TAG, "stopTimerTask called"); if (this.mUpdateTask != null) { Log.i(TAG, "updateTask is not null"); if (this.mUpdateTask.cancel() ) { Log.i(TAG, "updateTask.cancel returned true"); } else { Log.i(TAG, "updateTask.cancel returned false"); } this.mUpdateTask = null; } onTimerDisabled(); } private TimerTask getTimerTask() { return new TimerTask() { @Override public void run() { Log.i(TAG, "Timer task starting work"); Log.d(TAG, "Is the service paused?"); Boolean paused = false; if (mPausedUntil != null) { Log.d(TAG, "Service is paused until " + (new SimpleDateFormat("dd/MM/yyyy hh:mm:ss")).format(mPausedUntil)); Date current = new Date(); Log.d(TAG, "Current is " + (new SimpleDateFormat("dd/MM/yyyy hh:mm:ss")).format(current)); if (mPausedUntil.after(current)) { Log.d(TAG, "Service should be paused"); paused = true; // Still paused } else { Log.d(TAG, "Service should not be paused"); mPausedUntil = null; // Paused time has past so we can clear the pause onPauseComplete(); } } if (paused) { Log.d(TAG, "Service is paused"); } else { Log.d(TAG, "Service is not paused"); // Runs the doWork // Sets the last result & updates the listeners doWorkWrapper(); } Log.i(TAG, "Timer task completing work"); } }; } // Seperated out to allow the doWork to be called from timer and adhoc (via run method) private void doWorkWrapper() { JSONObject tmp = null; try { tmp = doWork(); } catch (Exception ex) { Log.i(TAG, "Exception occurred during doWork()", ex); } Log.i(TAG, "Syncing result"); setLatestResult(tmp); // Now call the listeners Log.i(TAG, "Sending to all listeners"); for (int i = 0; i < mListeners.size(); i++) { try { mListeners.get(i).handleUpdate(); Log.i(TAG, "Sent listener - " + i); } catch (RemoteException e) { Log.i(TAG, "Failed to send to listener - " + i + " - " + e.getMessage()); } } } /* ************************************************************************************************ * Methods for subclasses to override ************************************************************************************************ */ protected abstract JSONObject initialiseLatestResult(); protected abstract JSONObject doWork(); protected abstract JSONObject getConfig(); protected abstract void setConfig(JSONObject config); protected void onTimerEnabled() { } protected void onTimerDisabled() { } protected void onPause() { } protected void onPauseComplete() { } } ================================================ FILE: src/android/BackgroundServicePlugin.java ================================================ package com.red_folder.phonegap.plugin.backgroundservice; import org.apache.cordova.CallbackContext; import org.apache.cordova.PluginResult; import org.json.JSONArray; import android.util.Log; import org.apache.cordova.CordovaPlugin; import com.red_folder.phonegap.plugin.backgroundservice.BackgroundServicePluginLogic.ExecuteResult; import com.red_folder.phonegap.plugin.backgroundservice.BackgroundServicePluginLogic.ExecuteStatus; public class BackgroundServicePlugin extends CordovaPlugin implements BackgroundServicePluginLogic.IUpdateListener { /* ************************************************************************************************ * Static values ************************************************************************************************ */ private static final String TAG = BackgroundServicePlugin.class.getSimpleName(); /* ************************************************************************************************ * Fields ************************************************************************************************ */ // Part fix for https://github.com/Red-Folder/Cordova-Plugin-BackgroundService/issues/19 //private final BackgroundServicePluginLogic mLogic = new BackgroundServicePluginLogic(); private BackgroundServicePluginLogic mLogic = null; /* ************************************************************************************************ * Overriden Methods ************************************************************************************************ */ // Part fix for https://github.com/Red-Folder/Cordova-Plugin-BackgroundService/issues/19 //public boolean execute(String action, JSONArray data, CallbackContext callback) { @Override public boolean execute(final String action, final JSONArray data, final CallbackContext callback) { boolean result = false; if (this.mLogic == null) this.mLogic = new BackgroundServicePluginLogic(this.cordova.getActivity()); // Part fix for https://github.com/Red-Folder/Cordova-Plugin-BackgroundService/issues/19 //if (this.mLogic.isInitialized()) // this.mLogic.initialize(this.cordova.getActivity()); try { if (this.mLogic.isActionValid(action)) { //Part fix for https://github.com/Red-Folder/Cordova-Plugin-BackgroundService/issues/19 //final String finalAction = action; //final JSONArray finalData = data; //final CallbackContext finalCallback = callback; final BackgroundServicePluginLogic.IUpdateListener listener = this; final Object[] listenerExtras = new Object[] { callback }; cordova.getThreadPool().execute(new Runnable() { @Override public void run() { ExecuteResult logicResult = mLogic.execute(action, data, listener, listenerExtras); Log.d(TAG, "logicResult = " + logicResult.toString()); PluginResult pluginResult = transformResult(logicResult); Log.d(TAG, "pluginResult = " + pluginResult.toString()); Log.d(TAG, "pluginResult.getMessage() = " + pluginResult.getMessage()); if (pluginResult.getKeepCallback()) Log.d(TAG, "Keep Callback"); else Log.d(TAG, "Dont keep Callback"); callback.sendPluginResult(pluginResult); } }); result = true; } else { result = false; } } catch (Exception ex) { Log.d(TAG, "Exception - " + ex.getMessage()); } return result; } @Override public void onDestroy() { super.onDestroy(); if (this.mLogic != null) { this.mLogic.onDestroy(); this.mLogic = null; } } /* ************************************************************************************************ * Public Methods ************************************************************************************************ */ public void handleUpdate(ExecuteResult logicResult, Object[] listenerExtras) { Log.d(TAG, "Starting handleUpdate"); sendUpdateToListener(logicResult, listenerExtras); Log.d(TAG, "Finished handleUpdate"); } public void closeListener(ExecuteResult logicResult, Object[] listenerExtras) { Log.d(TAG, "Starting closeListener"); sendUpdateToListener(logicResult, listenerExtras); Log.d(TAG, "Finished closeListener"); } /* ************************************************************************************************ * Private Methods ************************************************************************************************ */ private void sendUpdateToListener(ExecuteResult logicResult, Object[] listenerExtras) { try { if (listenerExtras != null && listenerExtras.length > 0) { Log.d(TAG, "Sending update"); CallbackContext callback = (CallbackContext)listenerExtras[0]; callback.sendPluginResult(transformResult(logicResult)); Log.d(TAG, "Sent update"); } } catch (Exception ex) { Log.d(TAG, "Sending update failed", ex); } } private PluginResult transformResult(ExecuteResult logicResult) { PluginResult pluginResult = null; Log.d(TAG, "Start of transformResult"); if (logicResult.getStatus() == ExecuteStatus.OK) { Log.d(TAG, "Status is OK"); if (logicResult.getData() == null) { Log.d(TAG, "We dont have data"); pluginResult = new PluginResult(PluginResult.Status.OK); } else { Log.d(TAG, "We have data"); pluginResult = new PluginResult(PluginResult.Status.OK, logicResult.getData()); } } if (logicResult.getStatus() == ExecuteStatus.ERROR) { Log.d(TAG, "Status is ERROR"); if (logicResult.getData() == null) { Log.d(TAG, "We dont have data"); pluginResult = new PluginResult(PluginResult.Status.ERROR, "Unknown error"); } else { Log.d(TAG, "We have data"); pluginResult = new PluginResult(PluginResult.Status.ERROR, logicResult.getData()); } } if (logicResult.getStatus() == ExecuteStatus.INVALID_ACTION) { Log.d(TAG, "Status is INVALID_ACTION"); if (logicResult.getData() == null) { Log.d(TAG, "We have data"); pluginResult = new PluginResult(PluginResult.Status.INVALID_ACTION, "Unknown error"); } else { Log.d(TAG, "We dont have data"); pluginResult = new PluginResult(PluginResult.Status.INVALID_ACTION, logicResult.getData()); } } if (!logicResult.isFinished()) { Log.d(TAG, "Keep Callback set to true"); pluginResult.setKeepCallback(true); } Log.d(TAG, "End of transformResult"); return pluginResult; } } ================================================ FILE: src/android/BackgroundServicePluginLogic.java ================================================ package com.red_folder.phonegap.plugin.backgroundservice; import java.util.Enumeration; import java.util.Hashtable; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.app.ActivityManager; import android.app.ActivityManager.RunningServiceInfo; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import android.content.ServiceConnection; public class BackgroundServicePluginLogic { /* ************************************************************************************************ * Static values ************************************************************************************************ */ public static final String TAG = BackgroundServicePluginLogic.class.getSimpleName(); /* ************************************************************************************************ * Keys ************************************************************************************************ */ public static final String ACTION_START_SERVICE = "startService"; public static final String ACTION_STOP_SERVICE = "stopService"; public static final String ACTION_ENABLE_TIMER = "enableTimer"; public static final String ACTION_DISABLE_TIMER = "disableTimer"; public static final String ACTION_SET_CONFIGURATION = "setConfiguration"; public static final String ACTION_REGISTER_FOR_BOOTSTART = "registerForBootStart"; public static final String ACTION_DEREGISTER_FOR_BOOTSTART = "deregisterForBootStart"; public static final String ACTION_GET_STATUS = "getStatus"; public static final String ACTION_RUN_ONCE = "runOnce"; public static final String ACTION_REGISTER_FOR_UPDATES = "registerForUpdates"; public static final String ACTION_DEREGISTER_FOR_UPDATES = "deregisterForUpdates"; public static final int ERROR_NONE_CODE = 0; public static final String ERROR_NONE_MSG = ""; public static final int ERROR_PLUGIN_ACTION_NOT_SUPPORTED_CODE = -1; public static final String ERROR_PLUGIN_ACTION_NOT_SUPPORTED_MSG = "Passed action not supported by Plugin"; public static final int ERROR_INIT_NOT_YET_CALLED_CODE = -2; public static final String ERROR_INIT_NOT_YET_CALLED_MSG = "Please call init prior any other action"; public static final int ERROR_SERVICE_NOT_RUNNING_CODE = -3; public static final String ERROR_SERVICE_NOT_RUNNING_MSG = "Sevice not currently running"; public static final int ERROR_UNABLE_TO_BIND_TO_BACKGROUND_SERVICE_CODE = -4; public static final String ERROR_UNABLE_TO_BIND_TO_BACKGROUND_SERVICE_MSG ="Plugin unable to bind to background service"; public static final int ERROR_UNABLE_TO_RETRIEVE_LAST_RESULT_CODE = -5; public static final String ERROR_UNABLE_TO_RETRIEVE_LAST_RESULT_MSG = "Unable to retrieve latest result (reason unknown)"; public static final int ERROR_LISTENER_ALREADY_REGISTERED_CODE = -6; public static final String ERROR_LISTENER_ALREADY_REGISTERED_MSG = "Listener already registered"; public static final int ERROR_LISTENER_NOT_REGISTERED_CODE = -7; public static final String ERROR_LISTENER_NOT_REGISTERED_MSG = "Listener not registered"; public static final int ERROR_UNABLE_TO_CLOSED_LISTENER_CODE = -8; public static final String ERROR_UNABLE_TO_CLOSED_LISTENER_MSG = "Unable to close listener"; public static final int ERROR_ACTION_NOT_SUPPORTED__IN_PLUGIN_VERSION_CODE = -9; public static final String ERROR_ACTION_NOT_SUPPORTED__IN_PLUGIN_VERSION_MSG = "Action is not supported in this version of the plugin"; public static final int ERROR_EXCEPTION_CODE = -99; /* ************************************************************************************************ * Fields ************************************************************************************************ */ private Context mContext; private Hashtable mServices = new Hashtable(); /* ************************************************************************************************ * Constructors ************************************************************************************************ */ // Part fix for https://github.com/Red-Folder/Cordova-Plugin-BackgroundService/issues/19 //public BackgroundServicePluginLogic() { //} public BackgroundServicePluginLogic(Context pContext) { this.mContext = pContext; } /* ************************************************************************************************ * Public Methods ************************************************************************************************ */ // Part fix for https://github.com/Red-Folder/Cordova-Plugin-BackgroundService/issues/19 //public void initialize(Context pContext) { // this.mContext = pContext; //} //public boolean isInitialized() { // if (this.mContext == null) // return false; // else // return true; //} public boolean isActionValid(String action) { boolean result = false; if(ACTION_START_SERVICE.equals(action)) result = true; if(ACTION_STOP_SERVICE.equals(action)) result = true; if(ACTION_ENABLE_TIMER.equals(action)) result = true; if(ACTION_DISABLE_TIMER.equals(action)) result = true; if(ACTION_SET_CONFIGURATION.equals(action)) result = true; if(ACTION_REGISTER_FOR_BOOTSTART.equals(action)) result = true; if(ACTION_DEREGISTER_FOR_BOOTSTART.equals(action)) result = true; if(ACTION_GET_STATUS.equals(action)) result = true; if(ACTION_RUN_ONCE.equals(action)) result = true; if(ACTION_REGISTER_FOR_UPDATES.equals(action)) result = true; if(ACTION_DEREGISTER_FOR_UPDATES.equals(action)) result = true; return result; } public ExecuteResult execute(String action, JSONArray data) { return execute(action, data, null, null); } public ExecuteResult execute(String action, JSONArray data, IUpdateListener listener, Object[] listenerExtras) { ExecuteResult result = null; Log.d(TAG, "Start of Execute"); try { Log.d(TAG, "Withing try block"); if ((data != null) && (!data.isNull(0)) && (data.get(0) instanceof String) && (data.getString(0).length() > 0)) { String serviceName = data.getString(0); Log.d(TAG, "Finding servicename " + serviceName); ServiceDetails service = null; Log.d(TAG, "Services contain " + this.mServices.size() + " records"); if (this.mServices.containsKey(serviceName)) { Log.d(TAG, "Found existing Service Details"); service = this.mServices.get(serviceName); } else { Log.d(TAG, "Creating new Service Details"); service = new ServiceDetails(this.mContext, serviceName); this.mServices.put(serviceName, service); } Log.d(TAG, "Action = " + action); if (!service.isInitialised()) service.initialise(); if (ACTION_GET_STATUS.equals(action)) result = service.getStatus(); if (ACTION_START_SERVICE.equals(action)) result = service.startService(); if (ACTION_REGISTER_FOR_BOOTSTART.equals(action)) result = service.registerForBootStart(); if (ACTION_DEREGISTER_FOR_BOOTSTART.equals(action)) result = service.deregisterForBootStart(); if (ACTION_REGISTER_FOR_UPDATES.equals(action)) result = service.registerForUpdates(listener, listenerExtras); if (ACTION_DEREGISTER_FOR_UPDATES.equals(action)) result = service.deregisterForUpdates(); if (result == null) { Log.d(TAG, "Check if the service is running?"); if (service != null && service.isServiceRunning()) { Log.d(TAG, "Service is running?"); if (ACTION_STOP_SERVICE.equals(action)) result = service.stopService(); if (ACTION_ENABLE_TIMER.equals(action)) result = service.enableTimer(data); if (ACTION_DISABLE_TIMER.equals(action)) result = service.disableTimer(); if (ACTION_SET_CONFIGURATION.equals(action)) result = service.setConfiguration(data); if (ACTION_RUN_ONCE.equals(action)) result = service.runOnce(); } else { result = new ExecuteResult(ExecuteStatus.INVALID_ACTION); } } if (result == null) result = new ExecuteResult(ExecuteStatus.INVALID_ACTION); } else { result = new ExecuteResult(ExecuteStatus.ERROR); Log.d(TAG, "ERROR - no servicename"); } } catch (Exception ex) { result = new ExecuteResult(ExecuteStatus.ERROR); Log.d(TAG, "Exception - " + ex.getMessage()); } return result; } public void onDestroy() { Log.d(TAG, "On Destroy Start"); try { Log.d(TAG, "Checking for services"); if (this.mServices != null && this.mServices.size() > 0 ) { Log.d(TAG, "Found services"); Enumeration keys = this.mServices.keys(); while( keys.hasMoreElements() ) { String key = keys.nextElement(); ServiceDetails service = this.mServices.get(key); Log.d(TAG, "Calling service.close()"); service.close(); } } } catch (Throwable t) { // catch any issues, typical for destroy routines // even if we failed to destroy something, we need to continue destroying Log.d(TAG, "Error has occurred while trying to close services", t); } this.mServices = null; Log.d(TAG, "On Destroy Finish"); } /* ************************************************************************************************ * Internal Class ************************************************************************************************ */ protected class ServiceDetails { /* ************************************************************************************************ * Static values ************************************************************************************************ */ public final String LOCALTAG = BackgroundServicePluginLogic.ServiceDetails.class.getSimpleName(); /* ************************************************************************************************ * Fields ************************************************************************************************ */ private String mServiceName = ""; private Context mContext; private BackgroundServiceApi mApi; private String mUniqueID = java.util.UUID.randomUUID().toString(); private boolean mInitialised = false; private Intent mService = null; private Object mServiceConnectedLock = new Object(); private Boolean mServiceConnected = null; private IUpdateListener mListener = null; private Object[] mListenerExtras = null; /* ************************************************************************************************ * Constructors ************************************************************************************************ */ public ServiceDetails(Context context, String serviceName) { this.mContext = context; this.mServiceName = serviceName; } /* ************************************************************************************************ * Public Methods ************************************************************************************************ */ public void initialise() { this.mInitialised = true; // If the service is running, then automatically bind to it if (this.isServiceRunning()) { startService(); } } public boolean isInitialised() { return mInitialised; } public ExecuteResult startService() { Log.d(LOCALTAG, "Starting startService"); ExecuteResult result = null; try { Log.d(LOCALTAG, "Attempting to bind to Service"); if (this.bindToService()) { Log.d(LOCALTAG, "Bind worked"); result = new ExecuteResult(ExecuteStatus.OK, createJSONResult(true, ERROR_NONE_CODE, ERROR_NONE_MSG)); } else { Log.d(LOCALTAG, "Bind Failed"); result = new ExecuteResult(ExecuteStatus.ERROR, createJSONResult(false, ERROR_UNABLE_TO_BIND_TO_BACKGROUND_SERVICE_CODE, ERROR_UNABLE_TO_BIND_TO_BACKGROUND_SERVICE_MSG)); } } catch (Exception ex) { Log.d(LOCALTAG, "startService failed", ex); result = new ExecuteResult(ExecuteStatus.ERROR, createJSONResult(false, ERROR_EXCEPTION_CODE, ex.getMessage())); } Log.d(LOCALTAG, "Finished startService"); return result; } public ExecuteResult stopService() { ExecuteResult result = null; Log.d("ServiceDetails", "stopService called"); try { Log.d("ServiceDetails", "Unbinding Service"); this.mContext.unbindService(serviceConnection); Log.d("ServiceDetails", "Stopping service"); if (this.mContext.stopService(this.mService)) { Log.d("ServiceDetails", "Service stopped"); } else { Log.d("ServiceDetails", "Service not stopped"); } result = new ExecuteResult(ExecuteStatus.OK, createJSONResult(true, ERROR_NONE_CODE, ERROR_NONE_MSG)); } catch (Exception ex) { Log.d(LOCALTAG, "stopService failed", ex); result = new ExecuteResult(ExecuteStatus.ERROR, createJSONResult(false, ERROR_EXCEPTION_CODE, ex.getMessage())); } return result; } public ExecuteResult enableTimer(JSONArray data) { ExecuteResult result = null; int milliseconds = data.optInt(1, 60000); try { mApi.enableTimer(milliseconds); result = new ExecuteResult(ExecuteStatus.OK, createJSONResult(true, ERROR_NONE_CODE, ERROR_NONE_MSG)); } catch (RemoteException ex) { Log.d(LOCALTAG, "enableTimer failed", ex); result = new ExecuteResult(ExecuteStatus.ERROR, createJSONResult(false, ERROR_EXCEPTION_CODE, ex.getMessage())); } return result; } public ExecuteResult disableTimer() { ExecuteResult result = null; try { mApi.disableTimer(); result = new ExecuteResult(ExecuteStatus.OK, createJSONResult(true, ERROR_NONE_CODE, ERROR_NONE_MSG)); } catch (RemoteException ex) { Log.d(LOCALTAG, "disableTimer failed", ex); result = new ExecuteResult(ExecuteStatus.ERROR, createJSONResult(false, ERROR_EXCEPTION_CODE, ex.getMessage())); } return result; } public ExecuteResult registerForBootStart() { ExecuteResult result = null; try { PropertyHelper.addBootService(this.mContext, this.mServiceName); result = new ExecuteResult(ExecuteStatus.OK, createJSONResult(true, ERROR_NONE_CODE, ERROR_NONE_MSG)); } catch (Exception ex) { Log.d(LOCALTAG, "registerForBootStart failed", ex); result = new ExecuteResult(ExecuteStatus.ERROR, createJSONResult(false, ERROR_EXCEPTION_CODE, ex.getMessage())); } return result; } public ExecuteResult deregisterForBootStart() { ExecuteResult result = null; try { PropertyHelper.removeBootService(this.mContext, this.mServiceName); result = new ExecuteResult(ExecuteStatus.OK, createJSONResult(true, ERROR_NONE_CODE, ERROR_NONE_MSG)); } catch (Exception ex) { Log.d(LOCALTAG, "deregisterForBootStart failed", ex); result = new ExecuteResult(ExecuteStatus.ERROR, createJSONResult(false, ERROR_EXCEPTION_CODE, ex.getMessage())); } return result; } public ExecuteResult setConfiguration(JSONArray data) { ExecuteResult result = null; try { if (this.isServiceRunning()) { Object obj; try { obj = data.get(1); mApi.setConfiguration(obj.toString()); result = new ExecuteResult(ExecuteStatus.OK, createJSONResult(true, ERROR_NONE_CODE, ERROR_NONE_MSG)); } catch (JSONException e) { Log.d(LOCALTAG, "Processing config JSON from background service failed", e); result = new ExecuteResult(ExecuteStatus.ERROR, createJSONResult(false, ERROR_EXCEPTION_CODE, e.getMessage())); } } else { result = new ExecuteResult(ExecuteStatus.INVALID_ACTION, createJSONResult(false, ERROR_SERVICE_NOT_RUNNING_CODE, ERROR_SERVICE_NOT_RUNNING_MSG)); } } catch (RemoteException ex) { Log.d(LOCALTAG, "setConfiguration failed", ex); result = new ExecuteResult(ExecuteStatus.ERROR, createJSONResult(false, ERROR_EXCEPTION_CODE, ex.getMessage())); } return result; } public ExecuteResult getStatus() { ExecuteResult result = null; result = new ExecuteResult(ExecuteStatus.OK, createJSONResult(true, ERROR_NONE_CODE, ERROR_NONE_MSG)); return result; } public ExecuteResult runOnce() { ExecuteResult result = null; try { if (this.isServiceRunning()) { mApi.run(); result = new ExecuteResult(ExecuteStatus.OK, createJSONResult(true, ERROR_NONE_CODE, ERROR_NONE_MSG)); } else { result = new ExecuteResult(ExecuteStatus.INVALID_ACTION, createJSONResult(false, ERROR_SERVICE_NOT_RUNNING_CODE, ERROR_SERVICE_NOT_RUNNING_MSG)); } } catch (RemoteException ex) { Log.d(LOCALTAG, "runOnce failed", ex); result = new ExecuteResult(ExecuteStatus.ERROR, createJSONResult(false, ERROR_EXCEPTION_CODE, ex.getMessage())); } return result; } public ExecuteResult registerForUpdates(IUpdateListener listener, Object[] listenerExtras) { ExecuteResult result = null; try { // Check for if the listener is null // If it is then it will be because the Plguin version doesn't support the method if (listener == null) { result = new ExecuteResult(ExecuteStatus.INVALID_ACTION, createJSONResult(false, ERROR_ACTION_NOT_SUPPORTED__IN_PLUGIN_VERSION_CODE, ERROR_ACTION_NOT_SUPPORTED__IN_PLUGIN_VERSION_MSG)); } else { // If a listener already exists, then we fist need to deregister the original // Ignore any failures (likely due to the listener not being available anymore) if (this.isRegisteredForUpdates()) this.deregisterListener(); this.mListener = listener; this.mListenerExtras = listenerExtras; result = new ExecuteResult(ExecuteStatus.OK, createJSONResult(true, ERROR_NONE_CODE, ERROR_NONE_MSG), false); } } catch (Exception ex) { Log.d(LOCALTAG, "regsiterForUpdates failed", ex); result = new ExecuteResult(ExecuteStatus.ERROR, createJSONResult(false, ERROR_EXCEPTION_CODE, ex.getMessage())); } return result; } public ExecuteResult deregisterForUpdates() { ExecuteResult result = null; try { if (this.isRegisteredForUpdates()) if (this.deregisterListener()) result = new ExecuteResult(ExecuteStatus.OK, createJSONResult(true, ERROR_NONE_CODE, ERROR_NONE_MSG)); else result = new ExecuteResult(ExecuteStatus.ERROR, createJSONResult(false, ERROR_UNABLE_TO_CLOSED_LISTENER_CODE, ERROR_UNABLE_TO_CLOSED_LISTENER_MSG)); else result = new ExecuteResult(ExecuteStatus.INVALID_ACTION, createJSONResult(false, ERROR_LISTENER_NOT_REGISTERED_CODE, ERROR_LISTENER_NOT_REGISTERED_MSG)); } catch (Exception ex) { Log.d(LOCALTAG, "deregsiterForUpdates failed", ex); result = new ExecuteResult(ExecuteStatus.ERROR, createJSONResult(false, ERROR_EXCEPTION_CODE, ex.getMessage())); } return result; } /* * Background Service specific methods */ public void close() { Log.d("ServiceDetails", "Close called"); try { // Remove the lister to this publisher this.deregisterListener(); Log.d("ServiceDetails", "Removing ServiceListener"); mApi.removeListener(serviceListener); Log.d("ServiceDetails", "Removing ServiceConnection"); this.mContext.unbindService(serviceConnection); } catch (Exception ex) { // catch any issues, typical for destroy routines // even if we failed to destroy something, we need to continue destroying Log.d(LOCALTAG, "close failed", ex); Log.d(LOCALTAG, "Ignoring exception - will continue"); } Log.d("ServiceDetails", "Close finished"); } private boolean deregisterListener() { boolean result = false; if (this.isRegisteredForUpdates()) { Log.d("ServiceDetails", "Listener deregistering"); try { Log.d("ServiceDetails", "Listener closing"); this.mListener.closeListener(new ExecuteResult(ExecuteStatus.OK, createJSONResult(true, ERROR_NONE_CODE, ERROR_NONE_MSG)), this.mListenerExtras); Log.d("ServiceDetails", "Listener closed"); } catch (Exception ex) { Log.d("ServiceDetails", "Error occurred while closing the listener", ex); } this.mListener = null; this.mListenerExtras = null; Log.d("ServiceDetails", "Listener deregistered"); result = true; } return result; } /* ************************************************************************************************ * Private Methods ************************************************************************************************ */ private boolean bindToService() { boolean result = false; Log.d(LOCALTAG, "Starting bindToService"); try { // Fix to https://github.com/Red-Folder/bgs-core/issues/18 // Gets the class from string Class serviceClass = ReflectionHelper.LoadClass(this.mServiceName); this.mService = new Intent(this.mContext, serviceClass); Log.d(LOCALTAG, "Attempting to start service"); this.mContext.startService(this.mService); Log.d(LOCALTAG, "Attempting to bind to service"); if (this.mContext.bindService(this.mService, serviceConnection, 0)) { Log.d(LOCALTAG, "Waiting for service connected lock"); synchronized(mServiceConnectedLock) { while (mServiceConnected==null) { try { mServiceConnectedLock.wait(); } catch (InterruptedException e) { Log.d(LOCALTAG, "Interrupt occurred while waiting for connection", e); } } result = this.mServiceConnected; } } } catch (Exception ex) { Log.d(LOCALTAG, "bindToService failed", ex); } Log.d(LOCALTAG, "Finished bindToService"); return result; } private ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { // that's how we get the client side of the IPC connection mApi = BackgroundServiceApi.Stub.asInterface(service); try { mApi.addListener(serviceListener); } catch (RemoteException e) { Log.d(LOCALTAG, "addListener failed", e); } synchronized(mServiceConnectedLock) { mServiceConnected = true; mServiceConnectedLock.notify(); } } @Override public void onServiceDisconnected(ComponentName name) { synchronized(mServiceConnectedLock) { mServiceConnected = false; mServiceConnectedLock.notify(); } } }; private BackgroundServiceListener.Stub serviceListener = new BackgroundServiceListener.Stub() { @Override public void handleUpdate() throws RemoteException { handleLatestResult(); } @Override public String getUniqueID() throws RemoteException { return mUniqueID; } }; private void handleLatestResult() { Log.d("ServiceDetails", "Latest results received"); if (this.isRegisteredForUpdates()) { Log.d("ServiceDetails", "Calling listener"); ExecuteResult result = new ExecuteResult(ExecuteStatus.OK, createJSONResult(true, ERROR_NONE_CODE, ERROR_NONE_MSG), false); try { this.mListener.handleUpdate(result, this.mListenerExtras); Log.d("ServiceDetails", "Listener finished"); } catch (Exception ex) { Log.d("ServiceDetails", "Listener failed", ex); Log.d("ServiceDetails", "Disabling listener"); this.mListener = null; this.mListenerExtras = null; } } else { Log.d("ServiceDetails", "No action performed"); } } private JSONObject createJSONResult(Boolean success, int errorCode, String errorMessage) { JSONObject result = new JSONObject(); // Append the basic information try { result.put("Success", success); result.put("ErrorCode", errorCode); result.put("ErrorMessage", errorMessage); } catch (JSONException e) { Log.d(LOCALTAG, "Adding basic info to JSONObject failed", e); } if (this.mServiceConnected != null && this.mServiceConnected && this.isServiceRunning()) { try { result.put("ServiceRunning", true); } catch (Exception ex) {Log.d(LOCALTAG, "Adding ServiceRunning to JSONObject failed", ex);}; try { result.put("TimerEnabled", isTimerEnabled()); } catch (Exception ex) {Log.d(LOCALTAG, "Adding TimerEnabled to JSONObject failed", ex);}; try { result.put("Configuration", getConfiguration()); } catch (Exception ex) {Log.d(LOCALTAG, "Adding Configuration to JSONObject failed", ex);}; try { result.put("LatestResult", getLatestResult()); } catch (Exception ex) {Log.d(LOCALTAG, "Adding LatestResult to JSONObject failed", ex);}; try { result.put("TimerMilliseconds", getTimerMilliseconds()); } catch (Exception ex) {Log.d(LOCALTAG, "Adding TimerMilliseconds to JSONObject failed", ex);}; } else { try { result.put("ServiceRunning", false); } catch (Exception ex) {Log.d(LOCALTAG, "Adding ServiceRunning to JSONObject failed", ex);}; try { result.put("TimerEnabled", JSONObject.NULL); } catch (Exception ex) {Log.d(LOCALTAG, "Adding TimerEnabled to JSONObject failed", ex);}; try { result.put("Configuration", JSONObject.NULL); } catch (Exception ex) {Log.d(LOCALTAG, "Adding Configuration to JSONObject failed", ex);}; try { result.put("LatestResult", JSONObject.NULL); } catch (Exception ex) {Log.d(LOCALTAG, "Adding LatestResult to JSONObject failed", ex);}; try { result.put("TimerMilliseconds", JSONObject.NULL); } catch (Exception ex) {Log.d(LOCALTAG, "Adding TimerMilliseconds to JSONObject failed", ex);}; } try { result.put("RegisteredForBootStart", isRegisteredForBootStart()); } catch (Exception ex) {Log.d(LOCALTAG, "Adding RegisteredForBootStart to JSONObject failed", ex);}; try { result.put("RegisteredForUpdates", isRegisteredForUpdates()); } catch (Exception ex) {Log.d(LOCALTAG, "Adding RegisteredForUpdates to JSONObject failed", ex);}; return result; } private boolean isServiceRunning() { boolean result = false; try { // Return Plugin with ServiceRunning true/ false ActivityManager manager = (ActivityManager)this.mContext.getSystemService(Context.ACTIVITY_SERVICE); for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) { if (this.mServiceName.equals(service.service.getClassName())) { result = true; } } } catch (Exception ex) { Log.d(LOCALTAG, "isServiceRunning failed", ex); } return result; } private Boolean isTimerEnabled() { Boolean result = false; try { result = mApi.isTimerEnabled(); } catch (Exception ex) { Log.d(LOCALTAG, "isTimerEnabled failed", ex); } return result; } private Boolean isRegisteredForBootStart() { Boolean result = false; try { result = PropertyHelper.isBootService(this.mContext, this.mServiceName); } catch (Exception ex) { Log.d(LOCALTAG, "isRegisteredForBootStart failed", ex); } return result; } private Boolean isRegisteredForUpdates() { if (this.mListener == null) return false; else return true; } private JSONObject getConfiguration() { JSONObject result = null; try { String data = mApi.getConfiguration(); result = new JSONObject(data); } catch (Exception ex) { Log.d(LOCALTAG, "getConfiguration failed", ex); } return result; } private JSONObject getLatestResult() { JSONObject result = null; try { String data = mApi.getLatestResult(); result = new JSONObject(data); } catch (Exception ex) { Log.d(LOCALTAG, "getLatestResult failed", ex); } return result; } private int getTimerMilliseconds() { int result = -1; try { result = mApi.getTimerMilliseconds(); } catch (Exception ex) { Log.d(LOCALTAG, "getTimerMilliseconds failed", ex); } return result; } } protected class ExecuteResult { /* ************************************************************************************************ * Fields ************************************************************************************************ */ private ExecuteStatus mStatus; private JSONObject mData; private boolean mFinished = true; public ExecuteStatus getStatus() { return this.mStatus; } public void setStatus(ExecuteStatus pStatus) { this.mStatus = pStatus; } public JSONObject getData() { return this.mData; } public void setData(JSONObject pData) { this.mData = pData; } public boolean isFinished() { return this.mFinished; } public void setFinished(boolean pFinished) { this.mFinished = pFinished; } /* ************************************************************************************************ * Constructors ************************************************************************************************ */ public ExecuteResult(ExecuteStatus pStatus) { this.mStatus = pStatus; } public ExecuteResult(ExecuteStatus pStatus, JSONObject pData) { this.mStatus = pStatus; this.mData = pData; } public ExecuteResult(ExecuteStatus pStatus, JSONObject pData, boolean pFinished) { this.mStatus = pStatus; this.mData = pData; this.mFinished = pFinished; } } public interface IUpdateListener { public void handleUpdate(ExecuteResult logicResult, Object[] listenerExtras); public void closeListener(ExecuteResult logicResult, Object[] listenerExtras); } /* ************************************************************************************************ * Enums ************************************************************************************************ */ protected enum ExecuteStatus { OK, ERROR, INVALID_ACTION } } ================================================ FILE: src/android/BootReceiver.java ================================================ package com.red_folder.phonegap.plugin.backgroundservice; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; public class BootReceiver extends BroadcastReceiver { /* ************************************************************************************************ * Overriden Methods ************************************************************************************************ */ @Override public void onReceive(Context context, Intent intent) { // Get all the registered and loop through and start them String[] serviceList = PropertyHelper.getBootServices(context); if (serviceList != null) { for (int i = 0; i < serviceList.length; i++) { // Fix to https://github.com/Red-Folder/bgs-core/issues/18 // Gets the class from string Class serviceClass = ReflectionHelper.LoadClass(serviceList[i]); Intent serviceIntent = new Intent(context, serviceClass); context.startService(serviceIntent); } } } } ================================================ FILE: src/android/PropertyHelper.java ================================================ package com.red_folder.phonegap.plugin.backgroundservice; import android.content.Context; import android.content.SharedPreferences; import android.preference.PreferenceManager; public class PropertyHelper { /* ************************************************************************************************ * Fields ************************************************************************************************ */ private final static String KEY_BOOTSERVICES = "com.red_folder.phonegap.plugin.backgroundservice.BootServices"; /* ************************************************************************************************ * Public Methods ************************************************************************************************ */ protected static String[] getBootServices(Context context) { String serviceList = getBootServicesString(context); if (serviceList.length() == 0) return null; else return serviceList.split(";"); } protected static void addBootService(Context context, String serviceName) { String serviceList = getBootServicesString(context); if (serviceList.length() == 0) serviceList = serviceName; else if (serviceList.contains(serviceName)) { // Already in the list, so ignore the request } else { serviceList += ";" + serviceName; } saveBootServices(context, serviceList); } protected static void removeBootService(Context context, String serviceName) { String serviceList = getBootServicesString(context); String newServiceList = ""; if (serviceList.length() > 0) { if (serviceList.contains(serviceName)) { String[] services = serviceList.split(";"); for (int i = 0; i < services.length; i++) { if (!services[i].contains(serviceName)) newServiceList += ";" + services[i]; } } } saveBootServices(context, newServiceList); } protected static boolean isBootService(Context context, String serviceName) { String serviceList = getBootServicesString(context); boolean result = false; if (serviceList.length() > 0) { if (serviceList.contains(serviceName)) { result = true; } } return result; } /* ************************************************************************************************ * Private Methods ************************************************************************************************ */ private static void saveBootServices(Context context, String serviceList) { SharedPreferences.Editor editor = getEditor(context); editor.putString(KEY_BOOTSERVICES, serviceList); editor.commit(); // Very important } private static String getBootServicesString(Context context) { SharedPreferences sharedPrefs = getSharedPreferences(context); return sharedPrefs.getString(KEY_BOOTSERVICES, ""); } private static SharedPreferences getSharedPreferences(Context context) { return PreferenceManager.getDefaultSharedPreferences(context); } private static SharedPreferences.Editor getEditor(Context context) { SharedPreferences sharedPrefs = getSharedPreferences(context); return sharedPrefs.edit(); } } ================================================ FILE: src/android/ReflectionHelper.java ================================================ package com.red_folder.phonegap.plugin.backgroundservice; import android.util.Log; public class ReflectionHelper { /* ************************************************************************************************ * Static values ************************************************************************************************ */ public static final String TAG = ReflectionHelper.class.getSimpleName(); public static Class LoadClass(String className) { Class result = null; Log.d(TAG, "Attempting to load call: " + className); ClassLoader classLoader = ReflectionHelper.class.getClassLoader(); try { result = classLoader.loadClass(className); Log.d(TAG, "Class loaded"); } catch (ClassNotFoundException ex) { Log.d(TAG, "Class failed to load"); Log.d(TAG, ex.getMessage()); } return result; } } ================================================ FILE: www/backgroundService.js ================================================ /* * Copyright 2013 Red Folder Consultancy Ltd * * 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. */ /* * Declare a factory class which is used to create the background service "wrapper" */ function BackgroundServiceFactory() { } BackgroundServiceFactory.prototype.create = function (serviceName) { var exec = require("cordova/exec"); var BackgroundService = function (serviceName) { var ServiceName = serviceName; this.getServiceName = function() { return ServiceName; }; }; /** * All methods attempt to return the following data in both the success and failure callbacks * Front end development should take into account any all or all of these values may be null * * Following returned in the JSONObject: * Boolean Success - was the call a success * int ErrorCode - Error code if an error occurred, else will be zero * String ErrorMessage - Text representation of the error code * Boolean ServiceRunning - True if the Service is running * Boolean TimerEnabled - True if the Timer is enabled * Boolean RegisteredForBootStart - True if the Service is registered for boot start * JSONObject Configuration - A JSONObject of the configuration of the service (contents dependant on the service) * JSONObject LastestResult - A JSONObject of the last result of the service (contents dependant on the service) * int TimerMilliseconds - Milliseconds used by the background service if Timer enabled * Boolean RegisteredForUpdates - True if the Service is registered to send updates to the front-end */ /** * Starts the Service * * @param successCallback The callback which will be called if the method is successful * @param failureCallback The callback which will be called if the method encounters an error */ BackgroundService.prototype.startService = function(successCallback, failureCallback) { return exec( successCallback, failureCallback, 'BackgroundServicePlugin', 'startService', [this.getServiceName()]); }; /** * Stops the Service * * @param successCallback The callback which will be called if the method is successful * @param failureCallback The callback which will be called if the method encounters an error */ BackgroundService.prototype.stopService = function(successCallback, failureCallback) { return exec( successCallback, failureCallback, 'BackgroundServicePlugin', 'stopService', [this.getServiceName()]); }; /** * Enables the Service Timer * * @param milliseconds The milliseconds used for the timer * @param successCallback The callback which will be called if the method is successful * @param failureCallback The callback which will be called if the method encounters an error */ BackgroundService.prototype.enableTimer = function(milliseconds, successCallback, failureCallback) { return exec( successCallback, failureCallback, 'BackgroundServicePlugin', 'enableTimer', [this.getServiceName(), milliseconds]); }; /** * Disabled the Service Timer * * @param successCallback The callback which will be called if the method is successful * @param failureCallback The callback which will be called if the method encounters an error */ BackgroundService.prototype.disableTimer = function(successCallback, failureCallback) { return exec( successCallback, failureCallback, 'BackgroundServicePlugin', 'disableTimer', [this.getServiceName()]); }; /** * Sets the configuration for the service * * @param configuration JSONObject to be sent to the service * @param successCallback The callback which will be called if the method is successful * @param failureCallback The callback which will be called if the method encounters an error */ BackgroundService.prototype.setConfiguration = function(configuration, successCallback, failureCallback) { return exec( successCallback, failureCallback, 'BackgroundServicePlugin', 'setConfiguration', [this.getServiceName(), configuration]); }; /** * Registers the service for Boot Start * * @param successCallback The callback which will be called if the method is successful * @param failureCallback The callback which will be called if the method encounters an error */ BackgroundService.prototype.registerForBootStart = function(successCallback, failureCallback) { return exec( successCallback, failureCallback, 'BackgroundServicePlugin', 'registerForBootStart', [this.getServiceName()]); }; /** * Deregisters the service for Boot Start * * @param successCallback The callback which will be called if the method is successful * @param failureCallback The callback which will be called if the method encounters an error */ BackgroundService.prototype.deregisterForBootStart = function(successCallback, failureCallback) { return exec( successCallback, failureCallback, 'BackgroundServicePlugin', 'deregisterForBootStart', [this.getServiceName()]); }; /** * Get the current status of the service. * * @param successCallback The callback which will be called if the method is successful * @param failureCallback The callback which will be called if the method encounters an error */ BackgroundService.prototype.isRegisteredForBootStart = function(successCallback, failureCallback) { return exec( successCallback, failureCallback, 'BackgroundServicePlugin', 'isRegisteredForBootStart', [this.getServiceName()]); }; /** * Returns the status of the service * * @param successCallback The callback which will be called if the method is successful * @param failureCallback The callback which will be called if the method encounters an error */ BackgroundService.prototype.getStatus = function(successCallback, failureCallback) { return exec( successCallback, failureCallback, 'BackgroundServicePlugin', 'getStatus', [this.getServiceName()]); }; /** * Returns the doWork once * * @param successCallback The callback which will be called if the method is successful * @param failureCallback The callback which will be called if the method encounters an error */ BackgroundService.prototype.runOnce = function(successCallback, failureCallback) { return exec( successCallback, failureCallback, 'BackgroundServicePlugin', 'runOnce', [this.getServiceName()]); }; /** * Registers for doWork() updates * * @param successCallback The callback which will be called if the method is successful * @param failureCallback The callback which will be called if the method encounters an error */ BackgroundService.prototype.registerForUpdates = function(successCallback, failureCallback) { return exec( successCallback, failureCallback, 'BackgroundServicePlugin', 'registerForUpdates', [this.getServiceName()]); }; /** * Deregisters for doWork() updates * * @param successCallback The callback which will be called if the method is successful * @param failureCallback The callback which will be called if the method encounters an error */ BackgroundService.prototype.deregisterForUpdates = function(successCallback, failureCallback) { return exec( successCallback, failureCallback, 'BackgroundServicePlugin', 'deregisterForUpdates', [this.getServiceName()]); }; var backgroundService = new BackgroundService(serviceName); return backgroundService; }; module.exports = new BackgroundServiceFactory();