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();