🎯 Recommended Samples
Balanced sample collections from various categories for you to explore
Apache Cordova Mobile Development Samples
Apache Cordova hybrid mobile app development examples including plugin usage, device APIs, cross-platform deployment, and legacy system integration
💻 Cordova Core Plugins Implementation javascript
🟢 simple
Implement essential Cordova core plugins for device features including camera, geolocation, notifications, file system, and device information
// config.xml
<?xml version='1.0' encoding='utf-8'?>
<widget id="com.example.cordovaapp" version="1.0.0" xmlns="http://www.w3.org/ns/widgets">
<name>CordovaDemo</name>
<description>Apache Cordova Application</description>
<author email="[email protected]" href="http://example.com">
Example Developer
</author>
<content src="index.html" />
<access origin="*" />
<allow-intent href="http://*/*" />
<allow-intent href="https://*/*" />
<allow-intent href="tel:*" />
<allow-intent href="sms:*" />
<allow-intent href="mailto:*" />
<allow-intent href="geo:*" />
<!-- Core Plugins -->
<plugin name="cordova-plugin-camera" spec="^6.0.0" />
<plugin name="cordova-plugin-geolocation" spec="^4.1.0" />
<plugin name="cordova-plugin-device" spec="^2.1.0" />
<plugin name="cordova-plugin-file" spec="^6.0.2" />
<plugin name="cordova-plugin-file-transfer" spec="^1.7.1" />
<plugin name="cordova-plugin-dialogs" spec="^2.0.2" />
<plugin name="cordova-plugin-vibration" spec="^3.1.1" />
<plugin name="cordova-plugin-network-information" spec="^2.0.2" />
<plugin name="cordova-plugin-inappbrowser" spec="^3.2.0" />
<plugin name="cordova-plugin-media-capture" spec="^3.0.3" />
<plugin name="cordova-plugin-splashscreen" spec="^5.0.4" />
<plugin name="cordova-plugin-statusbar" spec="^2.4.3" />
<plugin name="cordova-plugin-whitelist" spec="^1.3.4" />
<!-- Platform-specific preferences -->
<platform name="android">
<preference name="android-minSdkVersion" value="22" />
<preference name="android-targetSdkVersion" value="30" />
<preference name="SplashMaintainAspectRatio" value="true" />
<preference name="SplashShowOnlyFirstTime" value="false" />
<preference name="SplashScreenDelay" value="3000" />
<preference name="StatusBarOverlaysWebView" value="false" />
<preference name="StatusBarBackgroundColor" value="#000000" />
<preference name="StatusBarStyle" value="lightcontent" />
</platform>
<platform name="ios">
<preference name="SplashMaintainAspectRatio" value="true" />
<preference name="SplashScreenDelay" value="3000" />
<preference name="StatusBarOverlaysWebView" value="false" />
<preference name="StatusBarBackgroundColor" value="#000000" />
<preference name="StatusBarStyle" value="lightcontent" />
<preference name="BackupWebStorage" value="none" />
</platform>
</widget>
// js/services/CordovaDeviceService.js
class CordovaDeviceService {
constructor() {
this.isDeviceReady = false;
this.initializeCordova();
}
initializeCordova() {
document.addEventListener('deviceready', () => {
console.log('Device is ready!');
this.isDeviceReady = true;
this.onDeviceReady();
}, false);
}
onDeviceReady() {
// Hide splash screen
if (navigator.splashscreen) {
navigator.splashscreen.hide();
}
// Setup status bar
if (window.StatusBar) {
StatusBar.backgroundColorByHexString("#000000");
StatusBar.styleLightContent();
}
// Setup network monitoring
this.setupNetworkMonitoring();
}
// Device Information
getDeviceInfo() {
if (!this.isDeviceReady) {
console.warn('Device not ready');
return null;
}
return {
platform: device.platform,
version: device.version,
model: device.model,
manufacturer: device.manufacturer,
uuid: device.uuid,
serial: device.serial,
isVirtual: device.isVirtual
};
}
// Camera Operations
takePhoto(options = {}) {
return new Promise((resolve, reject) => {
if (!this.isDeviceReady || !navigator.camera) {
reject(new Error('Camera not available'));
return;
}
const defaultOptions = {
quality: 80,
destinationType: Camera.DestinationType.FILE_URI,
sourceType: Camera.PictureSourceType.CAMERA,
encodingType: Camera.EncodingType.JPEG,
mediaType: Camera.MediaType.PICTURE,
allowEdit: false,
correctOrientation: true,
saveToPhotoAlbum: false
};
const cameraOptions = { ...defaultOptions, ...options };
navigator.camera.getPicture(
(imageUri) => {
resolve({
success: true,
imageUri: imageUri,
message: 'Photo captured successfully'
});
},
(error) => {
reject(new Error(`Camera error: ${error}`));
},
cameraOptions
);
});
}
getPhotoFromGallery(options = {}) {
return new Promise((resolve, reject) => {
if (!this.isDeviceReady || !navigator.camera) {
reject(new Error('Camera not available'));
return;
}
const defaultOptions = {
quality: 80,
destinationType: Camera.DestinationType.FILE_URI,
sourceType: Camera.PictureSourceType.PHOTOLIBRARY,
encodingType: Camera.EncodingType.JPEG,
mediaType: Camera.MediaType.PICTURE
};
const cameraOptions = { ...defaultOptions, ...options };
navigator.camera.getPicture(
(imageUri) => {
resolve({
success: true,
imageUri: imageUri,
message: 'Photo selected successfully'
});
},
(error) => {
reject(new Error(`Gallery error: ${error}`));
},
cameraOptions
);
});
}
// Geolocation Operations
getCurrentPosition(options = {}) {
return new Promise((resolve, reject) => {
if (!this.isDeviceReady || !navigator.geolocation) {
reject(new Error('Geolocation not available'));
return;
}
const defaultOptions = {
maximumAge: 3000,
timeout: 5000,
enableHighAccuracy: true
};
const geolocationOptions = { ...defaultOptions, ...options };
navigator.geolocation.getCurrentPosition(
(position) => {
resolve({
success: true,
latitude: position.coords.latitude,
longitude: position.coords.longitude,
accuracy: position.coords.accuracy,
altitude: position.coords.altitude,
altitudeAccuracy: position.coords.altitudeAccuracy,
heading: position.coords.heading,
speed: position.coords.speed,
timestamp: position.timestamp
});
},
(error) => {
reject(new Error(`Geolocation error: ${error.message}`));
},
geolocationOptions
);
});
}
watchPosition(callback, options = {}) {
if (!this.isDeviceReady || !navigator.geolocation) {
throw new Error('Geolocation not available');
}
const defaultOptions = {
maximumAge: 3000,
timeout: 5000,
enableHighAccuracy: true
};
const geolocationOptions = { ...defaultOptions, ...options };
return navigator.geolocation.watchPosition(
(position) => {
callback({
success: true,
latitude: position.coords.latitude,
longitude: position.coords.longitude,
accuracy: position.coords.accuracy,
timestamp: position.timestamp
});
},
(error) => {
callback({
success: false,
error: error.message
});
},
geolocationOptions
);
}
// File System Operations
readFile(path, options = {}) {
return new Promise((resolve, reject) => {
if (!this.isDeviceReady || !window.resolveLocalFileSystemURL) {
reject(new Error('File system not available'));
return;
}
window.resolveLocalFileSystemURL(
path,
(fileEntry) => {
fileEntry.file(
(file) => {
const reader = new FileReader();
reader.onloadend = function() {
resolve({
success: true,
data: this.result,
name: file.name,
size: file.size,
type: file.type,
lastModified: file.lastModified
});
};
reader.onerror = function() {
reject(new Error('Failed to read file'));
};
// Read based on options
if (options.asText) {
reader.readAsText(file);
} else if (options.asDataURL) {
reader.readAsDataURL(file);
} else {
reader.readAsArrayBuffer(file);
}
},
(error) => {
reject(new Error(`File error: ${error.code}`));
}
);
},
(error) => {
reject(new Error(`File system error: ${error.code}`));
}
);
});
}
writeFile(path, data, options = {}) {
return new Promise((resolve, reject) => {
if (!this.isDeviceReady || !window.resolveLocalFileSystemURL) {
reject(new Error('File system not available'));
return;
}
const defaultOptions = {
create: true,
exclusive: false
};
const fileOptions = { ...defaultOptions, ...options };
window.resolveLocalFileSystemURL(
path.substring(0, path.lastIndexOf('/')),
(directoryEntry) => {
directoryEntry.getFile(
path.substring(path.lastIndexOf('/') + 1),
fileOptions,
(fileEntry) => {
fileEntry.createWriter(
(writer) => {
writer.onwriteend = () => {
resolve({
success: true,
path: fileEntry.toURL(),
message: 'File written successfully'
});
};
writer.onerror = (error) => {
reject(new Error(`Write error: ${error}`));
};
if (typeof data === 'string') {
writer.write(data);
} else if (data instanceof Blob) {
writer.write(data);
} else {
const blob = new Blob([JSON.stringify(data)], { type: 'application/json' });
writer.write(blob);
}
},
(error) => {
reject(new Error(`Writer error: ${error.code}`));
}
);
},
(error) => {
reject(new Error(`File creation error: ${error.code}`));
}
);
},
(error) => {
reject(new Error(`Directory error: ${error.code}`));
}
);
});
}
// Dialog Operations
showAlert(message, title = 'Alert', buttonName = 'OK') {
return new Promise((resolve) => {
if (!this.isDeviceReady || !navigator.notification) {
// Fallback to browser alert
alert(message);
resolve();
return;
}
navigator.notification.alert(
message,
() => resolve(),
title,
buttonName
);
});
}
showConfirm(message, title = 'Confirm', buttonLabels = ['OK', 'Cancel']) {
return new Promise((resolve) => {
if (!this.isDeviceReady || !navigator.notification) {
// Fallback to browser confirm
const result = confirm(message);
resolve(result ? 1 : 2);
return;
}
navigator.notification.confirm(
message,
(buttonIndex) => resolve(buttonIndex),
title,
buttonLabels
);
});
}
showPrompt(message, title = 'Prompt', buttonLabels = ['OK', 'Cancel'], defaultText = '') {
return new Promise((resolve) => {
if (!this.isDeviceReady || !navigator.notification) {
// Fallback to browser prompt
const result = prompt(message, defaultText);
resolve(result ? { input1: result } : false);
return;
}
navigator.notification.prompt(
message,
(results) => resolve(results),
title,
buttonLabels,
defaultText
);
});
}
// Vibration
vibrate(pattern) {
if (!this.isDeviceReady || !navigator.notification || !navigator.vibrate) {
console.warn('Vibration not available');
return;
}
if (Array.isArray(pattern)) {
navigator.notification.vibrateWithPattern(pattern);
} else {
navigator.vibrate(pattern || 1000);
}
}
// Network Information
setupNetworkMonitoring() {
if (!this.isDeviceReady || !navigator.connection) {
console.warn('Network information not available');
return;
}
document.addEventListener('online', () => {
console.log('Network connection established');
this.onNetworkChange(true);
});
document.addEventListener('offline', () => {
console.log('Network connection lost');
this.onNetworkChange(false);
});
}
onNetworkChange(isOnline) {
// Override this method to handle network changes
console.log(`Network status changed: ${isOnline ? 'online' : 'offline'}`);
}
getNetworkInfo() {
if (!this.isDeviceReady || !navigator.connection) {
return {
online: navigator.onLine,
type: 'unknown'
};
}
const connection = navigator.connection;
return {
online: navigator.onLine,
type: connection.type,
effectiveType: connection.effectiveType,
downlink: connection.downlink,
rtt: connection.rtt
};
}
// In App Browser
openInAppBrowser(url, options = {}) {
if (!this.isDeviceReady || !cordova.InAppBrowser) {
// Fallback to opening in same window
window.open(url, '_blank');
return null;
}
const defaultOptions = {
location: 'yes',
clearcache: 'yes',
toolbar: 'yes',
closebuttoncaption: 'Close'
};
const browserOptions = { ...defaultOptions, ...options };
const target = options.target || '_blank';
return cordova.InAppBrowser.open(url, target, browserOptions);
}
// Media Capture
captureAudio(options = {}) {
return new Promise((resolve, reject) => {
if (!this.isDeviceReady || !navigator.device.capture) {
reject(new Error('Media capture not available'));
return;
}
const defaultOptions = {
limit: 1,
duration: 30
};
const captureOptions = { ...defaultOptions, ...options };
navigator.device.capture.captureAudio(
(mediaFiles) => {
resolve({
success: true,
files: mediaFiles.map(file => ({
name: file.name,
fullPath: file.fullPath,
type: file.type,
lastModifiedDate: file.lastModifiedDate,
size: file.size
}))
});
},
(error) => {
reject(new Error(`Audio capture error: ${error.code}`));
},
captureOptions
);
});
}
captureImage(options = {}) {
return new Promise((resolve, reject) => {
if (!this.isDeviceReady || !navigator.device.capture) {
reject(new Error('Media capture not available'));
return;
}
const defaultOptions = {
limit: 1
};
const captureOptions = { ...defaultOptions, ...options };
navigator.device.capture.captureImage(
(mediaFiles) => {
resolve({
success: true,
files: mediaFiles.map(file => ({
name: file.name,
fullPath: file.fullPath,
type: file.type,
lastModifiedDate: file.lastModifiedDate,
size: file.size
}))
});
},
(error) => {
reject(new Error(`Image capture error: ${error.code}`));
},
captureOptions
);
});
}
}
// Export for use in other modules
if (typeof module !== 'undefined' && module.exports) {
module.exports = CordovaDeviceService;
}
// js/app.js (Main Application)
const deviceService = new CordovaDeviceService();
// Example usage in your app
class AppController {
constructor() {
this.initializeEventListeners();
}
initializeEventListeners() {
document.addEventListener('DOMContentLoaded', () => {
this.setupUI();
});
// Wait for device ready
if (deviceService.isDeviceReady) {
this.onDeviceReady();
} else {
document.addEventListener('deviceready', () => {
this.onDeviceReady();
});
}
}
onDeviceReady() {
console.log('App is ready!');
this.displayDeviceInfo();
this.checkNetworkStatus();
}
async displayDeviceInfo() {
const deviceInfo = deviceService.getDeviceInfo();
if (deviceInfo) {
const infoElement = document.getElementById('device-info');
if (infoElement) {
infoElement.innerHTML = `
<p><strong>Platform:</strong> ${deviceInfo.platform}</p>
<p><strong>Version:</strong> ${deviceInfo.version}</p>
<p><strong>Model:</strong> ${deviceInfo.model}</p>
<p><strong>UUID:</strong> ${deviceInfo.uuid}</p>
`;
}
}
}
checkNetworkStatus() {
const networkInfo = deviceService.getNetworkInfo();
const statusElement = document.getElementById('network-status');
if (statusElement) {
statusElement.innerHTML = `
<p><strong>Status:</strong> ${networkInfo.online ? 'Online' : 'Offline'}</p>
<p><strong>Type:</strong> ${networkInfo.type}</p>
`;
statusElement.className = networkInfo.online ? 'online' : 'offline';
}
}
async takePicture() {
try {
const result = await deviceService.takePhoto({
quality: 90,
saveToPhotoAlbum: true
});
if (result.success) {
const imageElement = document.getElementById('captured-image');
if (imageElement) {
imageElement.src = result.imageUri;
imageElement.style.display = 'block';
}
await deviceService.showAlert(
'Photo captured successfully!',
'Success',
'OK'
);
}
} catch (error) {
console.error('Camera error:', error);
await deviceService.showAlert(
'Failed to capture photo: ' + error.message,
'Error',
'OK'
);
}
}
async getCurrentLocation() {
try {
const position = await deviceService.getCurrentPosition();
if (position.success) {
const locationElement = document.getElementById('location');
if (locationElement) {
locationElement.innerHTML = `
<p><strong>Latitude:</strong> ${position.latitude}</p>
<p><strong>Longitude:</strong> ${position.longitude}</p>
<p><strong>Accuracy:</strong> ${position.accuracy}m</p>
`;
}
}
} catch (error) {
console.error('Geolocation error:', error);
await deviceService.showAlert(
'Failed to get location: ' + error.message,
'Error',
'OK'
);
}
}
async showConfirmation() {
const result = await deviceService.showConfirm(
'Do you want to continue?',
'Confirmation',
['Yes', 'No']
);
if (result === 1) {
await deviceService.showAlert('You chose Yes!', 'Result', 'OK');
} else {
await deviceService.showAlert('You chose No!', 'Result', 'OK');
}
}
}
// Initialize the app
const app = new AppController();
💻 Cordova Custom Plugin Development javascript
🟡 intermediate
Create custom Cordova plugins with native code implementations for iOS (Objective-C/Swift) and Android (Java/Kotlin)
// Plugin Creation Command
// cordova plugin create cordova-plugin-myplugin --name "My Plugin" --plugin_id cordova-plugin-myplugin --plugin_version 1.0.0
// www/myplugin.js (JavaScript Interface)
var exec = require('cordova/exec');
var MyPlugin = {
/**
* Perform a custom operation
* @param {Function} successCallback - Success callback
* @param {Function} errorCallback - Error callback
* @param {Object} options - Options object
*/
customOperation: function(successCallback, errorCallback, options) {
var defaultOptions = {
value: '',
mode: 'normal'
};
var mergedOptions = Object.assign(defaultOptions, options || {});
exec(
successCallback,
errorCallback,
'MyPlugin',
'customOperation',
[mergedOptions]
);
},
/**
* Get device specific information
* @param {Function} successCallback - Success callback
* @param {Function} errorCallback - Error callback
*/
getDeviceInfo: function(successCallback, errorCallback) {
exec(
successCallback,
errorCallback,
'MyPlugin',
'getDeviceInfo',
[]
);
},
/**
* Start monitoring a custom event
* @param {Function} callback - Event callback
*/
startMonitoring: function(callback) {
exec(
callback,
callback,
'MyPlugin',
'startMonitoring',
[]
);
},
/**
* Stop monitoring custom events
*/
stopMonitoring: function() {
exec(
function() {},
function() {},
'MyPlugin',
'stopMonitoring',
[]
);
}
};
module.exports = MyPlugin;
// src/ios/MyPlugin.m (Objective-C Implementation)
#import <Cordova/CDV.h>
#import "MyPlugin.h"
@implementation MyPlugin
- (void)customOperation:(CDVInvokedUrlCommand*)command {
CDVPluginResult* pluginResult = nil;
NSString* value = nil;
NSString* mode = @"normal";
@try {
// Get parameters
NSDictionary* options = [command.arguments objectAtIndex:0];
if (options) {
value = [options objectForKey:@"value"];
mode = [options objectForKey:@"mode"];
if (!mode) {
mode = @"normal";
}
}
if (!value) {
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Missing required parameter: value"];
} else {
// Process the operation
NSString* processedValue = [NSString stringWithFormat:@"%@: %@",
[mode isEqualToString:@"advanced"] ? @"ADVANCED" : @"NORMAL",
value];
// Create response dictionary
NSDictionary* result = @{
@"success": @YES,
@"processedValue": processedValue,
@"timestamp": @([[NSDate date] timeIntervalSince1970] * 1000),
@"platform": @"iOS"
};
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:result];
}
} @catch (NSException* exception) {
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:[exception reason]];
}
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}
- (void)getDeviceInfo:(CDVInvokedUrlCommand*)command {
CDVPluginResult* pluginResult = nil;
@try {
UIDevice* device = [UIDevice currentDevice];
NSDictionary* deviceInfo = @{
@"platform": @"iOS",
@"systemVersion": device.systemVersion,
@"model": device.model,
@"name": device.name,
@"customProperty": @"ios-specific-value",
@"batteryLevel": @(device.batteryLevel),
@"batteryState": @(device.batteryState)
};
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:deviceInfo];
} @catch (NSException* exception) {
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:[exception reason]];
}
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}
- (void)startMonitoring:(CDVInvokedUrlCommand*)command {
// Start monitoring custom events
self.monitoringCallbackId = command.callbackId;
// Example: Monitor battery changes
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(batteryLevelDidChange:)
name:UIDeviceBatteryLevelDidChangeNotification
object:nil];
// Enable battery monitoring
[[UIDevice currentDevice] setBatteryMonitoringEnabled:YES];
// Keep the command alive for monitoring
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
[pluginResult setKeepCallbackAsBool:YES];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}
- (void)stopMonitoring:(CDVInvokedUrlCommand*)command {
// Stop monitoring
[[NSNotificationCenter defaultCenter] removeObserver:self];
[[UIDevice currentDevice] setBatteryMonitoringEnabled:NO];
self.monitoringCallbackId = nil;
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}
- (void)batteryLevelDidChange:(NSNotification*)notification {
if (self.monitoringCallbackId) {
UIDevice* device = [UIDevice currentDevice];
NSDictionary* batteryInfo = @{
@"eventType": @"batteryChange",
@"level": @(device.batteryLevel),
@"state": @(device.batteryState),
@"timestamp": @([[NSDate date] timeIntervalSince1970] * 1000)
};
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:batteryInfo];
[pluginResult setKeepCallbackAsBool:YES];
[self.commandDelegate sendPluginResult:pluginResult callbackId:self.monitoringCallbackId];
}
}
- (void)dispose {
[self stopMonitoring:nil];
[super dispose];
}
@end
// src/android/MyPlugin.java (Android Implementation)
package com.example.myplugin;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.PluginResult;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
import android.util.Log;
public class MyPlugin extends CordovaPlugin {
private static final String TAG = "MyPlugin";
private CallbackContext monitoringCallback;
@Override
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
if ("customOperation".equals(action)) {
this.customOperation(args, callbackContext);
return true;
} else if ("getDeviceInfo".equals(action)) {
this.getDeviceInfo(callbackContext);
return true;
} else if ("startMonitoring".equals(action)) {
this.startMonitoring(callbackContext);
return true;
} else if ("stopMonitoring".equals(action)) {
this.stopMonitoring(callbackContext);
return true;
} else {
callbackContext.error("Invalid action: " + action);
return false;
}
}
private void customOperation(JSONArray args, CallbackContext callbackContext) {
try {
if (args.length() < 1) {
callbackContext.error("Missing options parameter");
return;
}
JSONObject options = args.getJSONObject(0);
String value = options.optString("value", "");
String mode = options.optString("mode", "normal");
if (value.isEmpty()) {
callbackContext.error("Missing required parameter: value");
return;
}
// Process the operation
String processedValue = "advanced".equals(mode)
? "ADVANCED: " + value
: "NORMAL: " + value;
// Create response
JSONObject result = new JSONObject();
result.put("success", true);
result.put("processedValue", processedValue);
result.put("timestamp", System.currentTimeMillis());
result.put("platform", "Android");
callbackContext.success(result);
} catch (JSONException e) {
Log.e(TAG, "JSON Exception", e);
callbackContext.error("JSON Exception: " + e.getMessage());
}
}
private void getDeviceInfo(CallbackContext callbackContext) {
try {
Context context = cordova.getActivity().getApplicationContext();
JSONObject deviceInfo = new JSONObject();
deviceInfo.put("platform", "Android");
deviceInfo.put("version", android.os.Build.VERSION.RELEASE);
deviceInfo.put("model", android.os.Build.MODEL);
deviceInfo.put("manufacturer", android.os.Build.MANUFACTURER);
deviceInfo.put("customProperty", "android-specific-value");
// Battery information
IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus = context.registerReceiver(null, ifilter);
if (batteryStatus != null) {
int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
float batteryPct = level / (float) scale;
int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
deviceInfo.put("batteryLevel", batteryPct);
deviceInfo.put("batteryStatus", status);
}
callbackContext.success(deviceInfo);
} catch (JSONException e) {
Log.e(TAG, "JSON Exception", e);
callbackContext.error("JSON Exception: " + e.getMessage());
}
}
private void startMonitoring(CallbackContext callbackContext) {
this.monitoringCallback = callbackContext;
// Create a result that stays alive for monitoring
PluginResult result = new PluginResult(PluginResult.Status.OK);
result.setKeepCallback(true);
callbackContext.sendPluginResult(result);
// Start a background thread for monitoring
startBackgroundMonitoring();
}
private void stopMonitoring(CallbackContext callbackContext) {
this.monitoringCallback = null;
stopBackgroundMonitoring();
callbackContext.success();
}
private void startBackgroundMonitoring() {
cordova.getThreadPool().execute(new Runnable() {
@Override
public void run() {
while (monitoringCallback != null) {
try {
// Example: Monitor battery every 5 seconds
Thread.sleep(5000);
if (monitoringCallback != null) {
Context context = cordova.getActivity().getApplicationContext();
IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus = context.registerReceiver(null, ifilter);
if (batteryStatus != null) {
int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
float batteryPct = level / (float) scale;
JSONObject batteryInfo = new JSONObject();
try {
batteryInfo.put("eventType", "batteryChange");
batteryInfo.put("level", batteryPct);
batteryInfo.put("timestamp", System.currentTimeMillis());
PluginResult result = new PluginResult(PluginResult.Status.OK, batteryInfo);
result.setKeepCallback(true);
monitoringCallback.sendPluginResult(result);
} catch (JSONException e) {
Log.e(TAG, "JSON Exception", e);
}
}
}
} catch (InterruptedException e) {
Log.d(TAG, "Monitoring interrupted", e);
break;
}
}
}
});
}
private void stopBackgroundMonitoring() {
// Cleanup background monitoring
// This would typically involve stopping any background threads
}
@Override
public void onDestroy() {
stopMonitoring(null);
super.onDestroy();
}
}
// src/android/MyPlugin.gradle (Gradle Build File)
dependencies {
implementation 'org.apache.cordova:framework:9.0.0'
}
// package.json (Plugin Package Configuration)
{
"name": "cordova-plugin-myplugin",
"version": "1.0.0",
"description": "My Custom Cordova Plugin",
"cordova": {
"id": "cordova-plugin-myplugin",
"platforms": [
"android",
"ios"
]
},
"repository": {
"type": "git",
"url": "git+https://github.com/example/cordova-plugin-myplugin.git"
},
"keywords": [
"cordova",
"plugin",
"ecosystem:cordova",
"cordova-android",
"cordova-ios"
],
"author": "Example Developer",
"license": "MIT",
"bugs": {
"url": "https://github.com/example/cordova-plugin-myplugin/issues"
},
"homepage": "https://github.com/example/cordova-plugin-myplugin#readme"
}
// Usage in Cordova App
// js/app.js
document.addEventListener('deviceready', function() {
// Use the custom plugin
MyPlugin.customOperation(
function(result) {
console.log('Success:', result);
},
function(error) {
console.error('Error:', error);
},
{
value: 'Hello World',
mode: 'advanced'
}
);
// Get device info
MyPlugin.getDeviceInfo(
function(deviceInfo) {
console.log('Device Info:', deviceInfo);
},
function(error) {
console.error('Error:', error);
}
);
// Start monitoring
MyPlugin.startMonitoring(function(event) {
console.log('Event received:', event);
});
// Stop monitoring when done
// MyPlugin.stopMonitoring();
});
💻 Cordova Enterprise Deployment and Security javascript
🔴 complex
Configure enterprise-grade Cordova applications with security best practices, certificate pinning, encryption, and corporate deployment strategies
// config-security.xml (Security Configuration)
<?xml version='1.0' encoding='utf-8'?>
<widget id="com.enterprise.cordovaapp" version="2.0.0" xmlns="http://www.w3.org/ns/widgets">
<name>EnterpriseApp</name>
<description>Enterprise Cordova Application</description>
<author email="[email protected]" href="https://enterprise.example.com">
Enterprise Team
</author>
<content src="index.html" />
<!-- Security Settings -->
<access origin="https://api.enterprise.com" />
<access origin="https://auth.enterprise.com" />
<access origin="https://cdn.enterprise.com" />
<allow-navigation href="https://*.enterprise.com/*" />
<allow-intent href="https://*.enterprise.com/*" />
<feature name="Whitelist">
<param name="allowed-intents" value="https://*.enterprise.com/*" />
</feature>
<!-- Security Plugins -->
<plugin name="cordova-plugin-advanced-http" spec="^3.2.2" />
<plugin name="cordova-plugin-certificate-observer" spec="^1.0.0" />
<plugin name="cordova-plugin-secure-storage" spec="^4.0.2" />
<plugin name="cordova-plugin-screen-orientation" spec="^3.0.4" />
<plugin name="cordova-plugin-app-version" spec="^0.1.9" />
<plugin name="cordova-plugin-ios-security" spec="^2.0.0" />
<plugin name="cordova-android-security" spec="^1.0.0" />
<!-- Enterprise Platform Preferences -->
<platform name="android">
<preference name="android-minSdkVersion" value="23" />
<preference name="android-targetSdkVersion" value="30" />
<preference name="android:usesCleartextTraffic" value="false" />
<preference name="android:networkSecurityConfig" value="res/xml/network_security_config.xml" />
<preference name="android:allowBackup" value="false" />
<preference name="android:fullBackupContent" value="false" />
<preference name="AndroidInsecureFileModeEnabled" value="false" />
<!-- Certificate Pinning -->
<config-file target="AndroidManifest.xml" parent="/manifest/application">
<meta-data android:name="io.intercom.certificate-pin" android:value="sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" />
</config-file>
</platform>
<platform name="ios">
<preference name="deployment-target" value="12.0" />
<preference name="BackupWebStorage" value="none" />
<preference name="DisallowOverscroll" value="true" />
<!-- iOS Security Configuration -->
<config-file target="*-Info.plist" parent="ITSAppUsesNonExemptEncryption">
<true/>
</config-file>
<!-- App Transport Security -->
<config-file target="*-Info.plist" parent="NSAppTransportSecurity">
<dict>
<key>NSAllowsArbitraryLoads</key>
<false/>
<key>NSExceptionDomains</key>
<dict>
<key>api.enterprise.com</key>
<dict>
<key>NSExceptionRequiresForwardSecrecy</key>
<false/>
<key>NSExceptionMinimumTLSVersion</key>
<string>TLSv1.2</string>
<key>NSIncludesSubdomains</key>
<true/>
</dict>
</dict>
</dict>
</config-file>
</platform>
</widget>
// js/services/SecurityService.js
class SecurityService {
constructor() {
this.secureStorage = null;
this.initializeSecurity();
}
async initializeSecurity() {
try {
// Initialize secure storage
if (window.cordova && window.cordova.plugins.SecureStorage) {
this.secureStorage = cordova.plugins.SecureStorage;
await this.initializeSecureStorage();
}
// Setup certificate pinning
await this.setupCertificatePinning();
// Initialize encryption
await this.initializeEncryption();
} catch (error) {
console.error('Security initialization failed:', error);
}
}
async initializeSecureStorage() {
return new Promise((resolve, reject) => {
this.secureStorage.init(
() => {
console.log('Secure storage initialized');
resolve();
},
(error) => {
console.error('Secure storage initialization failed:', error);
reject(error);
},
'enterprise_secure_storage'
);
});
}
async setupCertificatePinning() {
if (!window.cordova || !window.cordova.plugins.CertificatePinning) {
console.warn('Certificate pinning plugin not available');
return;
}
return new Promise((resolve, reject) => {
const certificates = [
'sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=', // Primary certificate
'sha256/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=' // Backup certificate
];
cordova.plugins.CertificatePinning.pinCertificates(
certificates,
() => {
console.log('Certificate pinning configured');
resolve();
},
(error) => {
console.error('Certificate pinning failed:', error);
reject(error);
}
);
});
}
async initializeEncryption() {
// Initialize AES encryption key
this.encryptionKey = await this.getOrCreateEncryptionKey();
}
async getOrCreateEncryptionKey() {
try {
const existingKey = await this.getSecureItem('encryption_key');
if (existingKey) {
return existingKey;
}
// Generate new encryption key
const key = this.generateEncryptionKey();
await this.setSecureItem('encryption_key', key);
return key;
} catch (error) {
console.error('Encryption key setup failed:', error);
throw error;
}
}
generateEncryptionKey() {
// Generate a random 256-bit key
const array = new Uint8Array(32);
crypto.getRandomValues(array);
return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
}
// Secure Storage Operations
async setSecureItem(key, value) {
if (!this.secureStorage) {
throw new Error('Secure storage not available');
}
return new Promise((resolve, reject) => {
this.secureStorage.set(
() => resolve(),
(error) => reject(error),
key,
value
);
});
}
async getSecureItem(key) {
if (!this.secureStorage) {
throw new Error('Secure storage not available');
}
return new Promise((resolve, reject) => {
this.secureStorage.get(
(value) => resolve(value),
(error) => reject(error),
key
);
});
}
async removeSecureItem(key) {
if (!this.secureStorage) {
throw new Error('Secure storage not available');
}
return new Promise((resolve, reject) => {
this.secureStorage.remove(
() => resolve(),
(error) => reject(error),
key
);
});
}
// Encryption/Decryption
async encryptData(data) {
try {
const encoder = new TextEncoder();
const dataBuffer = encoder.encode(JSON.stringify(data));
// Import encryption key
const key = await crypto.subtle.importKey(
'raw',
new TextEncoder().encode(this.encryptionKey).slice(0, 32),
{ name: 'AES-CBC' },
false,
['encrypt']
);
// Generate random IV
const iv = crypto.getRandomValues(new Uint8Array(16));
// Encrypt data
const encryptedData = await crypto.subtle.encrypt(
{
name: 'AES-CBC',
iv: iv
},
key,
dataBuffer
);
// Combine IV and encrypted data
const combined = new Uint8Array(iv.length + encryptedData.byteLength);
combined.set(iv);
combined.set(new Uint8Array(encryptedData), iv.length);
return btoa(String.fromCharCode.apply(null, combined));
} catch (error) {
console.error('Encryption failed:', error);
throw error;
}
}
async decryptData(encryptedData) {
try {
const combined = new Uint8Array(
atob(encryptedData).split('').map(char => char.charCodeAt(0))
);
// Extract IV
const iv = combined.slice(0, 16);
const data = combined.slice(16);
// Import encryption key
const key = await crypto.subtle.importKey(
'raw',
new TextEncoder().encode(this.encryptionKey).slice(0, 32),
{ name: 'AES-CBC' },
false,
['decrypt']
);
// Decrypt data
const decryptedData = await crypto.subtle.decrypt(
{
name: 'AES-CBC',
iv: iv
},
key,
data
);
const decoder = new TextDecoder();
return JSON.parse(decoder.decode(decryptedData));
} catch (error) {
console.error('Decryption failed:', error);
throw error;
}
}
// API Security
async makeSecureRequest(url, options = {}) {
const defaultOptions = {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'X-App-Version': await this.getAppVersion(),
'X-Device-ID': await this.getDeviceId()
},
timeout: 30000
};
const requestOptions = { ...defaultOptions, ...options };
try {
// Add authentication token if available
const authToken = await this.getSecureItem('auth_token');
if (authToken) {
requestOptions.headers['Authorization'] = `Bearer ${authToken}`;
}
// Make request with certificate pinning
const response = await this.httpRequestWithPinning(url, requestOptions);
return response;
} catch (error) {
console.error('Secure request failed:', error);
throw error;
}
}
async httpRequestWithPinning(url, options) {
if (window.cordova && window.cordova.plugin.http) {
// Use Cordova HTTP plugin with certificate pinning
return new Promise((resolve, reject) => {
cordova.plugin.http.setRequestSerializer('json');
cordova.plugin.http.setResponseSerializer('json');
const success = (response) => {
resolve({
status: response.status,
data: response.data,
headers: response.headers
});
};
const failure = (error) => {
reject(new Error(`HTTP Error: ${error.error}`));
};
if (options.method === 'GET') {
cordova.plugin.http.get(url, options.params || {}, options.headers, success, failure);
} else if (options.method === 'POST') {
cordova.plugin.http.post(url, options.data || {}, options.headers, success, failure);
} else if (options.method === 'PUT') {
cordova.plugin.http.put(url, options.data || {}, options.headers, success, failure);
} else if (options.method === 'DELETE') {
cordova.plugin.http.delete(url, options.params || {}, options.headers, success, failure);
}
});
} else {
// Fallback to standard fetch with security considerations
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return {
status: response.status,
data: await response.json(),
headers: response.headers
};
}
}
async getAppVersion() {
return new Promise((resolve, reject) => {
if (window.cordova && window.cordova.getVersionCode) {
window.cordova.getVersionCode(
(version) => resolve(version),
(error) => reject(error)
);
} else {
resolve('1.0.0'); // Fallback version
}
});
}
async getDeviceId() {
try {
let deviceId = await this.getSecureItem('device_id');
if (!deviceId) {
deviceId = this.generateDeviceId();
await this.setSecureItem('device_id', deviceId);
}
return deviceId;
} catch (error) {
console.error('Failed to get device ID:', error);
return 'unknown';
}
}
generateDeviceId() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
const r = Math.random() * 16 | 0;
const v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
// Session Management
async createSession(userCredentials) {
try {
// Authenticate with backend
const authResponse = await this.makeSecureRequest(
'https://api.enterprise.com/auth/login',
{
method: 'POST',
data: userCredentials
}
);
if (authResponse.status === 200 && authResponse.data.token) {
// Securely store authentication tokens
await this.setSecureItem('auth_token', authResponse.data.token);
await this.setSecureItem('refresh_token', authResponse.data.refreshToken);
// Encrypt and store user session data
const encryptedSession = await this.encryptData({
user: authResponse.data.user,
permissions: authResponse.data.permissions,
expiresAt: Date.now() + (authResponse.data.expiresIn * 1000)
});
await this.setSecureItem('user_session', encryptedSession);
return authResponse.data;
} else {
throw new Error('Authentication failed');
}
} catch (error) {
console.error('Session creation failed:', error);
throw error;
}
}
async validateSession() {
try {
const encryptedSession = await this.getSecureItem('user_session');
if (!encryptedSession) {
return false;
}
const session = await this.decryptData(encryptedSession);
// Check if session is expired
if (session.expiresAt < Date.now()) {
await this.clearSession();
return false;
}
// Validate token with backend
const validationResponse = await this.makeSecureRequest(
'https://api.enterprise.com/auth/validate',
{
method: 'POST',
data: { token: await this.getSecureItem('auth_token') }
}
);
return validationResponse.status === 200;
} catch (error) {
console.error('Session validation failed:', error);
await this.clearSession();
return false;
}
}
async clearSession() {
try {
await this.removeSecureItem('auth_token');
await this.removeSecureItem('refresh_token');
await this.removeSecureItem('user_session');
await this.removeSecureItem('device_id');
} catch (error) {
console.error('Session cleanup failed:', error);
}
}
// Audit Logging
async logSecurityEvent(eventType, details) {
try {
const auditLog = {
timestamp: new Date().toISOString(),
eventType: eventType,
details: details,
deviceId: await this.getDeviceId(),
appVersion: await this.getAppVersion()
};
// Encrypt audit log
const encryptedLog = await this.encryptData(auditLog);
// Send to secure logging endpoint
await this.makeSecureRequest(
'https://api.enterprise.com/audit/log',
{
method: 'POST',
data: { log: encryptedLog }
}
);
} catch (error) {
console.error('Security event logging failed:', error);
}
}
}
// Export for use in other modules
if (typeof module !== 'undefined' && module.exports) {
module.exports = SecurityService;
}
// js/EnterpriseApp.js (Main Application)
class EnterpriseApp {
constructor() {
this.securityService = new SecurityService();
this.isAuthenticated = false;
}
async initialize() {
try {
// Validate existing session
this.isAuthenticated = await this.securityService.validateSession();
if (this.isAuthenticated) {
await this.loadUserData();
await this.showMainInterface();
} else {
await this.showLoginInterface();
}
// Log app initialization
await this.securityService.logSecurityEvent('APP_INITIALIZED', {
timestamp: Date.now(),
existingSession: this.isAuthenticated
});
} catch (error) {
console.error('App initialization failed:', error);
await this.handleInitializationError(error);
}
}
async login(username, password) {
try {
const credentials = {
username: username,
password: password,
deviceId: await this.securityService.getDeviceId(),
timestamp: Date.now()
};
const session = await this.securityService.createSession(credentials);
this.isAuthenticated = true;
await this.securityService.logSecurityEvent('USER_LOGIN', {
username: username,
success: true
});
await this.loadUserData();
await this.showMainInterface();
return session;
} catch (error) {
await this.securityService.logSecurityEvent('LOGIN_FAILED', {
username: username,
error: error.message
});
throw error;
}
}
async logout() {
try {
await this.securityService.logSecurityEvent('USER_LOGOUT', {
timestamp: Date.now()
});
await this.securityService.clearSession();
this.isAuthenticated = false;
await this.showLoginInterface();
} catch (error) {
console.error('Logout failed:', error);
}
}
async loadUserData() {
// Load user-specific data securely
}
async showMainInterface() {
// Show main application interface
}
async showLoginInterface() {
// Show login interface
}
async handleInitializationError(error) {
// Handle initialization errors
console.error('Initialization error:', error);
await this.securityService.logSecurityEvent('INIT_ERROR', {
error: error.message
});
}
}
// Initialize the enterprise app
document.addEventListener('deviceready', () => {
const app = new EnterpriseApp();
app.initialize();
});