Native plugin wrappers for Cordova and Ionic with TypeScript, ES6+, Promise and Observable support
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Access to device sensors including accelerometer, gyroscope, compass, battery status, vibration, and other hardware sensors for motion detection and device state monitoring.
Monitor device acceleration and movement for gesture recognition, orientation detection, and motion-based interactions.
/**
* Acceleration data interface
*/
interface Acceleration {
/** Acceleration along the x-axis in m/s² */
x: number;
/** Acceleration along the y-axis in m/s² */
y: number;
/** Acceleration along the z-axis in m/s² */
z: number;
/** Timestamp when acceleration was captured */
timestamp: number;
}
/**
* Accelerometer configuration options
*/
interface AccelerometerOptions {
/** Update frequency in milliseconds (default: 10000) */
frequency?: number;
}
/**
* DeviceMotion class for accelerometer access
*/
class DeviceMotion {
/**
* Get current acceleration values
* @returns Promise resolving to current Acceleration data
*/
static getCurrentAcceleration(): Promise<Acceleration>;
/**
* Watch acceleration changes continuously
* @param options Accelerometer configuration options
* @returns Observable emitting Acceleration updates
*/
static watchAcceleration(options?: AccelerometerOptions): Observable<Acceleration>;
}Usage Examples:
import { DeviceMotion, AccelerometerOptions } from 'ionic-native';
// Get current acceleration
async function getCurrentMotion() {
try {
const acceleration = await DeviceMotion.getCurrentAcceleration();
console.log('Current acceleration:');
console.log(`X: ${acceleration.x.toFixed(3)} m/s²`);
console.log(`Y: ${acceleration.y.toFixed(3)} m/s²`);
console.log(`Z: ${acceleration.z.toFixed(3)} m/s²`);
return acceleration;
} catch (error) {
console.error('Error getting acceleration:', error);
throw error;
}
}
// Monitor device motion for shake detection
function setupShakeDetection() {
const options: AccelerometerOptions = {
frequency: 100 // Update every 100ms
};
let lastAcceleration = { x: 0, y: 0, z: 0, timestamp: 0 };
const shakeThreshold = 15; // m/s²
return DeviceMotion.watchAcceleration(options).subscribe(
(acceleration) => {
// Calculate acceleration difference
const deltaX = Math.abs(acceleration.x - lastAcceleration.x);
const deltaY = Math.abs(acceleration.y - lastAcceleration.y);
const deltaZ = Math.abs(acceleration.z - lastAcceleration.z);
const totalDelta = deltaX + deltaY + deltaZ;
if (totalDelta > shakeThreshold) {
console.log('Shake detected!');
handleShakeEvent();
}
lastAcceleration = acceleration;
},
(error) => {
console.error('Accelerometer error:', error);
}
);
}
// Motion-based gesture recognition
class MotionGestureDetector {
private subscription: any;
private motionBuffer: Acceleration[] = [];
private readonly bufferSize = 20;
startDetection() {
const options: AccelerometerOptions = { frequency: 50 };
this.subscription = DeviceMotion.watchAcceleration(options).subscribe(
(acceleration) => {
this.processMotionData(acceleration);
}
);
}
stopDetection() {
if (this.subscription) {
this.subscription.unsubscribe();
}
this.motionBuffer = [];
}
private processMotionData(acceleration: Acceleration) {
// Add to buffer
this.motionBuffer.push(acceleration);
// Keep buffer size manageable
if (this.motionBuffer.length > this.bufferSize) {
this.motionBuffer.shift();
}
// Analyze gestures
this.detectGestures();
}
private detectGestures() {
if (this.motionBuffer.length < 10) return;
// Detect shake
if (this.detectShake()) {
this.onGestureDetected('shake');
}
// Detect tilt
const tiltDirection = this.detectTilt();
if (tiltDirection) {
this.onGestureDetected('tilt', tiltDirection);
}
// Detect tap
if (this.detectTap()) {
this.onGestureDetected('tap');
}
}
private detectShake(): boolean {
const recent = this.motionBuffer.slice(-5);
const avgAcceleration = recent.reduce((acc, curr) => {
return Math.abs(curr.x) + Math.abs(curr.y) + Math.abs(curr.z);
}, 0) / recent.length;
return avgAcceleration > 20;
}
private detectTilt(): string | null {
const latest = this.motionBuffer[this.motionBuffer.length - 1];
if (Math.abs(latest.x) > 7) {
return latest.x > 0 ? 'left' : 'right';
}
if (Math.abs(latest.y) > 7) {
return latest.y > 0 ? 'forward' : 'backward';
}
return null;
}
private detectTap(): boolean {
if (this.motionBuffer.length < 3) return false;
const recent = this.motionBuffer.slice(-3);
const zAccelerations = recent.map(a => Math.abs(a.z));
// Look for spike in Z acceleration (tap)
const maxZ = Math.max(...zAccelerations);
return maxZ > 15;
}
private onGestureDetected(gesture: string, direction?: string) {
console.log(`Gesture detected: ${gesture}`, direction ? `(${direction})` : '');
// Trigger appropriate actions
switch (gesture) {
case 'shake':
this.handleShake();
break;
case 'tilt':
this.handleTilt(direction!);
break;
case 'tap':
this.handleTap();
break;
}
}
private handleShake() {
// Implement shake action (e.g., refresh, undo)
}
private handleTilt(direction: string) {
// Implement tilt actions (e.g., navigation, scrolling)
}
private handleTap() {
// Implement tap action (e.g., select, activate)
}
}Access device compass and magnetometer for heading information and navigation features.
/**
* Compass heading data
*/
interface CompassHeading {
/** Magnetic heading in degrees (0-359.99) */
magneticHeading: number;
/** True heading in degrees (0-359.99) */
trueHeading: number;
/** Heading accuracy in degrees */
headingAccuracy: number;
/** Timestamp when heading was captured */
timestamp: number;
}
/**
* Compass error information
*/
interface CompassError {
/** Error code (0: COMPASS_INTERNAL_ERR, 20: COMPASS_NOT_SUPPORTED) */
code: number;
}
/**
* Compass configuration options
*/
interface CompassOptions {
/** Update frequency in milliseconds (default: 100) */
frequency?: number;
/** Compass filter value (iOS only, default: 1) */
filter?: number;
}
/**
* DeviceOrientation class for compass access
*/
class DeviceOrientation {
/**
* Get current compass heading
* @returns Promise resolving to current CompassHeading
*/
static getCurrentHeading(): Promise<CompassHeading>;
/**
* Watch compass heading changes
* @param options Compass configuration options
* @returns Observable emitting CompassHeading updates
*/
static watchHeading(options?: CompassOptions): Observable<CompassHeading>;
}Usage Examples:
import { DeviceOrientation, CompassOptions } from 'ionic-native';
// Get current compass heading
async function getCurrentHeading() {
try {
const heading = await DeviceOrientation.getCurrentHeading();
console.log('Current heading:');
console.log(`Magnetic: ${heading.magneticHeading.toFixed(1)}°`);
console.log(`True: ${heading.trueHeading.toFixed(1)}°`);
console.log(`Accuracy: ±${heading.headingAccuracy.toFixed(1)}°`);
return heading;
} catch (error) {
console.error('Error getting compass heading:', error);
throw error;
}
}
// Compass navigation system
class CompassNavigation {
private subscription: any;
private currentHeading = 0;
private targetHeading = 0;
startCompass(updateCallback?: (heading: number) => void) {
const options: CompassOptions = {
frequency: 100,
filter: 1
};
this.subscription = DeviceOrientation.watchHeading(options).subscribe(
(heading) => {
this.currentHeading = heading.magneticHeading;
if (updateCallback) {
updateCallback(this.currentHeading);
}
this.updateCompassUI(heading);
},
(error) => {
console.error('Compass error:', error);
this.handleCompassError(error);
}
);
}
stopCompass() {
if (this.subscription) {
this.subscription.unsubscribe();
}
}
setTarget(targetHeading: number) {
this.targetHeading = targetHeading;
}
getDirectionToTarget(): { direction: string; degrees: number } {
let difference = this.targetHeading - this.currentHeading;
// Normalize to -180 to 180 range
while (difference > 180) difference -= 360;
while (difference < -180) difference += 360;
const absDifference = Math.abs(difference);
let direction = '';
if (absDifference < 5) {
direction = 'straight';
} else if (difference > 0) {
direction = 'right';
} else {
direction = 'left';
}
return { direction, degrees: absDifference };
}
private updateCompassUI(heading: CompassHeading) {
// Update compass rose or navigation arrow
const compassElement = document.getElementById('compass');
if (compassElement) {
compassElement.style.transform = `rotate(${-heading.magneticHeading}deg)`;
}
// Update direction indicator
const directionInfo = this.getDirectionToTarget();
const directionElement = document.getElementById('direction');
if (directionElement) {
directionElement.textContent = `${directionInfo.direction} (${directionInfo.degrees.toFixed(1)}°)`;
}
}
private handleCompassError(error: any) {
if (error.code === 20) {
console.error('Compass not supported on this device');
} else {
console.error('Compass internal error');
}
}
}
// Compass-based features
function getCardinalDirection(heading: number): string {
const directions = ['N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE',
'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW'];
const index = Math.round(heading / 22.5) % 16;
return directions[index];
}
function calculateBearing(lat1: number, lon1: number, lat2: number, lon2: number): number {
const dLon = (lon2 - lon1) * Math.PI / 180;
const lat1Rad = lat1 * Math.PI / 180;
const lat2Rad = lat2 * Math.PI / 180;
const y = Math.sin(dLon) * Math.cos(lat2Rad);
const x = Math.cos(lat1Rad) * Math.sin(lat2Rad) -
Math.sin(lat1Rad) * Math.cos(lat2Rad) * Math.cos(dLon);
let bearing = Math.atan2(y, x) * 180 / Math.PI;
return (bearing + 360) % 360;
}Access device gyroscope for rotation rate detection and advanced motion sensing.
/**
* Gyroscope orientation data
*/
interface GyroscopeOrientation {
/** Angular velocity around x-axis in deg/s */
x: number;
/** Angular velocity around y-axis in deg/s */
y: number;
/** Angular velocity around z-axis in deg/s */
z: number;
/** Timestamp when data was captured */
timestamp: number;
}
/**
* Gyroscope configuration options
*/
interface GyroscopeOptions {
/** Update frequency in milliseconds (default: 100) */
frequency?: number;
}
/**
* Gyroscope class for rotation sensing
*/
class Gyroscope {
/**
* Get current gyroscope data
* @param options Gyroscope configuration options
* @returns Promise resolving to GyroscopeOrientation
*/
static getCurrent(options?: GyroscopeOptions): Promise<GyroscopeOrientation>;
/**
* Watch gyroscope changes continuously
* @param options Gyroscope configuration options
* @returns Observable emitting GyroscopeOrientation updates
*/
static watch(options?: GyroscopeOptions): Observable<GyroscopeOrientation>;
}Usage Examples:
import { Gyroscope, GyroscopeOptions } from 'ionic-native';
// Rotation rate monitoring
class RotationDetector {
private subscription: any;
private rotationThreshold = 50; // deg/s
startMonitoring() {
const options: GyroscopeOptions = {
frequency: 50
};
this.subscription = Gyroscope.watch(options).subscribe(
(orientation) => {
this.processRotationData(orientation);
},
(error) => {
console.error('Gyroscope error:', error);
}
);
}
stopMonitoring() {
if (this.subscription) {
this.subscription.unsubscribe();
}
}
private processRotationData(orientation: GyroscopeOrientation) {
const totalRotation = Math.abs(orientation.x) + Math.abs(orientation.y) + Math.abs(orientation.z);
if (totalRotation > this.rotationThreshold) {
this.detectRotationGesture(orientation);
}
}
private detectRotationGesture(orientation: GyroscopeOrientation) {
// Detect specific rotation patterns
if (Math.abs(orientation.z) > this.rotationThreshold) {
const direction = orientation.z > 0 ? 'clockwise' : 'counterclockwise';
console.log(`Device rotation: ${direction}`);
this.onRotationDetected('spin', direction);
}
if (Math.abs(orientation.x) > this.rotationThreshold) {
const direction = orientation.x > 0 ? 'forward' : 'backward';
console.log(`Device pitch: ${direction}`);
this.onRotationDetected('pitch', direction);
}
if (Math.abs(orientation.y) > this.rotationThreshold) {
const direction = orientation.y > 0 ? 'left' : 'right';
console.log(`Device roll: ${direction}`);
this.onRotationDetected('roll', direction);
}
}
private onRotationDetected(type: string, direction: string) {
// Handle rotation gestures
console.log(`Rotation gesture: ${type} ${direction}`);
}
}Monitor device battery level and charging status for power management features.
/**
* Battery status data
*/
interface StatusObject {
/** Battery level (0-100) */
level: number;
/** Whether device is plugged in */
isPlugged: boolean;
}
/**
* BatteryStatus class for battery monitoring
*/
class BatteryStatus {
/**
* Monitor battery status changes
* @returns Observable emitting battery status updates
*/
static onStatusChange(): Observable<StatusObject>;
/**
* Monitor low battery events
* @returns Observable emitting low battery events
*/
static onLow(): Observable<StatusObject>;
/**
* Monitor critical battery events
* @returns Observable emitting critical battery events
*/
static onCritical(): Observable<StatusObject>;
}Usage Examples:
import { BatteryStatus } from 'ionic-native';
// Battery monitoring service
class BatteryMonitor {
private statusSubscription: any;
private lowBatterySubscription: any;
private criticalBatterySubscription: any;
startMonitoring() {
// Monitor general status changes
this.statusSubscription = BatteryStatus.onStatusChange().subscribe(
(status) => {
this.updateBatteryUI(status);
this.handleBatteryStatus(status);
}
);
// Monitor low battery
this.lowBatterySubscription = BatteryStatus.onLow().subscribe(
(status) => {
this.handleLowBattery(status);
}
);
// Monitor critical battery
this.criticalBatterySubscription = BatteryStatus.onCritical().subscribe(
(status) => {
this.handleCriticalBattery(status);
}
);
}
stopMonitoring() {
if (this.statusSubscription) this.statusSubscription.unsubscribe();
if (this.lowBatterySubscription) this.lowBatterySubscription.unsubscribe();
if (this.criticalBatterySubscription) this.criticalBatterySubscription.unsubscribe();
}
private updateBatteryUI(status: StatusObject) {
// Update battery indicator
const batteryElement = document.getElementById('battery-level');
if (batteryElement) {
batteryElement.textContent = `${status.level}%`;
batteryElement.className = `battery-level ${this.getBatteryClass(status.level)}`;
}
// Update charging indicator
const chargingElement = document.getElementById('charging-status');
if (chargingElement) {
chargingElement.textContent = status.isPlugged ? 'Charging' : 'Not Charging';
}
}
private getBatteryClass(level: number): string {
if (level > 80) return 'high';
if (level > 50) return 'medium';
if (level > 20) return 'low';
return 'critical';
}
private handleBatteryStatus(status: StatusObject) {
// Adjust app behavior based on battery level
if (status.level < 20 && !status.isPlugged) {
this.enablePowerSavingMode();
} else if (status.level > 30 || status.isPlugged) {
this.disablePowerSavingMode();
}
}
private handleLowBattery(status: StatusObject) {
console.warn('Low battery warning:', status);
// Show low battery notification
this.showBatteryWarning('Battery Low', `Battery level: ${status.level}%. Please charge your device.`);
// Enable aggressive power saving
this.enablePowerSavingMode();
}
private handleCriticalBattery(status: StatusObject) {
console.error('Critical battery warning:', status);
// Show critical battery alert
this.showBatteryWarning('Critical Battery', `Battery critically low: ${status.level}%. Save your work immediately.`);
// Enable emergency power saving
this.enableEmergencyMode();
}
private enablePowerSavingMode() {
console.log('Enabling power saving mode');
// Reduce app functionality to save battery
// - Lower screen brightness
// - Reduce sync frequency
// - Disable animations
// - Limit background processes
}
private disablePowerSavingMode() {
console.log('Disabling power saving mode');
// Restore normal app functionality
}
private enableEmergencyMode() {
console.log('Enabling emergency mode');
// Minimal functionality only
// - Auto-save data
// - Disable non-essential features
// - Show power saving tips
}
private showBatteryWarning(title: string, message: string) {
// Show appropriate warning UI
// Could use Toast, Dialogs, or custom notification
}
}Control device vibration for haptic feedback and notifications.
/**
* Vibration class for haptic feedback
*/
class Vibration {
/**
* Vibrate device
* @param time Vibration duration in milliseconds, or array of durations for pattern
*/
static vibrate(time: number | number[]): void;
}Usage Examples:
import { Vibration } from 'ionic-native';
// Haptic feedback patterns
class HapticFeedback {
// Success feedback
static success() {
Vibration.vibrate(100);
}
// Error feedback
static error() {
Vibration.vibrate([100, 50, 100]);
}
// Warning feedback
static warning() {
Vibration.vibrate([200, 100, 200]);
}
// Notification feedback
static notification() {
Vibration.vibrate([50, 50, 50]);
}
// Custom pattern
static customPattern(pattern: number[]) {
Vibration.vibrate(pattern);
}
// Long press feedback
static longPress() {
Vibration.vibrate(50);
}
// Button tap feedback
static tap() {
Vibration.vibrate(30);
}
// Heartbeat pattern
static heartbeat() {
Vibration.vibrate([100, 100, 200, 100, 300]);
}
// SOS pattern
static sos() {
// S-O-S in morse code
Vibration.vibrate([
100, 50, 100, 50, 100, // S
200, 300, 50, 300, 50, 300, // O
200, 100, 50, 100, 50, 100 // S
]);
}
}
// Usage examples
function showHapticExamples() {
// Basic vibrations
HapticFeedback.success();
HapticFeedback.error();
HapticFeedback.warning();
// Custom patterns
HapticFeedback.customPattern([200, 100, 200, 100, 400]);
// Contextual feedback
HapticFeedback.tap(); // For button presses
HapticFeedback.longPress(); // For long press gestures
}Control device LED flashlight for utility and emergency features.
/**
* Flashlight class for LED control
*/
class Flashlight {
/**
* Check if flashlight is available
* @returns Promise resolving to availability status
*/
static available(): Promise<boolean>;
/**
* Turn flashlight on
* @returns Promise resolving to success status
*/
static switchOn(): Promise<boolean>;
/**
* Turn flashlight off
* @returns Promise resolving to success status
*/
static switchOff(): Promise<boolean>;
/**
* Toggle flashlight state
* @returns Promise resolving to new state (true = on)
*/
static toggle(): Promise<boolean>;
}Usage Examples:
import { Flashlight } from 'ionic-native';
// Flashlight controller
class FlashlightController {
private isOn = false;
async initialize() {
try {
const available = await Flashlight.available();
if (!available) {
console.log('Flashlight not available on this device');
return false;
}
console.log('Flashlight available');
return true;
} catch (error) {
console.error('Error checking flashlight availability:', error);
return false;
}
}
async turnOn() {
try {
const success = await Flashlight.switchOn();
if (success) {
this.isOn = true;
console.log('Flashlight turned on');
}
return success;
} catch (error) {
console.error('Error turning on flashlight:', error);
return false;
}
}
async turnOff() {
try {
const success = await Flashlight.switchOff();
if (success) {
this.isOn = false;
console.log('Flashlight turned off');
}
return success;
} catch (error) {
console.error('Error turning off flashlight:', error);
return false;
}
}
async toggle() {
try {
const newState = await Flashlight.toggle();
this.isOn = newState;
console.log(`Flashlight ${newState ? 'on' : 'off'}`);
return newState;
} catch (error) {
console.error('Error toggling flashlight:', error);
return this.isOn;
}
}
getState(): boolean {
return this.isOn;
}
// Morse code signaling
async morseCode(message: string) {
const morseMap: { [key: string]: string } = {
'A': '.-', 'B': '-...', 'C': '-.-.', 'D': '-..', 'E': '.', 'F': '..-.',
'G': '--.', 'H': '....', 'I': '..', 'J': '.---', 'K': '-.-', 'L': '.-..',
'M': '--', 'N': '-.', 'O': '---', 'P': '.--.', 'Q': '--.-', 'R': '.-.',
'S': '...', 'T': '-', 'U': '..-', 'V': '...-', 'W': '.--', 'X': '-..-',
'Y': '-.--', 'Z': '--..', '0': '-----', '1': '.----', '2': '..---',
'3': '...--', '4': '....-', '5': '.....', '6': '-....', '7': '--...',
'8': '---..', '9': '----.', ' ': '/'
};
for (const char of message.toUpperCase()) {
const morse = morseMap[char];
if (morse) {
await this.sendMorseSequence(morse);
await this.delay(200); // Inter-character pause
}
}
}
private async sendMorseSequence(morse: string) {
for (const symbol of morse) {
if (symbol === '.') {
await this.flash(100); // Dot
} else if (symbol === '-') {
await this.flash(300); // Dash
} else if (symbol === '/') {
await this.delay(500); // Word separator
}
await this.delay(100); // Inter-element pause
}
}
private async flash(duration: number) {
await this.turnOn();
await this.delay(duration);
await this.turnOff();
}
private delay(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
// SOS emergency signal
async sos() {
const sosPattern = '... --- ...'; // SOS in morse
await this.morseCode('SOS');
}
// Strobe effect
async strobe(duration: number = 5000, frequency: number = 10) {
const interval = 1000 / frequency;
const endTime = Date.now() + duration;
while (Date.now() < endTime) {
await this.toggle();
await this.delay(interval / 2);
}
// Ensure flashlight is off when done
if (this.isOn) {
await this.turnOff();
}
}
}