🎯 Exemplos recomendados
Balanced sample collections from various categories for you to explore
Exemplos de Desenvolvimento Móvel Apache Cordova
Exemplos de desenvolvimento de aplicativo móvel híbrido Apache Cordova incluindo uso de plugins, APIs de dispositivo, implantação multiplataforma e integração com sistemas legados
💻 Implementação de Plugins Core Cordova javascript
🟢 simple
Implementar plugins essenciais Cordova para recursos de dispositivo incluindo câmera, geolocalização, notificações, sistema de arquivos e informações do dispositivo
// 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();
💻 Desenvolvimento de Plugins Personalizados Cordova javascript
🟡 intermediate
Criar plugins personalizados Cordova com implementações de código nativo para iOS (Objective-C/Swift) e 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();
});
💻 Implantação e Segurança Empresarial Cordova javascript
🔴 complex
Configurar aplicativos Cordova de nível empresarial com melhores práticas de segurança, certificate pinning, criptografia e estratégias de implantação corporativa
// 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();
});