Enable Discovery debug logging for production troubleshooting
- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
This commit is contained in:
704
resources/js/modules/api-manager/DeviceManager.js
Normal file
704
resources/js/modules/api-manager/DeviceManager.js
Normal file
@@ -0,0 +1,704 @@
|
||||
// modules/api-manager/DeviceManager.js
|
||||
import { Logger } from '../../core/logger.js';
|
||||
|
||||
/**
|
||||
* Device APIs Manager - Geolocation, Sensors, Battery, Network, Vibration
|
||||
*/
|
||||
export class DeviceManager {
|
||||
constructor(config = {}) {
|
||||
this.config = config;
|
||||
this.activeWatchers = new Map();
|
||||
this.sensorData = new Map();
|
||||
|
||||
// Check API support
|
||||
this.support = {
|
||||
geolocation: 'geolocation' in navigator,
|
||||
deviceMotion: 'DeviceMotionEvent' in window,
|
||||
deviceOrientation: 'DeviceOrientationEvent' in window,
|
||||
vibration: 'vibrate' in navigator,
|
||||
battery: 'getBattery' in navigator,
|
||||
networkInfo: 'connection' in navigator || 'mozConnection' in navigator || 'webkitConnection' in navigator,
|
||||
wakeLock: 'wakeLock' in navigator,
|
||||
bluetooth: 'bluetooth' in navigator,
|
||||
usb: 'usb' in navigator,
|
||||
serial: 'serial' in navigator
|
||||
};
|
||||
|
||||
Logger.info('[DeviceManager] Initialized with support:', this.support);
|
||||
|
||||
// Initialize sensors if available
|
||||
this.initializeSensors();
|
||||
}
|
||||
|
||||
/**
|
||||
* Geolocation API
|
||||
*/
|
||||
geolocation = {
|
||||
// Get current position
|
||||
getCurrent: (options = {}) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this.support.geolocation) {
|
||||
reject(new Error('Geolocation not supported'));
|
||||
return;
|
||||
}
|
||||
|
||||
const defaultOptions = {
|
||||
enableHighAccuracy: true,
|
||||
timeout: 10000,
|
||||
maximumAge: 60000,
|
||||
...options
|
||||
};
|
||||
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
(position) => {
|
||||
const location = this.enhanceLocationData(position);
|
||||
Logger.info('[DeviceManager] Location acquired:', {
|
||||
lat: location.latitude,
|
||||
lng: location.longitude,
|
||||
accuracy: location.accuracy
|
||||
});
|
||||
resolve(location);
|
||||
},
|
||||
(error) => {
|
||||
Logger.error('[DeviceManager] Geolocation failed:', error.message);
|
||||
reject(error);
|
||||
},
|
||||
defaultOptions
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
// Watch position changes
|
||||
watch: (callback, options = {}) => {
|
||||
if (!this.support.geolocation) {
|
||||
throw new Error('Geolocation not supported');
|
||||
}
|
||||
|
||||
const defaultOptions = {
|
||||
enableHighAccuracy: true,
|
||||
timeout: 30000,
|
||||
maximumAge: 10000,
|
||||
...options
|
||||
};
|
||||
|
||||
const watchId = navigator.geolocation.watchPosition(
|
||||
(position) => {
|
||||
const location = this.enhanceLocationData(position);
|
||||
callback(location);
|
||||
},
|
||||
(error) => {
|
||||
Logger.error('[DeviceManager] Location watch failed:', error.message);
|
||||
callback({ error });
|
||||
},
|
||||
defaultOptions
|
||||
);
|
||||
|
||||
this.activeWatchers.set(`geo_${watchId}`, {
|
||||
type: 'geolocation',
|
||||
id: watchId,
|
||||
stop: () => {
|
||||
navigator.geolocation.clearWatch(watchId);
|
||||
this.activeWatchers.delete(`geo_${watchId}`);
|
||||
}
|
||||
});
|
||||
|
||||
Logger.info('[DeviceManager] Location watch started:', watchId);
|
||||
|
||||
return {
|
||||
id: watchId,
|
||||
stop: () => {
|
||||
navigator.geolocation.clearWatch(watchId);
|
||||
this.activeWatchers.delete(`geo_${watchId}`);
|
||||
Logger.info('[DeviceManager] Location watch stopped:', watchId);
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
// Calculate distance between two points
|
||||
distance: (pos1, pos2) => {
|
||||
const R = 6371; // Earth's radius in km
|
||||
const dLat = this.toRadians(pos2.latitude - pos1.latitude);
|
||||
const dLon = this.toRadians(pos2.longitude - pos1.longitude);
|
||||
|
||||
const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
|
||||
Math.cos(this.toRadians(pos1.latitude)) * Math.cos(this.toRadians(pos2.latitude)) *
|
||||
Math.sin(dLon / 2) * Math.sin(dLon / 2);
|
||||
|
||||
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||
const distance = R * c;
|
||||
|
||||
return {
|
||||
kilometers: distance,
|
||||
miles: distance * 0.621371,
|
||||
meters: distance * 1000
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Device Motion and Orientation
|
||||
*/
|
||||
motion = {
|
||||
// Start motion detection
|
||||
start: (callback, options = {}) => {
|
||||
if (!this.support.deviceMotion) {
|
||||
throw new Error('Device Motion not supported');
|
||||
}
|
||||
|
||||
const handler = (event) => {
|
||||
const motionData = {
|
||||
acceleration: event.acceleration,
|
||||
accelerationIncludingGravity: event.accelerationIncludingGravity,
|
||||
rotationRate: event.rotationRate,
|
||||
interval: event.interval,
|
||||
timestamp: event.timeStamp,
|
||||
// Enhanced data
|
||||
totalAcceleration: this.calculateTotalAcceleration(event.acceleration),
|
||||
shake: this.detectShake(event.accelerationIncludingGravity),
|
||||
orientation: this.getDeviceOrientation(event)
|
||||
};
|
||||
|
||||
callback(motionData);
|
||||
};
|
||||
|
||||
// Request permission for iOS 13+
|
||||
if (typeof DeviceMotionEvent.requestPermission === 'function') {
|
||||
DeviceMotionEvent.requestPermission().then(response => {
|
||||
if (response === 'granted') {
|
||||
window.addEventListener('devicemotion', handler);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
window.addEventListener('devicemotion', handler);
|
||||
}
|
||||
|
||||
const watcherId = this.generateId('motion');
|
||||
this.activeWatchers.set(watcherId, {
|
||||
type: 'motion',
|
||||
handler,
|
||||
stop: () => {
|
||||
window.removeEventListener('devicemotion', handler);
|
||||
this.activeWatchers.delete(watcherId);
|
||||
}
|
||||
});
|
||||
|
||||
Logger.info('[DeviceManager] Motion detection started');
|
||||
|
||||
return {
|
||||
id: watcherId,
|
||||
stop: () => {
|
||||
window.removeEventListener('devicemotion', handler);
|
||||
this.activeWatchers.delete(watcherId);
|
||||
Logger.info('[DeviceManager] Motion detection stopped');
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
// Start orientation detection
|
||||
startOrientation: (callback, options = {}) => {
|
||||
if (!this.support.deviceOrientation) {
|
||||
throw new Error('Device Orientation not supported');
|
||||
}
|
||||
|
||||
const handler = (event) => {
|
||||
const orientationData = {
|
||||
alpha: event.alpha, // Z axis (0-360)
|
||||
beta: event.beta, // X axis (-180 to 180)
|
||||
gamma: event.gamma, // Y axis (-90 to 90)
|
||||
absolute: event.absolute,
|
||||
timestamp: event.timeStamp,
|
||||
// Enhanced data
|
||||
compass: this.calculateCompass(event.alpha),
|
||||
tilt: this.calculateTilt(event.beta, event.gamma),
|
||||
rotation: this.getRotationState(event)
|
||||
};
|
||||
|
||||
callback(orientationData);
|
||||
};
|
||||
|
||||
// Request permission for iOS 13+
|
||||
if (typeof DeviceOrientationEvent.requestPermission === 'function') {
|
||||
DeviceOrientationEvent.requestPermission().then(response => {
|
||||
if (response === 'granted') {
|
||||
window.addEventListener('deviceorientation', handler);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
window.addEventListener('deviceorientation', handler);
|
||||
}
|
||||
|
||||
const watcherId = this.generateId('orientation');
|
||||
this.activeWatchers.set(watcherId, {
|
||||
type: 'orientation',
|
||||
handler,
|
||||
stop: () => {
|
||||
window.removeEventListener('deviceorientation', handler);
|
||||
this.activeWatchers.delete(watcherId);
|
||||
}
|
||||
});
|
||||
|
||||
Logger.info('[DeviceManager] Orientation detection started');
|
||||
|
||||
return {
|
||||
id: watcherId,
|
||||
stop: () => {
|
||||
window.removeEventListener('deviceorientation', handler);
|
||||
this.activeWatchers.delete(watcherId);
|
||||
Logger.info('[DeviceManager] Orientation detection stopped');
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Vibration API
|
||||
*/
|
||||
vibration = {
|
||||
// Simple vibration
|
||||
vibrate: (pattern) => {
|
||||
if (!this.support.vibration) {
|
||||
Logger.warn('[DeviceManager] Vibration not supported');
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
navigator.vibrate(pattern);
|
||||
Logger.info('[DeviceManager] Vibration triggered:', pattern);
|
||||
return true;
|
||||
} catch (error) {
|
||||
Logger.error('[DeviceManager] Vibration failed:', error);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
// Predefined patterns
|
||||
patterns: {
|
||||
short: 200,
|
||||
long: 600,
|
||||
double: [200, 100, 200],
|
||||
triple: [200, 100, 200, 100, 200],
|
||||
sos: [100, 30, 100, 30, 100, 200, 200, 30, 200, 30, 200, 200, 100, 30, 100, 30, 100],
|
||||
heartbeat: [100, 30, 100, 130, 40, 30, 40, 30, 100],
|
||||
notification: [200, 100, 200],
|
||||
success: [100],
|
||||
error: [300, 100, 300],
|
||||
warning: [200, 100, 200, 100, 200]
|
||||
},
|
||||
|
||||
// Stop vibration
|
||||
stop: () => {
|
||||
if (this.support.vibration) {
|
||||
navigator.vibrate(0);
|
||||
Logger.info('[DeviceManager] Vibration stopped');
|
||||
}
|
||||
},
|
||||
|
||||
// Haptic feedback helpers
|
||||
success: () => this.vibration.vibrate(this.vibration.patterns.success),
|
||||
error: () => this.vibration.vibrate(this.vibration.patterns.error),
|
||||
warning: () => this.vibration.vibrate(this.vibration.patterns.warning),
|
||||
notification: () => this.vibration.vibrate(this.vibration.patterns.notification)
|
||||
};
|
||||
|
||||
/**
|
||||
* Battery API
|
||||
*/
|
||||
battery = {
|
||||
// Get battery status
|
||||
get: async () => {
|
||||
if (!this.support.battery) {
|
||||
Logger.warn('[DeviceManager] Battery API not supported');
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const battery = await navigator.getBattery();
|
||||
|
||||
const batteryInfo = {
|
||||
level: Math.round(battery.level * 100),
|
||||
charging: battery.charging,
|
||||
chargingTime: battery.chargingTime,
|
||||
dischargingTime: battery.dischargingTime,
|
||||
// Enhanced data
|
||||
status: this.getBatteryStatus(battery),
|
||||
timeRemaining: this.formatBatteryTime(battery)
|
||||
};
|
||||
|
||||
Logger.info('[DeviceManager] Battery status:', batteryInfo);
|
||||
return batteryInfo;
|
||||
} catch (error) {
|
||||
Logger.error('[DeviceManager] Battery status failed:', error);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
// Watch battery changes
|
||||
watch: async (callback) => {
|
||||
if (!this.support.battery) {
|
||||
throw new Error('Battery API not supported');
|
||||
}
|
||||
|
||||
try {
|
||||
const battery = await navigator.getBattery();
|
||||
|
||||
const events = ['chargingchange', 'levelchange', 'chargingtimechange', 'dischargingtimechange'];
|
||||
const handlers = [];
|
||||
|
||||
events.forEach(eventType => {
|
||||
const handler = () => {
|
||||
const batteryInfo = {
|
||||
level: Math.round(battery.level * 100),
|
||||
charging: battery.charging,
|
||||
chargingTime: battery.chargingTime,
|
||||
dischargingTime: battery.dischargingTime,
|
||||
status: this.getBatteryStatus(battery),
|
||||
timeRemaining: this.formatBatteryTime(battery),
|
||||
event: eventType
|
||||
};
|
||||
callback(batteryInfo);
|
||||
};
|
||||
|
||||
battery.addEventListener(eventType, handler);
|
||||
handlers.push({ event: eventType, handler });
|
||||
});
|
||||
|
||||
const watcherId = this.generateId('battery');
|
||||
this.activeWatchers.set(watcherId, {
|
||||
type: 'battery',
|
||||
battery,
|
||||
handlers,
|
||||
stop: () => {
|
||||
handlers.forEach(({ event, handler }) => {
|
||||
battery.removeEventListener(event, handler);
|
||||
});
|
||||
this.activeWatchers.delete(watcherId);
|
||||
}
|
||||
});
|
||||
|
||||
Logger.info('[DeviceManager] Battery watch started');
|
||||
|
||||
return {
|
||||
id: watcherId,
|
||||
stop: () => {
|
||||
handlers.forEach(({ event, handler }) => {
|
||||
battery.removeEventListener(event, handler);
|
||||
});
|
||||
this.activeWatchers.delete(watcherId);
|
||||
Logger.info('[DeviceManager] Battery watch stopped');
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
Logger.error('[DeviceManager] Battery watch failed:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Network Information API
|
||||
*/
|
||||
network = {
|
||||
// Get connection info
|
||||
get: () => {
|
||||
if (!this.support.networkInfo) {
|
||||
Logger.warn('[DeviceManager] Network Information not supported');
|
||||
return null;
|
||||
}
|
||||
|
||||
const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
|
||||
|
||||
return {
|
||||
effectiveType: connection.effectiveType,
|
||||
downlink: connection.downlink,
|
||||
rtt: connection.rtt,
|
||||
saveData: connection.saveData,
|
||||
// Enhanced data
|
||||
speed: this.getConnectionSpeed(connection),
|
||||
quality: this.getConnectionQuality(connection),
|
||||
recommendation: this.getNetworkRecommendation(connection)
|
||||
};
|
||||
},
|
||||
|
||||
// Watch network changes
|
||||
watch: (callback) => {
|
||||
if (!this.support.networkInfo) {
|
||||
throw new Error('Network Information not supported');
|
||||
}
|
||||
|
||||
const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
|
||||
|
||||
const handler = () => {
|
||||
const networkInfo = {
|
||||
effectiveType: connection.effectiveType,
|
||||
downlink: connection.downlink,
|
||||
rtt: connection.rtt,
|
||||
saveData: connection.saveData,
|
||||
speed: this.getConnectionSpeed(connection),
|
||||
quality: this.getConnectionQuality(connection),
|
||||
recommendation: this.getNetworkRecommendation(connection)
|
||||
};
|
||||
|
||||
callback(networkInfo);
|
||||
};
|
||||
|
||||
connection.addEventListener('change', handler);
|
||||
|
||||
const watcherId = this.generateId('network');
|
||||
this.activeWatchers.set(watcherId, {
|
||||
type: 'network',
|
||||
connection,
|
||||
handler,
|
||||
stop: () => {
|
||||
connection.removeEventListener('change', handler);
|
||||
this.activeWatchers.delete(watcherId);
|
||||
}
|
||||
});
|
||||
|
||||
Logger.info('[DeviceManager] Network watch started');
|
||||
|
||||
return {
|
||||
id: watcherId,
|
||||
stop: () => {
|
||||
connection.removeEventListener('change', handler);
|
||||
this.activeWatchers.delete(watcherId);
|
||||
Logger.info('[DeviceManager] Network watch stopped');
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Wake Lock API
|
||||
*/
|
||||
wakeLock = {
|
||||
// Request wake lock
|
||||
request: async (type = 'screen') => {
|
||||
if (!this.support.wakeLock) {
|
||||
Logger.warn('[DeviceManager] Wake Lock not supported');
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const wakeLock = await navigator.wakeLock.request(type);
|
||||
Logger.info(`[DeviceManager] Wake lock acquired: ${type}`);
|
||||
|
||||
return {
|
||||
type: wakeLock.type,
|
||||
release: () => {
|
||||
wakeLock.release();
|
||||
Logger.info(`[DeviceManager] Wake lock released: ${type}`);
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
Logger.error('[DeviceManager] Wake lock failed:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Helper methods
|
||||
|
||||
initializeSensors() {
|
||||
// Initialize any sensors that need setup
|
||||
if (this.support.deviceMotion || this.support.deviceOrientation) {
|
||||
// Store baseline sensor data for comparison
|
||||
this.sensorData.set('motionBaseline', { x: 0, y: 0, z: 0 });
|
||||
this.sensorData.set('shakeThreshold', this.config.shakeThreshold || 15);
|
||||
}
|
||||
}
|
||||
|
||||
enhanceLocationData(position) {
|
||||
return {
|
||||
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,
|
||||
// Enhanced data
|
||||
coordinates: `${position.coords.latitude},${position.coords.longitude}`,
|
||||
accuracyLevel: this.getAccuracyLevel(position.coords.accuracy),
|
||||
mapUrl: `https://maps.google.com/?q=${position.coords.latitude},${position.coords.longitude}`
|
||||
};
|
||||
}
|
||||
|
||||
getAccuracyLevel(accuracy) {
|
||||
if (accuracy <= 5) return 'excellent';
|
||||
if (accuracy <= 10) return 'good';
|
||||
if (accuracy <= 50) return 'fair';
|
||||
return 'poor';
|
||||
}
|
||||
|
||||
calculateTotalAcceleration(acceleration) {
|
||||
if (!acceleration) return 0;
|
||||
const x = acceleration.x || 0;
|
||||
const y = acceleration.y || 0;
|
||||
const z = acceleration.z || 0;
|
||||
return Math.sqrt(x * x + y * y + z * z);
|
||||
}
|
||||
|
||||
detectShake(acceleration) {
|
||||
if (!acceleration) return false;
|
||||
|
||||
const threshold = this.sensorData.get('shakeThreshold');
|
||||
const x = Math.abs(acceleration.x || 0);
|
||||
const y = Math.abs(acceleration.y || 0);
|
||||
const z = Math.abs(acceleration.z || 0);
|
||||
|
||||
return (x > threshold || y > threshold || z > threshold);
|
||||
}
|
||||
|
||||
getDeviceOrientation(event) {
|
||||
const acceleration = event.accelerationIncludingGravity;
|
||||
if (!acceleration) return 'unknown';
|
||||
|
||||
const x = acceleration.x || 0;
|
||||
const y = acceleration.y || 0;
|
||||
const z = acceleration.z || 0;
|
||||
|
||||
if (Math.abs(x) > Math.abs(y) && Math.abs(x) > Math.abs(z)) {
|
||||
return x > 0 ? 'landscape-right' : 'landscape-left';
|
||||
} else if (Math.abs(y) > Math.abs(z)) {
|
||||
return y > 0 ? 'portrait-upside-down' : 'portrait';
|
||||
} else {
|
||||
return z > 0 ? 'face-down' : 'face-up';
|
||||
}
|
||||
}
|
||||
|
||||
calculateCompass(alpha) {
|
||||
if (alpha === null) return null;
|
||||
|
||||
const directions = ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW'];
|
||||
const index = Math.round(alpha / 45) % 8;
|
||||
|
||||
return {
|
||||
degrees: Math.round(alpha),
|
||||
direction: directions[index],
|
||||
cardinal: this.getCardinalDirection(alpha)
|
||||
};
|
||||
}
|
||||
|
||||
getCardinalDirection(alpha) {
|
||||
if (alpha >= 337.5 || alpha < 22.5) return 'North';
|
||||
if (alpha >= 22.5 && alpha < 67.5) return 'Northeast';
|
||||
if (alpha >= 67.5 && alpha < 112.5) return 'East';
|
||||
if (alpha >= 112.5 && alpha < 157.5) return 'Southeast';
|
||||
if (alpha >= 157.5 && alpha < 202.5) return 'South';
|
||||
if (alpha >= 202.5 && alpha < 247.5) return 'Southwest';
|
||||
if (alpha >= 247.5 && alpha < 292.5) return 'West';
|
||||
if (alpha >= 292.5 && alpha < 337.5) return 'Northwest';
|
||||
return 'Unknown';
|
||||
}
|
||||
|
||||
calculateTilt(beta, gamma) {
|
||||
return {
|
||||
x: Math.round(beta || 0),
|
||||
y: Math.round(gamma || 0),
|
||||
magnitude: Math.round(Math.sqrt((beta || 0) ** 2 + (gamma || 0) ** 2))
|
||||
};
|
||||
}
|
||||
|
||||
getRotationState(event) {
|
||||
const { alpha, beta, gamma } = event;
|
||||
|
||||
// Determine if device is being rotated significantly
|
||||
const rotationThreshold = 10;
|
||||
const isRotating = Math.abs(beta) > rotationThreshold || Math.abs(gamma) > rotationThreshold;
|
||||
|
||||
return {
|
||||
isRotating,
|
||||
intensity: isRotating ? Math.max(Math.abs(beta), Math.abs(gamma)) : 0
|
||||
};
|
||||
}
|
||||
|
||||
getBatteryStatus(battery) {
|
||||
const level = battery.level * 100;
|
||||
|
||||
if (battery.charging) return 'charging';
|
||||
if (level <= 10) return 'critical';
|
||||
if (level <= 20) return 'low';
|
||||
if (level <= 50) return 'medium';
|
||||
return 'high';
|
||||
}
|
||||
|
||||
formatBatteryTime(battery) {
|
||||
const time = battery.charging ? battery.chargingTime : battery.dischargingTime;
|
||||
|
||||
if (time === Infinity || isNaN(time)) return 'Unknown';
|
||||
|
||||
const hours = Math.floor(time / 3600);
|
||||
const minutes = Math.floor((time % 3600) / 60);
|
||||
|
||||
return `${hours}h ${minutes}m`;
|
||||
}
|
||||
|
||||
getConnectionSpeed(connection) {
|
||||
const downlink = connection.downlink;
|
||||
|
||||
if (downlink >= 10) return 'fast';
|
||||
if (downlink >= 1.5) return 'good';
|
||||
if (downlink >= 0.5) return 'slow';
|
||||
return 'very-slow';
|
||||
}
|
||||
|
||||
getConnectionQuality(connection) {
|
||||
const effectiveType = connection.effectiveType;
|
||||
|
||||
switch (effectiveType) {
|
||||
case '4g': return 'excellent';
|
||||
case '3g': return 'good';
|
||||
case '2g': return 'poor';
|
||||
case 'slow-2g': return 'very-poor';
|
||||
default: return 'unknown';
|
||||
}
|
||||
}
|
||||
|
||||
getNetworkRecommendation(connection) {
|
||||
const quality = this.getConnectionQuality(connection);
|
||||
|
||||
switch (quality) {
|
||||
case 'excellent':
|
||||
return 'Full quality content recommended';
|
||||
case 'good':
|
||||
return 'Moderate quality content recommended';
|
||||
case 'poor':
|
||||
return 'Light content only, avoid large files';
|
||||
case 'very-poor':
|
||||
return 'Text-only content recommended';
|
||||
default:
|
||||
return 'Monitor connection quality';
|
||||
}
|
||||
}
|
||||
|
||||
toRadians(degrees) {
|
||||
return degrees * (Math.PI / 180);
|
||||
}
|
||||
|
||||
generateId(prefix = 'device') {
|
||||
return `${prefix}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop all active watchers
|
||||
*/
|
||||
stopAllWatchers() {
|
||||
this.activeWatchers.forEach(watcher => {
|
||||
watcher.stop();
|
||||
});
|
||||
this.activeWatchers.clear();
|
||||
Logger.info('[DeviceManager] All watchers stopped');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get device capabilities summary
|
||||
*/
|
||||
getCapabilities() {
|
||||
return {
|
||||
support: this.support,
|
||||
activeWatchers: this.activeWatchers.size,
|
||||
watcherTypes: Array.from(this.activeWatchers.values()).map(w => w.type)
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user