package com.eclipsesource.tabris.internal; import static com.eclipsesource.tabris.internal.Clauses.whenNull; import static com.eclipsesource.tabris.internal.Constants.EVENT_LOCATION_UPDATE_ERROR_EVENT; import static com.eclipsesource.tabris.internal.Constants.EVENT_LOCATION_UPDATE_EVENT; import static com.eclipsesource.tabris.internal.Constants.PROPERTY_ACCURACY; import static com.eclipsesource.tabris.internal.Constants.PROPERTY_ALTITUDE; import static com.eclipsesource.tabris.internal.Constants.PROPERTY_ALTITUDE_ACCURACY; import static com.eclipsesource.tabris.internal.Constants.PROPERTY_ERROR_CODE; import static com.eclipsesource.tabris.internal.Constants.PROPERTY_ERROR_MESSAGE; import static com.eclipsesource.tabris.internal.Constants.PROPERTY_FREQUENCY; import static com.eclipsesource.tabris.internal.Constants.PROPERTY_HEADING; import static com.eclipsesource.tabris.internal.Constants.PROPERTY_HIGH_ACCURACY; import static com.eclipsesource.tabris.internal.Constants.PROPERTY_LATITUDE; import static com.eclipsesource.tabris.internal.Constants.PROPERTY_LONGITUDE; import static com.eclipsesource.tabris.internal.Constants.PROPERTY_MAXIMUM_AGE; import static com.eclipsesource.tabris.internal.Constants.PROPERTY_NEEDS_POSITION; import static com.eclipsesource.tabris.internal.Constants.PROPERTY_SPEED; import static com.eclipsesource.tabris.internal.Constants.PROPERTY_TIMESTAMP; import static com.eclipsesource.tabris.internal.Constants.TYPE_GEOLOCATION; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import org.eclipse.rap.json.JsonObject; import org.eclipse.rap.json.JsonValue; import org.eclipse.rap.rwt.RWT; import org.eclipse.rap.rwt.internal.remote.ConnectionImpl; import org.eclipse.rap.rwt.remote.AbstractOperationHandler; import org.eclipse.rap.rwt.remote.RemoteObject; import com.eclipsesource.tabris.geolocation.Coordinates; import com.eclipsesource.tabris.geolocation.Geolocation; import com.eclipsesource.tabris.geolocation.GeolocationListener; import com.eclipsesource.tabris.geolocation.GeolocationOptions; import com.eclipsesource.tabris.geolocation.Position; import com.eclipsesource.tabris.geolocation.PositionError; import com.eclipsesource.tabris.geolocation.PositionError.PositionErrorCode; @SuppressWarnings("restriction") public class GeolocationImpl extends AbstractOperationHandler implements Geolocation { private enum NeedsPositionFlavor { NEVER, ONCE, CONTINUOUS } private final RemoteObject remoteObject; private final List<GeolocationListener> geolocationListeners; public GeolocationImpl() { remoteObject = ( ( ConnectionImpl )RWT.getUISession().getConnection() ).createServiceObject( TYPE_GEOLOCATION ); remoteObject.setHandler( this ); remoteObject.set( PROPERTY_NEEDS_POSITION, NeedsPositionFlavor.NEVER.toString() ); geolocationListeners = new ArrayList<GeolocationListener>(); } @Override public void handleNotify( String event, JsonObject properties ) { if( EVENT_LOCATION_UPDATE_EVENT.equals( event ) ) { Position position = getPosition( properties ); notifyListenersWithPosition( position ); } else if( EVENT_LOCATION_UPDATE_ERROR_EVENT.equals( event ) ) { PositionError error = getPositionError( properties ); notifyListenersWithError( error ); } } private void notifyListenersWithPosition( Position position ) { List<GeolocationListener> listeners = new ArrayList<GeolocationListener>( geolocationListeners ); for( GeolocationListener listener : listeners ) { listener.positionReceived( position ); } } private void notifyListenersWithError( PositionError error ) { List<GeolocationListener> listeners = new ArrayList<GeolocationListener>( geolocationListeners ); for( GeolocationListener listener : listeners ) { listener.errorReceived( error ); } } private Position getPosition( JsonObject properties ) { String timestampValue = properties.get( PROPERTY_TIMESTAMP ).asString(); try { Date timestamp = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ssZ" ).parse( timestampValue ); Coordinates coordinates = getCoordinates( properties ); Position position = new Position( coordinates, timestamp ); return position; } catch( ParseException e ) { throw new IllegalStateException( "Could not parse date from string: " + timestampValue + ", needs to be yyyy-MM-dd'T'HH:mm:ssZ" ); } } private Coordinates getCoordinates( JsonObject properties ) { return new Coordinates( getPropertyAsDouble( properties, PROPERTY_LATITUDE ), getPropertyAsDouble( properties, PROPERTY_LONGITUDE ), getPropertyAsDouble( properties, PROPERTY_ALTITUDE ), getPropertyAsDouble( properties, PROPERTY_ACCURACY ), getPropertyAsDouble( properties, PROPERTY_ALTITUDE_ACCURACY ), getPropertyAsDouble( properties, PROPERTY_HEADING ), getPropertyAsDouble( properties, PROPERTY_SPEED ) ); } private double getPropertyAsDouble( JsonObject properties, String propertyName ) { double result = -1; JsonValue value = properties.get( propertyName ); if( value.isNumber() ) { result = value.asDouble(); } return result; } private PositionError getPositionError( JsonObject properties ) { String code = properties.get( PROPERTY_ERROR_CODE ).asString(); PositionErrorCode errorCode = PositionErrorCode.valueOf( code ); String message = properties.get( PROPERTY_ERROR_MESSAGE ).asString(); PositionError positionError = new PositionError( errorCode, message ); return positionError; } @Override public void determineCurrentPosition( GeolocationOptions options ) { startUpdatePosition( NeedsPositionFlavor.ONCE, options ); } @Override public void watchPosition( GeolocationOptions options ) { startUpdatePosition( NeedsPositionFlavor.CONTINUOUS, options ); } private void startUpdatePosition( NeedsPositionFlavor flavor, GeolocationOptions options ) { whenNull( options ).throwIllegalArgument( "Options must not be null" ); remoteObject.set( PROPERTY_NEEDS_POSITION, flavor.toString() ); setOptions( options ); } private void setOptions( GeolocationOptions options ) { remoteObject.set( PROPERTY_FREQUENCY, options.getFrequency() ); remoteObject.set( PROPERTY_MAXIMUM_AGE, options.getMaximumAge() ); remoteObject.set( PROPERTY_HIGH_ACCURACY, options.isHighAccuracyEnabled() ); } @Override public void clearWatch() { remoteObject.set( PROPERTY_NEEDS_POSITION, NeedsPositionFlavor.NEVER.toString() ); } RemoteObject getRemoteObject() { return remoteObject; } @Override public void addGeolocationListener( GeolocationListener listener ) { whenNull( listener ).throwIllegalArgument( "Listener must not be null" ); geolocationListeners.add( listener ); } @Override public void removeGeolocationListener( GeolocationListener listener ) { whenNull( listener ).throwIllegalArgument( "Listener must not be null" ); geolocationListeners.remove( listener ); } }