Reputation: 25595
The following code
navigator.geolocation.getCurrentPosition(getGeo_Success, getGeo_Fail, {
enableHighAccuracy : true,
maximumAge : Infinity,
timeout : 15000
});
Retrieves the current GPS position - BUT - there must be a valid GPS signal (and ofcourse, the device's GPS feature should be turned on).
If we look at other applications (say, Maps on Android devices) - it knows how to retrieve the last known position - even if I didn't use an application that updates the geolocation before opening Maps - it shows my position on the map, even if I'm inside a building with no GPS signal at all.
Just to clarify: I'm not interested in the last geolocation my application retrieved, as on the next time I`ll start it, that geolocation will probably be irrelevant.
Question is: How can we achieve this with HTML5/Phonegap? seems like navigator.geolocation
only knows to retrieve the current position, even though, maximumAge
is set to Infinity
(which means, the age of the last cached position is irrelevant, so any hit is okay (or, should be!))
Upvotes: 2
Views: 3711
Reputation: 25595
ANDROID SOLUTION (iPhone solution follows):
This is neat:
I've made use of Android's native LocationManager
, which provides a getLastKnownLocation
function - the name says it all
Here's the relevant code
1) Add the following java class to your application
package your.package.app.app;
import org.apache.cordova.DroidGap;
import android.content.Context;
import android.location.*;
import android.os.Bundle;
import android.webkit.WebView;
public class GetNativeLocation implements LocationListener {
private WebView mAppView;
private DroidGap mGap;
private Location mostRecentLocation;
public GetNativeLocation(DroidGap gap, WebView view) {
mAppView = view;
mGap = gap;
}
public void onLocationChanged(Location location) {
// TODO Auto-generated method stub
getLocation();
}
public void getLocation() {
LocationManager lm =
(LocationManager)mGap.
getSystemService(Context.LOCATION_SERVICE);
Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_FINE);
String provider = lm.getBestProvider(criteria, true);
lm.requestLocationUpdates(provider, 1000, 500, this);
mostRecentLocation = lm
.getLastKnownLocation(LocationManager.GPS_PROVIDER);
}
public void doInit(){
getLocation();
}
public double getLat(){ return mostRecentLocation.getLatitude();}
public double getLong() { return mostRecentLocation.getLongitude(); }
public void onProviderDisabled(String arg0) {
// TODO Auto-generated method stub
}
public void onProviderEnabled(String provider) {
// TODO Auto-generated method stub
}
public void onStatusChanged(String provider, int status, Bundle extras) {
// TODO Auto-generated method stub
}
}
2) Make sure your main class looks as follows:
public class App extends DroidGap {
// Hold a private member of the class that calls LocationManager
private GetNativeLocation gLocation;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// This line is important, as System Services are not available
// prior to initialization
super.init();
gLocation = new GetNativeLocation(this, appView);
// Add the interface so we can invoke the java functions from our .js
appView.addJavascriptInterface(gLocation, "NativeLocation");
try {
super.loadUrl("file:///android_asset/www/index.html");
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
}
@Override
protected void onStop() {
// TODO Auto-generated method stub
super.onStop();
}
}
3) on your JS code, just call the native java using:
window.NativeLocation.doInit();
alert(window.NativeLocation.getLat());
alert(window.NativeLocation.getLong());
Thats all folks! :-)
EDIT: iPhone solution:
I wrote a tiny Phonegap plugin that creates an interface to a custom class which is making use of iOS's native CLLocationManager
1) Phonegap plugin (JS)
var NativeLocation = {
doInit: function(types, success, fail) {
return Cordova.exec(success, fail, "NativeLocation", "doInit", types);
},
getLongitude: function(types, success, fail){
return Cordova.exec(success, fail, "NativeLocation", "getLongitude", types);
},
getLatitude: function(types, success, fail){
return Cordova.exec(success, fail, "NativeLocation", "getLatitude", types);
}
}
2) The objective-c class that enables us to invoke 'CCLocationManager's functions *NativeLocation.h*
#import <Foundation/Foundation.h>
#import <Cordova/CDVPlugin.h>
#import <CoreLocation/CoreLocation.h>
@protocol NativeLocationDelegate
@required
- (void)locationUpdate:(CLLocation *)location;
- (void)locationError:(NSError *)error;
@end
@interface NativeLocation : CDVPlugin <CLLocationManagerDelegate> {
id delegate;
NSString* callbackID;
CLLocationManager *lm;
Boolean bEnabled;
double nLat;
double nLon;
}
@property (nonatomic, copy) NSString* callbackID;
@property (nonatomic, retain) CLLocationManager *lm;
@property (nonatomic, readonly) Boolean bEnabled;
@property (nonatomic, assign) id delegate;
- (void) doInit:(NSMutableArray*)arguments
withDict:(NSMutableDictionary*)options;
- (void) getLatitude:(NSMutableArray*)arguments
withDict:(NSMutableDictionary *)options;
- (void) getLongitude:(NSMutableArray*)arguments
withDict:(NSMutableDictionary *)options;
@end
NativeLocation.m
#import "NativeLocation.h"
@implementation NativeLocation
@synthesize callbackID;
@synthesize lm;
@synthesize bEnabled;
@synthesize delegate;
- (void)doInit:(NSMutableArray *)arguments
withDict:(NSMutableDictionary *)options{
if (self != nil){
self.lm = [[[CLLocationManager alloc] init] autorelease];
self.lm.delegate = self;
if (self.lm.locationServicesEnabled == NO)
bEnabled = FALSE;
else bEnabled = TRUE;
}
nLat = 0.0;
nLon = 0.0;
if (bEnabled == TRUE)
[self.lm startUpdatingLocation];
CDVPluginResult* pluginResult = [CDVPluginResult
resultWithStatus:CDVCommandStatus_OK
messageAsString[@"OK"
stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
if (bEnabled == TRUE){
[self writeJavascript: [pluginResult
toSuccessCallbackString:self.callbackID]];
} else {
[self writeJavascript: [pluginResult
toErrorCallbackString:self.callbackID]];
}
}
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation {
if ([self.delegate conformsToProtocol:@protocol(NativeLocationDelegate)])
[self.delegate locationUpdate:newLocation ];
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
if ([self.delegate conformsToProtocol:@protocol(NativeLocationDelegate)])
[self.delegate locationError:error];
}
- (void)dealloc {
[self.lm release];
[super dealloc];
}
- (void)locationUpdate:(CLLocation *)location {
CLLocationCoordinate2D cCoord = [location coordinate];
nLat = cCoord.latitude;
nLon = cCoord.longitude;
}
- (void)getLatitude:(NSMutableArray *)arguments
withDict:(NSMutableDictionary *)options{
self.callbackID = [arguments pop];
nLat = lm.location.coordinate.latitude;
nLon = lm.location.coordinate.longitude;
CDVPluginResult* pluginResult = [CDVPluginResult
resultWithStatus:CDVCommandStatus_OK
messageAsDouble:nLat];
[self writeJavascript: [pluginResult toSuccessCallbackString:self.callbackID]];
}
- (void)getLongitude:(NSMutableArray *)arguments
withDict:(NSMutableDictionary *)options{
self.callbackID = [arguments pop];
nLat = lm.location.coordinate.latitude;
nLon = lm.location.coordinate.longitude;
CDVPluginResult* pluginResult = [CDVPluginResult
resultWithStatus:CDVCommandStatus_OK messageAsDouble:nLon];
[self writeJavascript: [pluginResult toSuccessCallbackString:self.callbackID]];
}
@end
3) And finally, invoking everything from the main .js
function getLongitudeSuccess(result){
gLongitude = result;
}
function getLatitudeSuccess(result){
gLatitude = result;
}
function runGPSTimer(){
var sTmp = "gps";
theTime = setTimeout('runGPSTimer()', 1000);
NativeLocation.getLongitude(
["getLongitude"],
getLongitudeSuccess,
function(error){ alert("error: " + error); }
);
NativeLocation.getLatitude(
["getLatitude"],
getLatitudeSuccess,
function(error){ alert("error: " + error); }
);
Upvotes: 2
Reputation: 456
In my PhoneGap/Sencha Touch 2 Application i created the function
function getLastKnownLocation(){
if(typeof localStorage.lastKnownPosition == "undefined"){
localStorage.lastKnownPosition = JSON.stringify(null);
}
navigator.geolocation.getCurrentPosition(
function(position){
localStorage.lastKnownPosition = JSON.stringify(position);
}
);
return JSON.parse(localStorage.lastKnownPosition);
}
So every time i call the getLastKnownLocation() i get a result immediately. It's a good workaround for my purposes.
Upvotes: 1
Reputation: 25270
i suggest in your getGeo_Success
function call setLocation([the_location])
to store it on localStorage.
you can assume that each device that uses phoneGap already implements localStorage.
this way, you always have valid location there. and then just pop it whenever you need.
function getLocation() {
return JSON.parse(localStorage.getItem('location'));
}
function setLocation(location) {
localStorage.setItem('vibesList', JSON.stringify(vibes));
}
edit: if you want to retrive the latest place the device knows (not the app), you have to do it via background proccess that pulling the current data from the device. this is problematic approach because:
Upvotes: 0