365 lines
13 KiB
JavaScript
365 lines
13 KiB
JavaScript
/**
|
|
* Enhanced Android Location Fix - Separate Module
|
|
* File: static/js/android_location_handler.js
|
|
*
|
|
* This module addresses Android-specific geolocation issues:
|
|
* 1. Android Chrome timeout handling
|
|
* 2. Progressive fallback strategy
|
|
* 3. Enhanced permission detection
|
|
* 4. Network location fallback
|
|
* 5. Multiple retry attempts with different configurations
|
|
*/
|
|
|
|
// Android-specific geolocation configuration
|
|
const ANDROID_LOCATION_CONFIG = {
|
|
// Primary attempt - High accuracy with reasonable timeout
|
|
highAccuracy: {
|
|
enableHighAccuracy: true,
|
|
timeout: 15000, // Increased from 10000 for Android
|
|
maximumAge: 60000 // Reduced cache time for fresh location
|
|
},
|
|
|
|
// Fallback attempt - Network-based location
|
|
networkBased: {
|
|
enableHighAccuracy: false,
|
|
timeout: 20000, // Longer timeout for network-based
|
|
maximumAge: 300000
|
|
},
|
|
|
|
// Final attempt - Any available location
|
|
anyLocation: {
|
|
enableHighAccuracy: false,
|
|
timeout: 30000, // Maximum patience for Android
|
|
maximumAge: 600000
|
|
}
|
|
};
|
|
|
|
// Enhanced location request with Android-specific handling
|
|
function requestAndroidEnhancedLocation() {
|
|
console.log("📱 Starting Android-enhanced location request...");
|
|
|
|
if (typeof locationRequestActive !== 'undefined' && locationRequestActive) {
|
|
console.log("📍 Location request already active, skipping Android enhancement");
|
|
return;
|
|
}
|
|
|
|
if (!navigator.geolocation) {
|
|
console.log("❌ Geolocation not supported");
|
|
if (typeof userLocation !== 'undefined') {
|
|
userLocation.source = "manual";
|
|
}
|
|
if (typeof currentUserLocation !== 'undefined') {
|
|
currentUserLocation.source = "manual";
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Set active flag
|
|
if (typeof locationRequestActive !== 'undefined') {
|
|
locationRequestActive = true;
|
|
}
|
|
if (typeof locationCaptureActive !== 'undefined') {
|
|
locationCaptureActive = true;
|
|
}
|
|
|
|
console.log("📱 Attempting Android-optimized location sequence...");
|
|
|
|
// Start progressive location attempts
|
|
attemptAndroidLocationSequence();
|
|
}
|
|
|
|
function attemptAndroidLocationSequence() {
|
|
console.log("🔄 Android Location Sequence - Attempt 1: High Accuracy GPS");
|
|
|
|
// Attempt 1: High accuracy with Android-optimized timeout
|
|
navigator.geolocation.getCurrentPosition(
|
|
(position) => {
|
|
console.log("✅ Android high-accuracy location success!");
|
|
handleAndroidLocationSuccess(position, "gps_high_accuracy");
|
|
},
|
|
(error) => {
|
|
console.log(`❌ High accuracy failed (${error.message}), trying network-based...`);
|
|
attemptNetworkBasedLocation();
|
|
},
|
|
ANDROID_LOCATION_CONFIG.highAccuracy
|
|
);
|
|
}
|
|
|
|
function attemptNetworkBasedLocation() {
|
|
console.log("🔄 Android Location Sequence - Attempt 2: Network-based");
|
|
|
|
navigator.geolocation.getCurrentPosition(
|
|
(position) => {
|
|
console.log("✅ Android network-based location success!");
|
|
handleAndroidLocationSuccess(position, "network");
|
|
},
|
|
(error) => {
|
|
console.log(`❌ Network-based failed (${error.message}), trying any location...`);
|
|
attemptAnyAvailableLocation();
|
|
},
|
|
ANDROID_LOCATION_CONFIG.networkBased
|
|
);
|
|
}
|
|
|
|
function attemptAnyAvailableLocation() {
|
|
console.log("🔄 Android Location Sequence - Attempt 3: Any available");
|
|
|
|
navigator.geolocation.getCurrentPosition(
|
|
(position) => {
|
|
console.log("✅ Android any-location success!");
|
|
handleAndroidLocationSuccess(position, "any");
|
|
},
|
|
(error) => {
|
|
console.log(`❌ All location attempts failed (${error.message}), trying watchPosition...`);
|
|
attemptWatchPosition();
|
|
},
|
|
ANDROID_LOCATION_CONFIG.anyLocation
|
|
);
|
|
}
|
|
|
|
function attemptWatchPosition() {
|
|
console.log("🔄 Android Location Sequence - Attempt 4: Watch Position (single shot)");
|
|
|
|
let watchId = null;
|
|
let watchTimeout = null;
|
|
|
|
// Set timeout for watch attempt
|
|
watchTimeout = setTimeout(() => {
|
|
if (watchId !== null) {
|
|
navigator.geolocation.clearWatch(watchId);
|
|
}
|
|
console.log("❌ Watch position timed out, location failed");
|
|
handleAndroidLocationError("All location methods failed");
|
|
}, 25000);
|
|
|
|
// Use watchPosition for more persistent location tracking
|
|
watchId = navigator.geolocation.watchPosition(
|
|
(position) => {
|
|
console.log("✅ Android watch position success!");
|
|
|
|
// Clear watch and timeout
|
|
navigator.geolocation.clearWatch(watchId);
|
|
clearTimeout(watchTimeout);
|
|
|
|
handleAndroidLocationSuccess(position, "watch");
|
|
},
|
|
(error) => {
|
|
console.log(`❌ Watch position error: ${error.message}`);
|
|
// Don't clear watch immediately, let timeout handle it
|
|
},
|
|
{
|
|
enableHighAccuracy: false,
|
|
timeout: 20000,
|
|
maximumAge: 0 // Force fresh location
|
|
}
|
|
);
|
|
}
|
|
|
|
function handleAndroidLocationSuccess(position, source) {
|
|
console.log(`✅ Android location obtained successfully via ${source}`);
|
|
console.log(`📍 Coordinates: ${position.coords.latitude}, ${position.coords.longitude}`);
|
|
console.log(`📊 Accuracy: ${position.coords.accuracy}m`);
|
|
|
|
// Create location object
|
|
const locationData = {
|
|
latitude: position.coords.latitude,
|
|
longitude: position.coords.longitude,
|
|
accuracy: position.coords.accuracy,
|
|
altitude: position.coords.altitude,
|
|
timestamp: new Date(),
|
|
source: source,
|
|
address: null,
|
|
};
|
|
|
|
// Update global location variables based on what's available
|
|
if (typeof userLocation !== 'undefined') {
|
|
Object.assign(userLocation, locationData);
|
|
console.log("📍 Updated userLocation with Android data");
|
|
|
|
// Trigger reverse geocoding if function exists
|
|
if (typeof reverseGeocode === 'function') {
|
|
reverseGeocode(userLocation.latitude, userLocation.longitude);
|
|
}
|
|
}
|
|
|
|
if (typeof currentUserLocation !== 'undefined') {
|
|
Object.assign(currentUserLocation, locationData);
|
|
console.log("📍 Updated currentUserLocation with Android data");
|
|
|
|
// Trigger enhanced reverse geocoding if function exists
|
|
if (typeof reverseGeocodeEnhanced === 'function') {
|
|
reverseGeocodeEnhanced(currentUserLocation.latitude, currentUserLocation.longitude);
|
|
}
|
|
}
|
|
|
|
// Clear active flags
|
|
if (typeof locationRequestActive !== 'undefined') {
|
|
locationRequestActive = false;
|
|
}
|
|
if (typeof locationCaptureActive !== 'undefined') {
|
|
locationCaptureActive = false;
|
|
}
|
|
|
|
// Log success for monitoring
|
|
logLocationAction('android_location_success', {
|
|
source: source,
|
|
accuracy: position.coords.accuracy,
|
|
coordinates: `${position.coords.latitude},${position.coords.longitude}`
|
|
});
|
|
}
|
|
|
|
function handleAndroidLocationError(errorMessage) {
|
|
console.log(`❌ Android location error: ${errorMessage}`);
|
|
|
|
// Update global location variables
|
|
if (typeof userLocation !== 'undefined') {
|
|
userLocation.source = "manual";
|
|
}
|
|
if (typeof currentUserLocation !== 'undefined') {
|
|
currentUserLocation.source = "manual";
|
|
}
|
|
|
|
// Clear active flags
|
|
if (typeof locationRequestActive !== 'undefined') {
|
|
locationRequestActive = false;
|
|
}
|
|
if (typeof locationCaptureActive !== 'undefined') {
|
|
locationCaptureActive = false;
|
|
}
|
|
|
|
// Log error for monitoring
|
|
logLocationAction('android_location_error', {
|
|
error: errorMessage,
|
|
userAgent: navigator.userAgent
|
|
});
|
|
}
|
|
|
|
// Enhanced permission checking for Android
|
|
function checkAndroidLocationPermissions() {
|
|
console.log("📱 Checking Android location permissions...");
|
|
|
|
// Check if permissions API is available (newer Android browsers)
|
|
if ('permissions' in navigator) {
|
|
navigator.permissions.query({name: 'geolocation'}).then(function(result) {
|
|
console.log(`📱 Geolocation permission: ${result.state}`);
|
|
|
|
if (result.state === 'granted') {
|
|
console.log("✅ Android location permission granted");
|
|
requestAndroidEnhancedLocation();
|
|
} else if (result.state === 'prompt') {
|
|
console.log("⚠️ Android location permission will be prompted");
|
|
requestAndroidEnhancedLocation();
|
|
} else {
|
|
console.log("❌ Android location permission denied");
|
|
handleAndroidLocationError("Permission denied");
|
|
}
|
|
}).catch(function(error) {
|
|
console.log("⚠️ Could not check permissions, proceeding with location request");
|
|
requestAndroidEnhancedLocation();
|
|
});
|
|
} else {
|
|
// Fallback for older Android browsers
|
|
console.log("📱 Permissions API not available, proceeding with location request");
|
|
requestAndroidEnhancedLocation();
|
|
}
|
|
}
|
|
|
|
// Detect if device is Android
|
|
function isAndroidDevice() {
|
|
const userAgent = navigator.userAgent.toLowerCase();
|
|
return userAgent.includes('android');
|
|
}
|
|
|
|
// Detect if browser is Chrome on Android
|
|
function isAndroidChrome() {
|
|
const userAgent = navigator.userAgent.toLowerCase();
|
|
return userAgent.includes('android') && userAgent.includes('chrome') && !userAgent.includes('edg');
|
|
}
|
|
|
|
// Enhanced location initialization for Android
|
|
function initializeAndroidLocation() {
|
|
console.log("📱 Initializing Android-enhanced location services...");
|
|
|
|
if (isAndroidDevice()) {
|
|
console.log("📱 Android device detected, using enhanced location handling");
|
|
|
|
// Use Android-specific permission checking
|
|
checkAndroidLocationPermissions();
|
|
} else {
|
|
console.log("📱 Non-Android device, using standard location request");
|
|
|
|
// Fall back to standard location request
|
|
if (typeof requestLocationData === 'function') {
|
|
requestLocationData();
|
|
} else if (typeof requestEnhancedLocation === 'function') {
|
|
requestEnhancedLocation();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Location action logging function
|
|
function logLocationAction(action, data) {
|
|
try {
|
|
console.log(`📊 Location Action Log: ${action}`, data);
|
|
|
|
// Send to server for monitoring if endpoint exists
|
|
if (typeof fetch !== 'undefined') {
|
|
fetch('/api/log-location-action', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
action: action,
|
|
data: data,
|
|
timestamp: new Date().toISOString(),
|
|
userAgent: navigator.userAgent
|
|
})
|
|
}).catch(error => {
|
|
console.log('📊 Could not send location log to server:', error);
|
|
});
|
|
}
|
|
} catch (error) {
|
|
console.log('📊 Location logging error:', error);
|
|
}
|
|
}
|
|
|
|
// Override standard location initialization if this is an Android device
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Small delay to ensure other scripts are loaded
|
|
setTimeout(() => {
|
|
if (isAndroidDevice()) {
|
|
console.log("📱 Android detected - overriding standard location initialization");
|
|
|
|
// Replace standard initialization with Android-enhanced version
|
|
if (typeof initializeLocation === 'function') {
|
|
const originalInitializeLocation = initializeLocation;
|
|
window.initializeLocation = function() {
|
|
console.log("📱 Using Android-enhanced location initialization");
|
|
initializeAndroidLocation();
|
|
};
|
|
}
|
|
|
|
// Also handle the enhanced location capture
|
|
if (typeof requestEnhancedLocation === 'function') {
|
|
const originalRequestEnhanced = requestEnhancedLocation;
|
|
window.requestEnhancedLocation = function() {
|
|
console.log("📱 Using Android-enhanced location request");
|
|
initializeAndroidLocation();
|
|
};
|
|
}
|
|
}
|
|
}, 100);
|
|
});
|
|
|
|
// Export functions for external use
|
|
if (typeof window !== 'undefined') {
|
|
window.AndroidLocationHandler = {
|
|
requestAndroidEnhancedLocation,
|
|
checkAndroidLocationPermissions,
|
|
isAndroidDevice,
|
|
isAndroidChrome,
|
|
initializeAndroidLocation,
|
|
logLocationAction
|
|
};
|
|
} |