/**
* CoreGPSDiagnosticScreen.java
*
* Copyright � 1998-2011 Research In Motion Limited
*
* 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.
*
* Note: For the sake of simplicity, this sample application may not leverage
* resource bundles and resource strings. However, it is STRONGLY recommended
* that application developers make use of the localization features available
* within the BlackBerry development platform to ensure a seamless application
* experience across a variety of languages and geographies. For more information
* on localizing your application, please refer to the BlackBerry Java Development
* Environment Development Guide associated with this release.
*/
package com.rim.samples.device.gpsdemoadvanced;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Calendar;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import javax.microedition.location.Criteria;
import javax.microedition.location.Location;
import javax.microedition.location.LocationException;
import javax.microedition.location.LocationListener;
import javax.microedition.location.LocationProvider;
import net.rim.blackberry.api.invoke.Invoke;
import net.rim.blackberry.api.invoke.MapsArguments;
import net.rim.device.api.command.Command;
import net.rim.device.api.command.CommandHandler;
import net.rim.device.api.command.ReadOnlyCommandMetadata;
import net.rim.device.api.gps.GPSSettings;
import net.rim.device.api.i18n.SimpleDateFormat;
import net.rim.device.api.io.LineReader;
import net.rim.device.api.system.DeviceInfo;
import net.rim.device.api.system.EventLogger;
import net.rim.device.api.system.RadioInfo;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.FieldChangeListener;
import net.rim.device.api.ui.MenuItem;
import net.rim.device.api.ui.UiApplication;
import net.rim.device.api.ui.component.BasicEditField;
import net.rim.device.api.ui.component.CheckboxField;
import net.rim.device.api.ui.component.Dialog;
import net.rim.device.api.ui.component.EditField;
import net.rim.device.api.ui.component.Menu;
import net.rim.device.api.ui.component.ObjectChoiceField;
import net.rim.device.api.ui.component.SeparatorField;
import net.rim.device.api.ui.component.TextField;
import net.rim.device.api.ui.container.MainScreen;
import net.rim.device.api.util.StringProvider;
/**
* A screen for testing the use of the core APIs included in the
* javax.microedition.location package.
*
* There are several available modes the user can choose. (m) stands for
* 'multiple fix' and (s) stands for 'single fix'.
*
* In Smart Mode the application operates in MS-Based mode but falls back to
* MS-Assisted for a single fix if the LocationProvider is unable to return a
* valid fix in (maxInvalidTime) seconds. The provider then goes back to
* MS-Based again.
*
* For any assisted modes (all those except Stand Alone or Cellsite), additional
* helper data from a PDE server is required.
*/
public final class CoreGPSDiagnosticScreen extends MainScreen implements
FieldChangeListener {
// This number represents the maximum number of provider resets performed
// before falling back to a single assisted fix. Only applies to Smart Mode.
private static final int FALL_BACK_COUNTER_THRESHOLD = 2;
// Provides a reference to the UiApplication for faster updating
private final UiApplication _uiApp;
private ObjectChoiceField _modeField;
// Check box to indicate whether location should be displayed on a map
private CheckboxField _enableMapLocationField;
// Field to specify if the device uses Verizon as a carrier. If so, app
// credentials will be set instead of PDE IP and Port.
private CheckboxField _isVerizonField;
// Field to specify the zoom level used in the BlackBerry Maps application
private BasicEditField _zoomLevelField;
// Field to enter desired horizontal accuracy. This value may be ignored in
// the case that the selected mode requires a specific horizontal accuracy
// value.
private BasicEditField _horizontalAccuracyField;
// Field to enter preferred response time
private BasicEditField _preferredResponseTimeField;
// Field to enter the interval parameter for a LocationListener
private BasicEditField _frequencyField;
// Field to enter the timeout parameter for a LocationListener
private BasicEditField _timeoutField;
// Field to enter the maximum age of the returned location in seconds
private BasicEditField _maxAgeField;
// Field to enter how long consecutive invalid fixes can occur until a
// LocationProvider reset is performed.
private BasicEditField _maxInvalidTimeField;
// Displays the value returned by Location.getLocationMethod()
private BasicEditField _currentModeField;
// Displays the number of satellites used to compute the current fix
private BasicEditField _currentSatelliteCountField;
// Field to enter the PDE IP
private BasicEditField _pdeIPField;
// Field to enter the PDE port
private BasicEditField _pdePortField;
// Displays the total number of updates that have occurred
private EditField _numberUpdatesField;
// Displays the total number of MS-Assisted updates that have occurred
private EditField _numberAssistedUpdatesField;
// Displays the total number of MS-Based updates that have occurred
private EditField _numberUnassistedUpdatesField;
// Displays the total number of valid updates that have occurred
private EditField _numberValidUpdatesField;
// Displays the total number of invalid updates that have occurred
private EditField _numberInvalidUpdatesField;
// Displays the location information for the current fix
private EditField _currentLocationField;
// Displays the time of the last valid fix
private EditField _lastValidFixField;
// Displays the time of the last LocationProvider reset
private EditField _lastResetField;
// EditField to show the log messages generated during the test
private TextField _log;
// A Thread that performs all location related work
private LocationThread _locThread;
// A menu item for stopping a running test
private final MenuItem _stopTestItem;
private boolean _PDESet;
/**
* Creates a new CoreGPSDiagnosticScreen object
*/
public CoreGPSDiagnosticScreen() {
// Initialize reference to UiApplication
_uiApp = UiApplication.getUiApplication();
// Initialize UI components
setTitle("Core GPS Diagnostic Test");
initFields();
_isVerizonField.setChangeListener(this);
// A MenuItem to start the diagnostic test
final MenuItem startTestItem =
new MenuItem(new StringProvider("Start Test"), 0x300010, 0);
startTestItem.setCommand(new Command(new CommandHandler() {
/**
* @see net.rim.device.api.command.CommandHandler#execute(ReadOnlyCommandMetadata,
* Object)
*/
public void execute(final ReadOnlyCommandMetadata metadata,
final Object context) {
// Format the UI for output
showOutputFields();
if (_locThread != null) {
if (!_locThread.isStopped()) {
_locThread.stop();
}
}
_log.setText("");
log("Core GPS API test starting");
log("Device: " + DeviceInfo.getDeviceName());
log("Device Software: " + DeviceInfo.getSoftwareVersion());
log("Carrier: " + RadioInfo.getCurrentNetworkName());
// Begin test
_locThread = new LocationThread();
_locThread.start();
}
}));
addMenuItem(startTestItem);
_stopTestItem =
new MenuItem(new StringProvider("Stop Test"), 0x300020, 1);
_stopTestItem.setCommand(new Command(new CommandHandler() {
/**
* @see net.rim.device.api.command.CommandHandler#execute(ReadOnlyCommandMetadata,
* Object)
*/
public void execute(final ReadOnlyCommandMetadata metadata,
final Object context) {
// Stop the thread
log("Stopping test");
_locThread.stop();
_locThread = null;
}
}));
addMenuItem(_stopTestItem);
// A MenuItem to display the help dialog
final MenuItem helpItem =
new MenuItem(new StringProvider("Help"), 0x300030, 2);
helpItem.setCommand(new Command(new CommandHandler() {
/**
* @see net.rim.device.api.command.CommandHandler#execute(ReadOnlyCommandMetadata,
* Object)
*/
public void execute(final ReadOnlyCommandMetadata metadata,
final Object context) {
// Display a pop-up dialog with instructions for using this
// application
displayHelp();
}
}));
addMenuItem(helpItem);
showInputFields();
}
/**
* Initializes the UI fields
*/
private void initFields() {
_modeField =
new ObjectChoiceField("Mode: ", new String[] {
"Stand Alone(s)", "Stand Alone(m)", "Data Optimal(m)",
"Speed Optimal(m)", "MS-Based(m)",
"Accuracy Optimal(s)", "PDE Calculate(s)",
"Cellsite(s)", "Cellsite(m)", "AFLT(s)",
"SmartMode(m)", "Default(s)", "Default(m)",
"NULL Criteria(s)", "NULL Criteria(m)" }, 1);
_enableMapLocationField = new CheckboxField("Map Location", false);
_isVerizonField = new CheckboxField("Verizon?", false);
_zoomLevelField = new BasicEditField("Zoom: ", "1");
_horizontalAccuracyField =
new BasicEditField("Horizontal Accuracy (meters): ", "100", 6,
BasicEditField.FILTER_INTEGER);
_preferredResponseTimeField =
new BasicEditField("Pref'd Response Time (sec): ", "16", 6,
BasicEditField.FILTER_INTEGER);
_frequencyField =
new BasicEditField("Fix Frequency (sec): ", "5", 5,
BasicEditField.FILTER_INTEGER);
_timeoutField =
new BasicEditField("Timeout (sec): ", "-1", 5,
BasicEditField.FILTER_INTEGER);
_maxAgeField =
new BasicEditField("MaxAge (sec): ", "-1", 5,
BasicEditField.FILTER_INTEGER);
_maxInvalidTimeField =
new BasicEditField("Max Invalid Time (sec): ", "90", 5,
BasicEditField.FILTER_INTEGER);
_currentModeField = new BasicEditField("Current Mode: ", "-");
_currentSatelliteCountField = new BasicEditField("Satellites: ", "-");
_pdeIPField = new BasicEditField("PDE IP: ", "");
_pdePortField = new BasicEditField("PDE Port: ", "");
_numberUpdatesField = new EditField("Total Updates: ", "0");
_numberAssistedUpdatesField = new EditField("Assisted: ", "0");
_numberUnassistedUpdatesField = new EditField("Unassisted: ", "0");
_numberValidUpdatesField = new EditField("Valid Updates: ", "0");
_numberInvalidUpdatesField = new EditField("Invalid Updates: ", "0");
_currentLocationField = new EditField("Location: ", "-");
_lastValidFixField = new EditField("Last Valid Fix: ", "-");
_lastResetField = new EditField("Last Reset: ", "-");
_log = new TextField();
_log.setLabel("Log: ");
}
/**
* Clears the screen and adds the UI input fields
*/
private void showInputFields() {
// Remove all current fields
deleteAll();
// Populate UI
add(_modeField);
add(new SeparatorField());
add(_enableMapLocationField);
add(_zoomLevelField);
add(new SeparatorField());
add(_horizontalAccuracyField);
add(_preferredResponseTimeField);
add(new SeparatorField());
add(_frequencyField);
add(_timeoutField);
add(_maxAgeField);
add(new SeparatorField());
add(_maxInvalidTimeField);
add(new SeparatorField());
add(_isVerizonField);
add(_pdeIPField);
add(_pdePortField);
add(new SeparatorField());
}
/**
* Clears the screen and adds the UI output fields
*/
private void showOutputFields() {
// Remove all current fields
deleteAll();
// Populate UI
add(_currentModeField);
add(new SeparatorField());
add(_currentLocationField);
add(new SeparatorField());
add(_currentSatelliteCountField);
add(new SeparatorField());
add(new SeparatorField());
add(_lastValidFixField);
add(new SeparatorField());
add(_lastResetField);
add(new SeparatorField());
add(new SeparatorField());
add(_numberUpdatesField);
add(new SeparatorField());
add(_numberAssistedUpdatesField);
add(new SeparatorField());
add(_numberUnassistedUpdatesField);
add(new SeparatorField());
add(_numberValidUpdatesField);
add(new SeparatorField());
add(_numberInvalidUpdatesField);
add(new SeparatorField());
add(new SeparatorField());
add(_log);
}
/**
* Displays a message in the log EditField as well as in the device event
* log
*
* @param message
* The text to be logged
*/
private void log(final String message) {
if (message != null) {
final String newMsg =
dateFormatter(System.currentTimeMillis()) + message + "\n";
// Add event to device log
EventLogger.logEvent(0x9876543212345L, newMsg.getBytes(),
EventLogger.ALWAYS_LOG);
_uiApp.invokeLater(new Runnable() {
/**
* @see java.lang.Runnable#run()
*/
public void run() {
// If log is too long, reset
if (_log.getText().length() > 1000) {
_log.setText("");
}
// Print message to log field
_log.setText(_log.getText() + newMsg);
}
});
}
}
/**
* Returns a String representation of a date provided in long format
*
* @param date
* Date in long format
* @return String representation of <code>date</code>
*/
private String dateFormatter(final long date) {
final SimpleDateFormat sdf =
new SimpleDateFormat(SimpleDateFormat.TIME_LONG);
final Date d = new Date(date);
final Calendar cal = Calendar.getInstance();
cal.setTime(d);
final StringBuffer buff = new StringBuffer();
buff.append('[');
buff.append(cal.get(Calendar.DAY_OF_MONTH));
buff.append('-');
buff.append(sdf.format(cal, new StringBuffer(), null).toString());
buff.append("] ");
return buff.toString();
}
/**
* Resets the fix-related UI items to their initial values
*/
private void resetDataFields() {
_uiApp.invokeLater(new Runnable() {
/**
* @see java.lang.Runnable#run()
*/
public void run() {
// Replace all entered values with defaults
_numberUpdatesField.setText("0");
_numberAssistedUpdatesField.setText("0");
_numberUnassistedUpdatesField.setText("0");
_numberValidUpdatesField.setText("0");
_numberInvalidUpdatesField.setText("0");
_currentLocationField.setText("-");
_currentSatelliteCountField.setText("-");
_locThread._lastValid = System.currentTimeMillis();
_lastValidFixField
.setText(dateFormatter(_locThread._lastValid));
_lastResetField.setText(dateFormatter(_locThread._lastReset));
_currentModeField.setText("-");
}
});
}
/**
* All location related tasks are performed in this Thread
*/
private final class LocationThread extends Thread {
/**
* If true, the app will use a LocationListener to continually provide
* updates with regard to location. If false, a single call to
* LocationProvider.getLocation() will be made.
*/
private boolean _isMultipleFixes = true;
/*
* If > FALL_BACK_COUNTER_THRESHOLD, a single fallBack fix will be
* computed in MS-Assisted mode. This is only used in SmartMode.
*/
private int _fallBackCounter = FALL_BACK_COUNTER_THRESHOLD;
/*
* In SmartMode the application operates mostly in MS-Based mode but
* falls back to MS-Assisted for a single fix if the LocationProvider is
* unable to return a valid fix after the number of resets reaches
* FALL_BACK_COUNTER_THRESHOLD. If the number of resets exceeds
* FALL_BACK_COUNTER_THRESHOLD, a single assisted fix will be computed.
* A reset is performed if the provider fails to return a valid fix for
* more than "maxInvalidTimeField.getText()" seconds. The provider then
* goes back to MS-Based mode again.
*/
private boolean _isSmartMode;
// Determines the mode of the LocationProvider
private Criteria _criteria;
// Arguments passed to LocationProvider.setLocationListener()
private final int _frequency, _timeout, _maxage;
// Counter variables for valid, invalid, assisted, unassisted and total
// updates
private int _totalUpdates, _validUpdates, _inValidUpdates,
_assistedUpdates, _unassistedUpdates;
// Reference to the LocationProvider
private LocationProvider _provider;
// Variable to store the date/time (in long format) of the last valid
// fix
private long _lastValid = System.currentTimeMillis();
// Variable to store the date/time (in long format) of the last
// LocationProvider reset
private long _lastReset = System.currentTimeMillis();
// Location object that holds the current location at a given time
private Location _location = null;
// Indicates whether the thread has been stopped
private boolean _isStopped;
/**
* Creates a new LocationThread object. Initializes frequency, timeout
* and max age counters and sets PDE parameters according to the PDE IP
* and PORT provided by the user.
*/
LocationThread() {
_frequency = Integer.parseInt(_frequencyField.getText());
_timeout = Integer.parseInt(_timeoutField.getText());
_maxage = Integer.parseInt(_maxAgeField.getText());
}
/*
* Sets up the PDE server
*/
private void setupPDE() {
final String pdeIPText = _pdeIPField.getText();
final String pdePortText = _pdePortField.getText();
if (pdeIPText.length() > 0) {
if (!_isVerizonField.getChecked()) {
log("Using PDE: " + pdeIPText + ":" + pdePortText);
final boolean setPDESuccess =
GPSSettings.setPDEInfo(pdeIPText, Integer
.parseInt(pdePortText));
if (setPDESuccess) {
_PDESet = true;
log("setPDEInfo() successful");
}
} else {
// Set Verizon specific settings
log("Using VZ Credentials: " + ";" + pdeIPText + ";"
+ pdePortText);
GPSSettings.setPDEInfo(";" + pdeIPText + ";" + pdePortText,
0);
// Verizon Verizon PDE server sessions time out after
// 12 hours. Set up a timer to reset the connection
// at ~12 hour intervals.
final Timer timer = new Timer();
final TimerTask task = new TimerTask() {
/**
* @see java.util.TimerTask#run()
*/
public void run() {
clearVerizonCredential();
setupPDE();
resetProvider();
setupProvider();
}
};
// Set period to just under 12 hours
final long period = 1000 * 60 * 60 * 12 - 60000;
// Set date to just under 12 hours from now
long date = new Date().getTime();
date = date + period;
timer.scheduleAtFixedRate(task, period, date);
_PDESet = true;
}
}
}
/**
* This method validates the value in the _horizontalAccuracy EditField.
* If mustBeZero is true then the value is set to 0. Otherwise, the user
* specified value is validated i.e. if the user sets a value < = 0 the
* value is force set to a default value of 100.
*
* @param mustBeZero
* True if the value must be 0, false otherwise
*/
private void validateHorizontalAccuracy(final boolean mustBeZero) {
_uiApp.invokeLater(new Runnable() {
/**
* @see java.lang.Runnable#run()
*/
public void run() {
final int value =
Integer.parseInt(_horizontalAccuracyField.getText());
if (!mustBeZero) {
if (value <= 0) {
_horizontalAccuracyField.setText("100");
}
} else {
_horizontalAccuracyField.setText("0");
}
}
});
}
/**
* @see java.lang.Thread#run()
*/
public void run() {
resetDataFields();
setupCriteria();
log("Criteria initialized");
if (!_PDESet) {
setupPDE();
}
log("Starting Updates: "
+ dateFormatter(System.currentTimeMillis()));
setupProvider();
}
/**
* Resets logic for most carriers (excludes Verizon)
*/
private void resetProvider() {
// Set to indicate that the provider has been reset
_lastReset = System.currentTimeMillis();
_uiApp.invokeLater(new Runnable() {
/**
* @see java.lang.Runnable#run()
*/
public void run() {
// Indicate in the UI that the provider is being reset
_lastResetField.setText(dateFormatter(_lastReset));
}
});
log("Resetting LocationProvider");
_provider.setLocationListener(null, 0, 0, 0);
_provider.reset();
_provider = null;
}
/**
* Resets credential logic for Verizon. Also refreshes the Verizon PDE
* session which needs to be done every 12 hours (contact Verizon).
*/
private void clearVerizonCredential() {
final Thread resetThread = new Thread() {
/**
* @see java.lang.Thread#run()
*/
public void run() {
LocationProvider tempProvider = null;
try {
tempProvider = LocationProvider.getInstance(_criteria);
} catch (final LocationException e) {
log(e.toString());
}
if (tempProvider != null) {
log("Clearing VZ credentials. Please wait...");
try {
Thread.sleep(2000);
} catch (final InterruptedException e) {
log(e.toString());
}
GPSSettings.setPDEInfo("127.0.0.1", 0);
try {
// Sleep so the PDE has time to be set up
Thread.sleep(2000);
} catch (final InterruptedException e) {
log(e.toString());
}
try {
tempProvider.getLocation(1);
} catch (final Exception e) {
log(e.toString());
}
try {
Thread.sleep(15000);
} catch (final InterruptedException e) {
log(e.toString());
}
tempProvider = null;
log("Old Verizon session cleared.");
}
}
};
resetThread.start();
try {
resetThread.join();
} catch (final InterruptedException e) {
log(e.toString());
}
}
/**
* Initializes criteria according to the mode selected by the user. The
* following algorithm is used: If costAllowed = FALSE mode is Stand
* Alone Otherwise, if costAllowed=TRUE, -if horizontalAccuracy = 0,
* mode is Data Optimal -if horizontalAccuracy > 0, -if multiplied fixes
* requested, -if Telus, mode is MS-based -otherwise, -if powerUsage =
* HIGH, mode is Speed Optimal; -if powerUsage != HIGH, mode is MS-based
* -else if single fix requested, -if powerUsage = HIGH, mode is
* Accuracy Optimal -if powerUsage != HIGH, mode is PDE Calculate -if
* powerUsage = LOW mode is Cellsite
*/
private void setupCriteria() {
_criteria = new Criteria();
_criteria.setPreferredResponseTime(Integer
.parseInt(_preferredResponseTimeField.getText()));
switch (_modeField.getSelectedIndex()) {
case 0: // Stand Alone(s)
_isMultipleFixes = false;
_isSmartMode = false;
_criteria.setCostAllowed(false);
validateHorizontalAccuracy(false);
_criteria.setVerticalAccuracy(200);
_criteria.setHorizontalAccuracy(Integer
.parseInt(_horizontalAccuracyField.getText()));
log("Criteria set for Stand Alone");
break;
case 1: // Stand Alone(m)
_isMultipleFixes = true;
_isSmartMode = false;
_criteria.setCostAllowed(false);
validateHorizontalAccuracy(false);
_criteria.setHorizontalAccuracy(Integer
.parseInt(_horizontalAccuracyField.getText()));
_criteria.setVerticalAccuracy(200);
log("Criteria set for Stand Alone");
break;
case 2: // Data Optimal(m)
_isMultipleFixes = true;
_isSmartMode = false;
_criteria.setCostAllowed(true);
validateHorizontalAccuracy(false);
_criteria.setHorizontalAccuracy(Criteria.NO_REQUIREMENT);
_criteria.setVerticalAccuracy(Criteria.NO_REQUIREMENT);
log("Criteria set for Data Optimal");
break;
case 3: // Speed Optimal(m)
_isMultipleFixes = true;
_isSmartMode = false;
_criteria.setCostAllowed(true);
validateHorizontalAccuracy(false);
_criteria.setHorizontalAccuracy(Integer
.parseInt(_horizontalAccuracyField.getText()));
_criteria.setVerticalAccuracy(200);
_criteria
.setPreferredPowerConsumption(Criteria.POWER_USAGE_HIGH);
log("Criteria set for Speed Optimal");
break;
case 4: // MS-Based(m)
_isMultipleFixes = true;
_isSmartMode = false;
_criteria.setCostAllowed(true);
validateHorizontalAccuracy(false);
_criteria.setHorizontalAccuracy(Integer
.parseInt(_horizontalAccuracyField.getText()));
_criteria.setVerticalAccuracy(200);
_criteria
.setPreferredPowerConsumption(Criteria.POWER_USAGE_MEDIUM);
log("Criteria set for MS-Based");
break;
case 5: // Accuracy Optimal(s)
_isMultipleFixes = false;
_isSmartMode = false;
_criteria.setCostAllowed(true);
validateHorizontalAccuracy(false);
_criteria.setHorizontalAccuracy(Integer
.parseInt(_horizontalAccuracyField.getText()));
_criteria.setVerticalAccuracy(200);
_criteria
.setPreferredPowerConsumption(Criteria.POWER_USAGE_HIGH);
log("Criteria set for Accuracy Optimal");
break;
case 6: // PDE Calculate(s)
_isMultipleFixes = false;
_isSmartMode = false;
_criteria.setCostAllowed(true);
validateHorizontalAccuracy(false);
_criteria.setHorizontalAccuracy(Integer
.parseInt(_horizontalAccuracyField.getText()));
_criteria.setVerticalAccuracy(200);
_criteria
.setPreferredPowerConsumption(Criteria.POWER_USAGE_MEDIUM);
log("Criteria set for PDE Calculate");
break;
case 7: // Cellsite(s)
_isMultipleFixes = false;
_isSmartMode = false;
_criteria.setCostAllowed(true);
_criteria.setHorizontalAccuracy(Criteria.NO_REQUIREMENT);
_criteria.setVerticalAccuracy(Criteria.NO_REQUIREMENT);
_criteria
.setPreferredPowerConsumption(Criteria.POWER_USAGE_LOW);
log("Criteria set for Cellsite(s)");
break;
case 8: // Cellsite(m)
_isMultipleFixes = true;
_isSmartMode = false;
_criteria.setCostAllowed(true);
_criteria.setHorizontalAccuracy(Criteria.NO_REQUIREMENT);
_criteria.setVerticalAccuracy(Criteria.NO_REQUIREMENT);
_criteria
.setPreferredPowerConsumption(Criteria.POWER_USAGE_LOW);
log("Criteria set for Cellsite(m)");
break;
case 9: // AFLT(s)
_isMultipleFixes = false;
_isSmartMode = false;
_criteria.setCostAllowed(true);
validateHorizontalAccuracy(false);
_criteria.setHorizontalAccuracy(Integer
.parseInt(_horizontalAccuracyField.getText()));
_criteria.setVerticalAccuracy(200);
_criteria
.setPreferredPowerConsumption(Criteria.POWER_USAGE_MEDIUM);
_criteria.setPreferredResponseTime(0);
log("Criteria set for AFLT");
break;
case 10: // SmartMode(m)
_isMultipleFixes = true;
_isSmartMode = true;
_criteria.setCostAllowed(true);
validateHorizontalAccuracy(false);
_criteria.setHorizontalAccuracy(Integer
.parseInt(_horizontalAccuracyField.getText()));
_criteria.setVerticalAccuracy(200);
_criteria
.setPreferredPowerConsumption(Criteria.POWER_USAGE_MEDIUM);
log("Criteria set for Smart Mode");
break;
case 11: // default criteria(s)
_isMultipleFixes = false;
_isSmartMode = false;
log("Criteria set for Default Criteria");
break;
case 12: // default criteria(m)
_isMultipleFixes = true;
_isSmartMode = false;
log("Criteria set for Default Criteria");
break;
case 13: // null criteria(s)
_isMultipleFixes = false;
_isSmartMode = false;
_criteria = null;
log("Criteria set to null");
break;
case 14: // null criteria(m)
_isMultipleFixes = true;
_isSmartMode = false;
_criteria = null;
log("Criteria set to null");
break;
}
}
/**
* This method initializes the LocationProvider and sets a
* LocationListener if <code>isMultipleFixes</code> is TRUE. Otherwise
* it simply calls singleFixLocationUpdate() which calls
* LocationProvider.getLocation() once to get a single fix.
*/
private void setupProvider() {
try {
log("setupProvider()");
try {
// Sleep to give _provider and _criteria enough time to
// instantiate
Thread.sleep(5000);
} catch (final InterruptedException ie) {
log(ie.toString());
}
_provider = LocationProvider.getInstance(_criteria);
log("LocationProvider initialized");
if (_provider != null) {
if (_isMultipleFixes && _isSmartMode
&& _fallBackCounter < FALL_BACK_COUNTER_THRESHOLD
|| _isMultipleFixes && !_isSmartMode) {
// Multifix non-SmartMode or SmartMode going back to
// MS-Based
if (_isSmartMode) {
log("SmartMode in MS-Based mode - fallBackCounter: "
+ _fallBackCounter);
}
_provider.setLocationListener(new LocListener(),
_frequency, _timeout, _maxage);
log("LocationListener started");
} else {
// Single fix non-SmartMode or SmartMode Falling back to
// MS-Assisted
if (_isSmartMode) {
log("SmartMode in MS-Assisted mode - fallBackCounter: "
+ _fallBackCounter);
}
log("Initiating single shot GPS fix");
singleFixLocationUpdate();
}
} else {
log("Provider unavailable for Criteria");
}
} catch (final LocationException le) {
log(le.toString());
}
}
/**
* Gets a single fix by calling LocationProvider.getLocation(). Updates
* the UI with the fix information. In case of a valid fix it maps the
* fix by invoking the Maps application.
*/
private void singleFixLocationUpdate() {
try {
_location = _provider.getLocation(100);
} catch (final InterruptedException ie) {
log("InterruptedException thrown by getLocation(): "
+ ie.getMessage());
} catch (final LocationException le) {
log("LocationException thrown by getLocation(): "
+ le.getMessage());
}
if (_location != null) {
_uiApp.invokeLater(new Runnable() {
/**
* @see java.lang.Runnable#run()
*/
public void run() {
_numberUpdatesField.setText(Integer
.toString(++_totalUpdates));
}
});
if (_location.isValid()) {
_lastValid = System.currentTimeMillis();
// Update UI to reflect new location
_uiApp.invokeLater(new Runnable() {
/**
* @see java.lang.Runnable#run()
*/
public void run() {
_lastValidFixField
.setText(dateFormatter(_lastValid));
_currentModeField
.setText(getLocMethodString(_location
.getLocationMethod()));
final StringBuffer buff = new StringBuffer();
buff.append(_location.getQualifiedCoordinates()
.getLatitude());
buff.append(' ');
buff.append(_location.getQualifiedCoordinates()
.getLongitude());
buff.append(' ');
buff.append(_location.getQualifiedCoordinates()
.getAltitude());
buff.append(' ');
_currentLocationField.setText(buff.toString());
_currentSatelliteCountField
.setText(getNumSatellites(_location));
_numberValidUpdatesField.setText(Integer
.toString(++_validUpdates));
_numberAssistedUpdatesField.setText(Integer
.toString(++_assistedUpdates));
}
});
final StringBuffer logText =
new StringBuffer("Valid single fix: ");
logText.append(_location.getQualifiedCoordinates()
.getLatitude());
logText.append(", ");
logText.append(_location.getQualifiedCoordinates()
.getLongitude());
logText.append(' ');
logText.append(_location.getQualifiedCoordinates()
.getAltitude());
log(logText.toString());
log("Method: "
+ getLocMethodString(_location.getLocationMethod()));
// If Smart Mode, go back to MS-Based
if (_isSmartMode) {
log("Smart Mode got single MS-Assisted fix");
_fallBackCounter = 0;
resetProvider();
setupProvider();
}
if (_enableMapLocationField.getChecked()) {
// Launch Maps application with current location
displayLocationOnMap(_location);
}
} else {
// Update UI to reflect invalid location
_uiApp.invokeLater(new Runnable() {
/**
* @see java.lang.Runnable#run()
*/
public void run() {
_currentLocationField.setText("Invalid");
_currentSatelliteCountField.setText("-");
_numberInvalidUpdatesField.setText(Integer
.toString(++_inValidUpdates));
}
});
log("Invalid single fix");
// Check if invalid fixes have exceeded allowed time
if (System.currentTimeMillis() - _lastValid >= Integer
.parseInt(_maxInvalidTimeField.getText()) * 1000
&& System.currentTimeMillis() - _lastReset >= Integer
.parseInt(_maxInvalidTimeField.getText()) * 1000
|| System.currentTimeMillis() - _lastReset >= Integer
.parseInt(_maxInvalidTimeField.getText()) * 1000) {
final StringBuffer logText =
new StringBuffer(
"Resetting Location Provider because: \nInvalid fixes for ");
logText.append((System.currentTimeMillis() - _lastValid) / 1000);
logText.append(" seconds\nNo provider reset for ");
logText.append((System.currentTimeMillis() - _lastReset) / 1000);
logText.append(" seconds");
log(logText.toString());
// If Smart Mode, go back to MS-Based
if (_isSmartMode) {
log("Smart Mode failed to get single MS-Assisted fix");
_fallBackCounter = 0;
resetProvider();
setupProvider();
}
resetProvider();
setupProvider();
}
}
} else {
log("Location is null");
// If Smart Mode, go back to MS-Based
if (_isSmartMode) {
log("Smart Mode FAILED! to get single MS-Assisted fix");
_fallBackCounter = 0;
resetProvider();
setupProvider();
}
}
}
/**
* Invoke the Maps application to show a fix on a map
*
* @param location
* The location object to display on map
*/
private void displayLocationOnMap(final Location location) {
try {
String lon =
Double.toString(location.getQualifiedCoordinates()
.getLongitude() * 100000);
lon = lon.substring(0, 8);
String lat =
Double.toString(location.getQualifiedCoordinates()
.getLatitude() * 100000);
lat = lat.substring(0, 7);
final StringBuffer document =
new StringBuffer("<lbs><location lon='");
document.append(lon);
document.append("' lat='");
document.append(lat);
document.append("' label='MyLocation' zoom='");
document.append(_zoomLevelField.getText());
document.append("'/></lbs>");
// Launch Maps application
Invoke.invokeApplication(Invoke.APP_TYPE_MAPS,
new MapsArguments(MapsArguments.ARG_LOCATION_DOCUMENT,
document.toString()));
} catch (final Exception e) {
log("Unable to map Location. Please make sure that BlackBerry Maps is installed.");
}
}
/**
* Returns a String representation of the location process being used
*
* @param method
* The the location method for which to retrieve a string
* @return Location method string
*/
private String getLocMethodString(final int method) {
final StringBuffer buf = new StringBuffer();
if ((method & Location.MTA_ASSISTED) != 0) {
buf.append("*MTA_ASSISTED");
}
if ((method & Location.MTA_UNASSISTED) != 0) {
buf.append("*MTA_UNASSISTED");
}
if ((method & Location.MTE_ANGLEOFARRIVAL) != 0) {
buf.append("*MTE_ANGLEOFARRIVAL");
}
if ((method & Location.MTE_CELLID) != 0) {
buf.append("*MTE_CELLID");
}
if ((method & Location.MTE_SATELLITE) != 0) {
buf.append("*MTE_SATELLITE");
}
if ((method & Location.MTE_SHORTRANGE) != 0) {
buf.append("*MTE_SHORTRANGE");
}
if ((method & Location.MTE_TIMEDIFFERENCE) != 0) {
buf.append("*MTE_TIMEDIFFERENCE");
}
if ((method & Location.MTE_TIMEOFARRIVAL) != 0) {
buf.append("*MTE_TIMEOFARRIVAL");
}
if ((method & Location.MTY_NETWORKBASED) != 0) {
buf.append("*MTY_NETWORKBASED");
}
if ((method & Location.MTY_TERMINALBASED) != 0) {
buf.append("*MTY_TERMINALBASED");
}
buf.append("*");
return buf.toString();
}
/**
* Retrieve the satellite data for a given location
*
* @param location
* The location to retrieve information for
* @return A string describing the number of available satellites
*/
private String getNumSatellites(final Location location) {
String extra =
location.getExtraInfo("application/X-jsr179-location-nmea");
// Retrieve the eighth section of the comma-delimited extra string
for (int i = 0; i < 7; i++) {
extra = extra.substring(extra.indexOf(',') + 1, extra.length());
}
return extra.substring(0, extra.indexOf(','));
}
/**
* Resets the BlackBerryLocationProvider and removes reference
*/
public void stop() {
_isStopped = true;
// Log the statistics for the location session
log("Stopping Updates: "
+ dateFormatter(System.currentTimeMillis()));
log("Total Updates: " + _numberUpdatesField);
log("Assisted Updates: " + _numberAssistedUpdatesField);
log("Unassisted Updates: " + _numberUnassistedUpdatesField);
log("Valid Updates: " + _numberValidUpdatesField);
log("Invalid Updates: " + _numberInvalidUpdatesField);
if (_provider != null) {
_provider.setLocationListener(null, 0, 0, 0);
_provider.reset();
_provider = null;
}
}
/**
* Returns the running/stopped status of this thread
*
* @return The running/stopped status of this thread
*/
boolean isStopped() {
return _isStopped;
}
/**
* LocationListener implementation
*/
private class LocListener implements LocationListener {
// Flag indicating an immediate reset is required due
// to TEMPORARILY_UNAVAILABLE event.
boolean _resetNow;
/**
* @see javax.microedition.location.LocationListener#locationUpdated(LocationProvider,
* Location)
*/
public void locationUpdated(final LocationProvider provider,
final Location location) {
_uiApp.invokeLater(new Runnable() {
/**
* @see java.lang.Runnable#run()
*/
public void run() {
_numberUpdatesField.setText(Integer
.toString(++_totalUpdates));
}
});
if (location.isValid()) {
_lastValid = System.currentTimeMillis();
_uiApp.invokeLater(new Runnable() {
/**
* @see java.lang.Runnable#run()
*/
public void run() {
// Update UI to reflect new location
_lastValidFixField
.setText(dateFormatter(_lastValid));
_currentModeField
.setText(getLocMethodString(location
.getLocationMethod()));
final StringBuffer buff = new StringBuffer();
buff.append(location.getQualifiedCoordinates()
.getLatitude());
buff.append(' ');
buff.append(location.getQualifiedCoordinates()
.getLongitude());
buff.append(' ');
buff.append(location.getQualifiedCoordinates()
.getAltitude());
buff.append(' ');
_currentLocationField.setText(buff.toString());
_currentSatelliteCountField
.setText(getNumSatellites(location));
_numberValidUpdatesField.setText(Integer
.toString(++_validUpdates));
_numberUnassistedUpdatesField.setText(Integer
.toString(++_unassistedUpdates));
}
});
final StringBuffer buff =
new StringBuffer("Valid multiple fix: ");
buff.append(location.getQualifiedCoordinates()
.getLatitude());
buff.append(", ");
buff.append(location.getQualifiedCoordinates()
.getLongitude());
buff.append(' ');
buff.append(location.getQualifiedCoordinates()
.getAltitude());
log(buff.toString());
log("Method: "
+ getLocMethodString(location.getLocationMethod()));
// If map location enabled, launch Maps application
if (_enableMapLocationField.getChecked()) {
displayLocationOnMap(location);
}
} else {
if (_resetNow) {
resetProvider();
setupProvider();
return;
}
_uiApp.invokeLater(new Runnable() {
/**
* @see java.lang.Runnable#run()
*/
public void run() {
_currentLocationField.setText("Invalid");
_currentSatelliteCountField.setText("-");
_numberInvalidUpdatesField.setText(Integer
.toString(++_inValidUpdates));
}
});
log("Invalid multiple fix");
// If invalid fixes have exceeded allowed time, reset the
// location provider
if (System.currentTimeMillis() - _lastValid >= Integer
.parseInt(_maxInvalidTimeField.getText()) * 1000
&& System.currentTimeMillis() - _lastReset >= Integer
.parseInt(_maxInvalidTimeField.getText()) * 1000
|| System.currentTimeMillis() - _lastReset >= Integer
.parseInt(_maxInvalidTimeField.getText()) * 1000) {
final StringBuffer buff =
new StringBuffer(
"Resetting Location Provider because: \nInvalid fixes for ");
buff.append((System.currentTimeMillis() - _lastValid)
/ 1000 + " seconds \nNo provider reset for ");
buff.append((System.currentTimeMillis() - _lastReset)
/ 1000 + " seconds");
log(buff.toString());
if (_isSmartMode) {
_fallBackCounter++;
}
resetProvider();
setupProvider();
}
}
}
/**
* @see javax.microedition.location.LocationListener#providerStateChanged(LocationProvider,
* int)
*/
public void providerStateChanged(final LocationProvider provider,
final int newState) {
switch (newState) {
case LocationProvider.OUT_OF_SERVICE: // Triggered when a BES
// policy does not allow
// location capabilities
log("State Change: Out of Service");
break;
case LocationProvider.TEMPORARILY_UNAVAILABLE: // Triggered when
// the system has
// stopped
// looking for a
// fix and went
// cold
log("State Change: Temp Unavailable");
// This is set to indicate that the provider has been reset
_lastValid = System.currentTimeMillis();
_uiApp.invokeLater(new Runnable() {
/**
* @see java.lang.Runnable#run()
*/
public void run() {
_lastValidFixField
.setText(dateFormatter(_lastValid));
}
});
log("Resetting Location Provider due to TEMPORARILY UNAVAILABLE state");
if (_isSmartMode) {
log("Smart Mode resetting...");
_fallBackCounter++;
_resetNow = true;
} else {
_resetNow = true;
}
break;
}
}
}
}
/**
* @see net.rim.device.api.ui.Screen#onClose()
*/
public boolean onClose() {
log("Closing Application");
if (_locThread != null) {
_locThread.stop();
_locThread = null;
}
return super.onClose();
}
/**
* @see net.rim.device.api.ui.Screen#onSavePrompt()
*/
public boolean onSavePrompt() {
// Suppress the save dialog
return true;
}
/**
* @see net.rim.device.api.ui.container.MainScreen#makeMenu(Menu, int)
*/
protected void makeMenu(final Menu menu, final int instance) {
super.makeMenu(menu, instance);
if (_locThread != null) {
menu.setDefault(_stopTestItem);
}
}
/**
* @see net.rim.device.api.ui.FieldChangeListener#fieldChanged(Field, int)
*/
public void fieldChanged(final Field field, final int context) {
if (field == _isVerizonField) {
if (_isVerizonField.getChecked()) {
_pdeIPField.setLabel("Client ID: ");
_pdePortField.setLabel("Password: ");
} else {
_pdeIPField.setLabel("PDE IP: ");
_pdePortField.setLabel("PDE Port: ");
}
}
}
/**
* Displays the help dialog
*/
private void displayHelp() {
final InputStream stream =
getClass().getResourceAsStream("/resource/help_core.txt");
final LineReader lineReader = new LineReader(stream);
final StringBuffer help = new StringBuffer();
for (;;) {
try {
help.append(new String(lineReader.readLine()));
help.append('\n');
} catch (final EOFException eof) {
// We've reached the end of the file
break;
} catch (final IOException ioe) {
Dialog.alert("LineReader#readLine() threw " + ioe.toString());
return;
}
}
Dialog.inform(help.toString());
}
}