/*
* Copyright 2012 Monits
*
* 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.monits.blackberry.commons.service.impl;
import java.util.Vector;
import javax.microedition.location.Criteria;
import javax.microedition.location.Location;
import javax.microedition.location.LocationException;
import javax.microedition.location.LocationProvider;
import com.monits.blackberry.commons.location.LocationListener;
import com.monits.blackberry.commons.logger.Logger;
import com.monits.blackberry.commons.service.LocationService;
/**
* Implementation for {@link LocationService}
* @author Rodrigo Pereyra
*/
public class LocationServiceImpl implements LocationService {
/**
* Will retrieve a default implementation.
*/
public static final int GPS_MODE_ANY = 0;
/**
* Computes a fix using only satellites
* <p> - Requires a clear view of the sky.
* <p><p> - Relatively higher Average Time To First Fix (TTFF).
* <p><p> - No data connectivity required.
* <p><p> - Recommended for applications that require frequent fixes.
*/
public static final int GPS_MODE_STANDALONE = 1;
/**
* Use a Position Determining Entity (PDE) server hosted by the carrier,
* and the device periodically get a fix.
* <p> - Requires data connectivity.
* <p><p> - Work fine with a partial view of the sky,outdoors and indoors.
* <p><p> - Requires data connectivity.
* <p><p> - Average TTFF is faster than Standalone.
*/
public static final int GPS_MODE_ASSISTED = 2;
/**
* Retrieves a fix based on the location of the nearest cell towers
* <p> - Extremely fast
* <p><p> - Accuracy is very low
* <p><p> - Recommended for applications where accuracy is not a concern
*/
public static final int GPS_MODE_CELLSITE = 3;
/**
* Device is assisted by the PDE server in every fix
* <p> - Requires data connectivity
* <p><p> - Can operate indoors and outdoors without any difficulty
* <p><p> - Very fast Average Time To First Fix
* <p><p> - Operates anywhere with a network connection
* <p><p> - Not recommended for frequent fixes
* <p><p> - Should not be used more than once every 10-15 minutes
*/
public static final int GPS_MODE_MS_ASSISTED = 4;
/**
* Use PDE server, hosted by the carrier, and periodically get a fix.
* <p> - Requires data connectivity
* <p><p> - Operates outdoors and indoors with a partial view of the sky
* <p><p> - Average Time To First Fix is faster than Standalone
* <p><p> - Recommended for applications that require frequent fixes
*/
public static final int GPS_MODE_MS_BASED = 5;
/**
* Preferred mode: MS-Based
* <p> - Initial mode attempted: MS-Based or MS-Assisted
* <p><p> - Fallback mode: If MB-Based is attempted first, fallback is to MS-Assisted;
* if MS-Assisted is attempted first, fallback is to MS-Based.
*/
public static final int GPS_MODE_SPEED_OPTIMAL = 6;
/**
* Preferred mode: MS-Assisted
* <p> - Initial mode attempted: MS-Assisted
* <p><p> - Fallback mode: MS-Based, if MS-Assisted fails
*/
public static final int GPS_MODE_ACCURACY_OPTIMAL = 7;
/**
* Preferred mode: MS-Based
* <p> - Initial mode attempted: MS-Based
* <p><p> - Fallback mode: MS-Assisted, if MS-Based fails
* <p><p> - Minimum PDE/network access is allowe
*/
public static final int GPS_MODE_DATA_OPTIMAL = 8;
public static final Logger logger = Logger.getLogger(LocationServiceImpl.class);
private static int GPS_MODE;
private Vector listeners;
private Location lastLocation;
private boolean started;
private LocationProvider locationProvider;
/**
* Constructor. Use a default location provider.
*/
public LocationServiceImpl() {
this(GPS_MODE_ANY);
}
/**
* Constructor.
*
* @param gpsMode The desired GPS mode
*/
public LocationServiceImpl(int gpsMode) {
this(new int[] { gpsMode });
}
/**
* Constructor. This method iterate the alternatives,
* and use the first one that is available.
*
* @param gpsModeAlternatives Alternatives for GPS mode, in order of importance.
*/
public LocationServiceImpl(int[] gpsModeAlternatives){
for(int i = 0; i < gpsModeAlternatives.length; i++) {
LocationProvider lp;
try {
lp = LocationProvider.getInstance(setGpsMode((gpsModeAlternatives[i])));
if(lp != null){
GPS_MODE = gpsModeAlternatives[i];
locationProvider = lp;
}
} catch (LocationException e) {
logger.error("Error trying to inititate the location service. Most likely unavailable.");
}
}
listeners = new Vector();
lastLocation = null;
started = false;
start();
}
/* (non-Javadoc)
* @see com.monits.blackberry.commons.services.LocationService#getCurrentLocation()
*/
public Location getCurrentLocation() {
return lastLocation;
}
/* (non-Javadoc)
* @see com.monits.blackberry.commons.services.LocationService#isStarted()
*/
public boolean isStarted() {
return started;
}
/* (non-Javadoc)
* @see com.monits.blackberry.commons.services.LocationService#addLocationListener(com.monits.blackberry.commons.location.LocationListener)
*/
public synchronized void addLocationListener(LocationListener listener) {
if (!listeners.contains(listener)) {
listeners.addElement(listener);
// Immediately notify the listener if possible!
if (lastLocation != null) {
listener.update(lastLocation);
}
}
}
/* (non-Javadoc)
* @see com.monits.blackberry.commons.services.LocationService#removeLocationListener(com.monits.blackberry.commons.location.LocationListener)
*/
public synchronized void removeLocationListener(LocationListener listener) {
listeners.removeElement(listener);
}
/* (non-Javadoc)
* @see com.monits.blackberry.commons.services.LocationService#start()
*/
public synchronized void start() {
if (started) {
return;
}
try {
locationProvider.setLocationListener(new javax.microedition.location.LocationListener() {
public void providerStateChanged(LocationProvider provider,
int newState) {
// Pass
}
public void locationUpdated(LocationProvider provider,
Location location) {
if (!location.isValid()) {
return;
}
LocationServiceImpl.this.lastLocation = location;
// Notify all listeners
for (int i = 0; i < LocationServiceImpl.this.listeners.size(); i++) {
LocationListener listener = (LocationListener) LocationServiceImpl.this.listeners.elementAt(i);
listener.update(location);
}
}
}, -1, -1, -1);
try {
// The API is pretty crappy and takes 30 secs to give the first fix, but this is almost instantaneous...
lastLocation = locationProvider.getLocation(5);
} catch (InterruptedException e) {
logger.error("Failed to get a first location due to interruption. " + e.getMessage());
}
started = true;
} catch (LocationException e) {
logger.error("Failed to get a location provider. " + e.getMessage());
}
}
/* (non-Javadoc)
* @see com.monits.blackberry.commons.services.LocationService#stop()
*/
public synchronized void stop() {
if (!started) {
return;
}
locationProvider.setLocationListener(null, -1, -1, -1);
locationProvider.reset();
started = false;
}
/**
* Set the GPS mode.
* @param gpsMode
* @return If is available, the Criteria object for the gps mode.
*/
public Criteria setGpsMode(int gpsMode){
Criteria criteria = new Criteria();
switch(GPS_MODE) {
case GPS_MODE_ANY:
criteria = null;
break;
case GPS_MODE_STANDALONE:
criteria.setCostAllowed(false);
break;
case GPS_MODE_ASSISTED:
criteria.setCostAllowed(true);
criteria.setHorizontalAccuracy(200);
criteria.setVerticalAccuracy(200);
criteria.setPreferredPowerConsumption(Criteria.POWER_USAGE_MEDIUM);
break;
case GPS_MODE_CELLSITE:
criteria.setCostAllowed(true);
criteria.setHorizontalAccuracy(Criteria.NO_REQUIREMENT);
criteria.setVerticalAccuracy(Criteria.NO_REQUIREMENT);
criteria.setPreferredPowerConsumption(Criteria.POWER_USAGE_LOW);
break;
case GPS_MODE_MS_ASSISTED:
criteria.setCostAllowed(true);
criteria.setHorizontalAccuracy(200);
criteria.setVerticalAccuracy(200);
criteria.setPreferredPowerConsumption(Criteria.POWER_USAGE_MEDIUM);
break;
case GPS_MODE_MS_BASED:
criteria.setCostAllowed(true);
criteria.setHorizontalAccuracy(200);
criteria.setVerticalAccuracy(200);
criteria.setPreferredPowerConsumption(Criteria.POWER_USAGE_MEDIUM);
break;
case GPS_MODE_SPEED_OPTIMAL:
criteria.setCostAllowed(true);
criteria.setHorizontalAccuracy(200);
criteria.setVerticalAccuracy(200);
criteria.setPreferredPowerConsumption(Criteria.POWER_USAGE_HIGH);
break;
case GPS_MODE_ACCURACY_OPTIMAL:
criteria.setCostAllowed(true);
criteria.setHorizontalAccuracy(200);
criteria.setVerticalAccuracy(200);
criteria.setPreferredPowerConsumption(Criteria.POWER_USAGE_HIGH);
break;
case GPS_MODE_DATA_OPTIMAL:
criteria.setCostAllowed(true);
criteria.setHorizontalAccuracy(Criteria.NO_REQUIREMENT);
criteria.setVerticalAccuracy(Criteria.NO_REQUIREMENT);
break;
}
return criteria;
}
}