blob: 3bd24943d603fea02fefe31c2c3dc63aa2886fc3 [file] [log] [blame]
/*
* Copyright 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.geolocation.client;
import com.google.gwt.core.client.Callback;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.impl.Disposable;
import com.google.gwt.core.client.impl.Impl;
import com.google.gwt.dom.client.PartialSupport;
/**
* Implements the HTML5 Geolocation interface.
*
* <p>
* You can obtain a user's position by first calling
* <code>Geolocation.getIfSupported()</code>
* </p>
*
* <p>
* Once you have a <code>Geolocation</code>, you can request the user's current
* position by calling {@link #getCurrentPosition(Callback)} or
* {@link #watchPosition(Callback)}.
* </p>
*
* <p>
* The first time an application requests the user's position, the browser will
* prompt the user for permission. If the user grants permission, the browser
* will locate the user and report it back to your application. If the user
* declines permission, the callback's {@link Callback#onFailure(Object)} method
* will be called with a {@link PositionError} with its code set to
* {@link PositionError#PERMISSION_DENIED}.
* </p>
*
* <p>
* <span style="color:red;">Experimental API: This API is still under
* development and is subject to change.</span>
*
* <p>
* This may not be supported on all browsers.
* </p>
*
* @see <a href="http://www.w3.org/TR/geolocation-API/">W3C Geolocation API</a>
* @see <a href="http://diveintohtml5.info/geolocation.html">Dive Into HTML5 -
* Geolocation</a>
*/
@PartialSupport
public class Geolocation {
private static GeolocationSupportDetector detector;
private static Geolocation impl;
/**
* Detector for browser support for Geolocation.
*/
private static class GeolocationSupportDetector {
private static native boolean detectSupport() /*-{
return !!$wnd.navigator.geolocation;
}-*/;
private boolean supported = detectSupport();
public boolean isSupported() {
return supported;
}
}
/**
* Detector for browsers that do not support Geolocation.
*/
@SuppressWarnings("unused")
private static class GeolocationSupportDetectorNo extends GeolocationSupportDetector {
@Override
public boolean isSupported() {
return false;
}
}
/**
* Additional options for receiving the user's location.
*/
public static class PositionOptions {
private boolean enableHighAccuracy = false;
private int timeout = -1;
private int maximumAge = 0;
/**
* Sets whether or not the application will request a more accurate position
* from the browser.
*
* <p>
* If the browser supports this option, the user will be prompted to grant
* permission to this application, even if permission to get the user's
* (less accurate) position has already been granted.</p>
*
* <p>
* Requesting high accuracy may be slower, or not supported at all,
* depending on the browser.
* </p>
*
* <p>
* By default this is <code>false</code>
* </p>
*/
public PositionOptions setHighAccuracyEnabled(boolean enabled) {
this.enableHighAccuracy = enabled;
return this;
}
/**
* Allows the browser to return a position immediately with a cached
* position. The maximum age is then the oldest acceptable cached
* position. If no acceptable cached position is found, the browser will
* locate the user and cache and return the position.
*
* <p>
* By default this is 0, which means that the position cache will not be
* used.
* </p>
*/
public PositionOptions setMaximumAge(int maximumAge) {
this.maximumAge = maximumAge;
return this;
}
/**
* Sets the amount of time (in milliseconds) that the application is willing
* to wait before getting the user's position. If a request for position
* takes more than this amount of time, an error will result.
*
* <p>
* By default this is -1, which means there is no application-specified
* timeout.
* </p>
*/
public PositionOptions setTimeout(int timeout) {
this.timeout = timeout;
return this;
}
}
/**
* Returns a {@link Geolocation} if the browser supports this feature, and
* <code>null</code> otherwise.
*/
public static Geolocation getIfSupported() {
if (!isSupported()) {
return null;
} else {
if (impl == null) {
impl = new Geolocation();
}
return impl;
}
}
/**
* Returns <code>true</code> if the browser supports geolocation.
*/
public static boolean isSupported() {
if (detector == null) {
detector = GWT.create(GeolocationSupportDetector.class);
}
return detector.isSupported();
}
private static void handleFailure(Callback<Position, PositionError> callback, int code,
String msg) {
callback.onFailure(new PositionError(code, msg));
}
private static void handleSuccess(Callback<Position, PositionError> callback, PositionImpl pos) {
callback.onSuccess(pos);
}
private static native JavaScriptObject toJso(PositionOptions options) /*-{
var opt = {};
if (options) {
opt.enableHighAccuracy = options.@com.google.gwt.geolocation.client.Geolocation.PositionOptions::enableHighAccuracy;
opt.maximumAge = options.@com.google.gwt.geolocation.client.Geolocation.PositionOptions::maximumAge;
if (options.@com.google.gwt.geolocation.client.Geolocation.PositionOptions::timeout > 0) {
opt.timeout = options.@com.google.gwt.geolocation.client.Geolocation.PositionOptions::timeout;
}
}
return opt;
}-*/;
/**
* Should be instantiated by {@link #getIfSupported()}.
*/
protected Geolocation() {
}
/**
* Stops watching the user's position.
*
* @param watchId the ID of a position watch as returned by a previous call to
* {@link #watchPosition(Callback)}.
*/
public native void clearWatch(int watchId) /*-{
$wnd.navigator.geolocation.clearWatch(watchId);
}-*/;
/**
* Calls the callback with the user's current position.
*/
public void getCurrentPosition(Callback<Position, PositionError> callback) {
getCurrentPosition(callback, null);
}
/**
* Calls the callback with the user's current position, with additional
* options.
*/
public native void getCurrentPosition(Callback<Position, PositionError> callback,
PositionOptions options) /*-{
var opt = @com.google.gwt.geolocation.client.Geolocation::toJso(*)(options);
var success = $entry(function(pos) {
@com.google.gwt.geolocation.client.Geolocation::handleSuccess(*)(callback, pos);
});
var failure = $entry(function(err) {
@com.google.gwt.geolocation.client.Geolocation::handleFailure(*)
(callback, err.code, err.message);
});
if (@com.google.gwt.geolocation.client.Geolocation::isSupported()) {
$wnd.navigator.geolocation.getCurrentPosition(success, failure, opt);
}
}-*/;
/**
* Repeatedly calls the given callback with the user's position, as it
* changes.
*
* <p>
* The frequency of these updates is entirely up to the browser. There is no
* guarantee that updates will be received at any set interval, but are
* instead designed to be sent when the user's position changes. This method
* should be used instead of polling the user's current position.
* </p>
*
* @return the ID of this watch, which can be passed to
* {@link #clearWatch(int)} to stop watching the user's position.
*/
public int watchPosition(Callback<Position, PositionError> callback) {
final int watchId = watchPosition(callback, null);
Impl.scheduleDispose(new Disposable() {
@Override
public void dispose() {
clearWatch(watchId);
}
});
return watchId;
}
/**
* Repeatedly calls the given callback with the user's position, as it
* changes, with additional options.
*
* <p>
* The frequency of these updates is entirely up to the browser. There is no
* guarantee that updates will be received at any set interval, but are
* instead designed to be sent when the user's position changes. This method
* should be used instead of polling the user's current position.
* </p>
*
* <p>
* If the browser does not support geolocation, this method will do nothing,
* and will return -1.
* </p>
*
* @return the ID of this watch, which can be passed to
* {@link #clearWatch(int)} to stop watching the user's position.
*/
public native int watchPosition(Callback<Position, PositionError> callback,
PositionOptions options) /*-{
var opt = @com.google.gwt.geolocation.client.Geolocation::toJso(*)(options);
var success = $entry(function(pos) {
@com.google.gwt.geolocation.client.Geolocation::handleSuccess(*)(callback, pos);
});
var failure = $entry(function(err) {
@com.google.gwt.geolocation.client.Geolocation::handleFailure(*)
(callback, err.code, err.message);
});
var id = -1;
if (@com.google.gwt.geolocation.client.Geolocation::isSupported()) {
id = $wnd.navigator.geolocation.watchPosition(success, failure, opt);
}
return id;
}-*/;
}