org.smap.smapTask.android.activities.MainMapsActivity.java Source code

Java tutorial

Introduction

Here is the source code for org.smap.smapTask.android.activities.MainMapsActivity.java

Source

/*
 * Copyright (C) 2011 Cloudtec Pty Ltd
 * 
 * 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 org.smap.smapTask.android.activities;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Vector;

import org.odk.collect.android.activities.FormDownloadList;
import org.odk.collect.android.activities.FormEntryActivity;
import org.odk.collect.android.activities.InstanceUploaderList;
import org.odk.collect.android.application.Collect;
import org.odk.collect.android.database.FileDbAdapter;
import org.odk.collect.android.preferences.PreferencesActivity;
import org.odk.collect.android.provider.InstanceProviderAPI.InstanceColumns;
import org.odk.collect.android.database.TaskAssignment;
import org.smap.smapTask.android.R;
import org.smap.smapTask.android.listeners.TaskDownloaderListener;
import org.smap.smapTask.android.tasks.DownloadTasksTask;
import org.smap.smapTask.android.utilities.RouteToTaskWaiter;

import com.nutiteq.BasicMapComponent;
import com.nutiteq.android.MapView;
import com.nutiteq.cache.AndroidFileSystemCache;
import com.nutiteq.cache.MemoryCache;
import com.nutiteq.components.Line;
import com.nutiteq.components.LineStyle;
import com.nutiteq.components.OnMapElement;
import com.nutiteq.components.Place;
import com.nutiteq.components.PlaceIcon;
import com.nutiteq.components.PlaceLabel;
import com.nutiteq.components.Placemark;
import com.nutiteq.components.PolyStyle;
import com.nutiteq.components.Polygon;
import com.nutiteq.components.Route;
import com.nutiteq.components.RouteInstruction;
import com.nutiteq.components.WgsBoundingBox;
import com.nutiteq.components.WgsPoint;
import com.nutiteq.fs.FileSystem;
import com.nutiteq.fs.JSR75FileSystem;
import com.nutiteq.listeners.ErrorListener;
import com.nutiteq.listeners.OnMapElementListener;
import com.nutiteq.log.Log;
import com.nutiteq.maps.OpenStreetMap;
import com.nutiteq.maps.StoredMap;
import com.nutiteq.services.DirectionsService;
import com.nutiteq.services.DirectionsWaiter;
import com.nutiteq.services.YourNavigationDirections;
import com.nutiteq.ui.ThreadDrivenPanning;
import com.nutiteq.utils.Utils;
import com.nutiteq.wrappers.AppContext;
import com.nutiteq.wrappers.Graphics;
import com.nutiteq.wrappers.Image;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.BroadcastReceiver;
import android.content.ContentUris;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.preference.PreferenceManager;
import android.support.v4.content.LocalBroadcastManager;
import android.text.format.Time;
import android.view.ContextMenu;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ContextMenu.ContextMenuInfo;
import android.widget.AdapterView;
import android.widget.Toast;
import android.widget.ZoomControls;

/**
 * Responsible for displaying maps of tasks.
 * 
 * @author Neil Penman 
 */
public class MainMapsActivity extends Activity implements TaskDownloaderListener, OnMapElementListener {

    MapView mMapView;
    int mSelectedOverlayItem;
    FileDbAdapter fda = null;
    Cursor mTaskListCursor = null;
    public DownloadTasksTask mDownloadTasks;
    private boolean onRetainCalled;
    private static Image[] icons = { Utils.createImage("/res/drawable/user_marker.png"),
            Utils.createImage("/res/drawable/turn1.png"), Utils.createImage("/res/drawable/turn2.png"),
            Utils.createImage("/res/drawable/turn3.gif") };

    // menu options
    private static final int MENU_PREFERENCES = Menu.FIRST;
    private static final int MENU_ENTERDATA = Menu.FIRST + 1;
    private static final int MENU_SENDDATA = Menu.FIRST + 3;
    private static final int MENU_ZOOMTODATA = Menu.FIRST + 4;
    private static final int MENU_GETFORMS = Menu.FIRST + 6;

    // request codes for returning chosen form to main menu.
    private static final int FORM_CHOOSER = 0;
    private static final int INSTANCE_CHOOSER = 1;
    private static final int INSTANCE_UPLOADER = 2;
    private static final int REQUEST_AUTHENTICATE = 3;

    private String mProgressMsg;
    private static final int PROGRESS_DIALOG = 1;
    private static final int PROGRESS_ROUTING = 2;
    private AlertDialog mAlertDialog;
    private ProgressDialog mProgressDialog;
    private Context mContext;
    private Cursor taskListCursor;

    private LocationManager locationManager;
    private LocationListener locationListener;
    private BasicMapComponent mapComponent;
    public static RouteToTaskWaiter instance;
    private Bitmap icon;
    private Bitmap taskDoneIcon;
    private Placemark pMark;
    private Placemark pMarkDone;
    private boolean routingInprogress = false;
    private boolean routingEnabled = false; // TODO add menu option to enable routing
    private Place userPlace = null;
    private Place currentPlace = null;
    private OnMapElement[] taskPlaces; // Store the tasks as locations on the map in here
    private Polygon[] polyArray;
    private Line[] lineArray;
    private ArrayList<Line> routeLines = new ArrayList<Line>();
    private boolean justCreated = true;
    private StoredMap storedMap;

    private static final PolyStyle POLY_ACCEPTED = new PolyStyle(Color.BLUE & 0x20ffffff, PolyStyle.FILL,
            new LineStyle(Color.BLUE, 3));
    private static final PolyStyle POLY_DONE = new PolyStyle(Color.GREEN & 0x20ffffff, PolyStyle.FILL,
            new LineStyle(Color.GREEN, 3));
    private static final PolyStyle POLY_MISSED = new PolyStyle(Color.RED & 0x20ffffff, PolyStyle.FILL,
            new LineStyle(Color.RED, 3));
    private static final PolyStyle POLY_PENDING = new PolyStyle(Color.YELLOW & 0x20ffffff, PolyStyle.FILL,
            new LineStyle(Color.YELLOW, 3));
    private static final LineStyle LINE_ACCEPTED = new LineStyle(Color.BLUE, 3);
    private static final LineStyle LINE_DONE = new LineStyle(Color.GREEN, 3);
    private static final LineStyle LINE_PENDING = new LineStyle(Color.YELLOW, 3);
    private static final LineStyle LINE_MISSED = new LineStyle(Color.RED, 3);

    // handler for received Intents for the "refresh" event 
    private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            refreshTaskOverlay();
        }
    };

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_map);
        onRetainCalled = false;
        mapComponent = new BasicMapComponent("ef0d3930a7b6c95bd2b32ed45989c61f507eb65ee43283.04602330",
                new AppContext(this), 1, 1, new WgsPoint(144.959, -37.818), 10);
        mapComponent.setMap(OpenStreetMap.MAPNIK);

        final MemoryCache memoryCache = new MemoryCache(10 * 1024 * 1024);
        final File cacheDir = new File("/sdcard/maps_lib_cache");
        if (!cacheDir.exists()) {
            cacheDir.mkdir();
        }
        final AndroidFileSystemCache fileSystemCache = new AndroidFileSystemCache(this, "network_cache", cacheDir,
                10 * 1024 * 1024);
        //        mapComponent.setNetworkCache(new CachingChain(new Cache[] { memoryCache, fileSystemCache }));
        mapComponent.setNetworkCache(fileSystemCache);

        storedMap = new StoredMap("x", "/sdcard/maps_lib_cache", true);
        final Image missing = Image.createImage(storedMap.getTileSize(), storedMap.getTileSize());
        final Graphics graphics = missing.getGraphics();
        graphics.setColor(0xFFCCCECC);
        graphics.fillRect(0, 0, storedMap.getTileSize(), storedMap.getTileSize());
        storedMap.setMissingTileImage(missing);

        FileSystem fs = new JSR75FileSystem();
        mapComponent.setFileSystem(fs);
        mapComponent.setErrorListener(new MyErrorListener());

        mapComponent.setPanningStrategy(new ThreadDrivenPanning());
        mapComponent.startMapping();
        mapComponent.looseFocusOnDrag(true);

        // Define the location marker
        icon = BitmapFactory.decodeResource(getResources(), R.drawable.user_marker);
        pMark = new PlaceIcon(Image.createImage(icon), icon.getWidth() / 2, icon.getHeight());

        // Define the completed task actual location marker
        taskDoneIcon = BitmapFactory.decodeResource(getResources(), R.drawable.task_done_marker);
        pMarkDone = new PlaceIcon(Image.createImage(taskDoneIcon), icon.getWidth() / 2, icon.getHeight());

        // get the mapview that was defined in main.xml
        mMapView = (MapView) findViewById(R.id.mapview);
        //registerForContextMenu(mMapView);

        // mapview requires a mapcomponent
        mMapView.setMapComponent(mapComponent);

        mapComponent.setOnMapElementListener(this);
        mMapView.setFocusable(true);

        ZoomControls zoomControls = (ZoomControls) findViewById(R.id.zoomcontrols);

        // set zoomcontrols listeners to enable zooming
        zoomControls.setOnZoomInClickListener(new View.OnClickListener() {
            public void onClick(final View v) {
                mapComponent.zoomIn();
            }
        });

        zoomControls.setOnZoomOutClickListener(new View.OnClickListener() {
            public void onClick(final View v) {
                mapComponent.zoomOut();
            }
        });

        /*
         * Add a location listener
         */
        locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        locationListener = new LocationListener() {

            @Override
            public void onLocationChanged(Location location) {

                if (userPlace != null) {
                    mapComponent.removePlace(userPlace);
                }

                // TODO check for accuracy, save previous location
                WgsPoint point = new WgsPoint(location.getLongitude(), location.getLatitude());
                userPlace = new Place(0, new PlaceLabel("Location"), pMark, point);
                mapComponent.addPlace(userPlace);
            }

            @Override
            public void onProviderDisabled(String arg0) {

            }

            @Override
            public void onProviderEnabled(String provider) {

            }

            @Override
            public void onStatusChanged(String provider, int status, Bundle extras) {

            }

        };

        mapComponent.setMiddlePoint(new WgsPoint(144.95987, -37.81819));
        mapComponent.setSmoothZoom(true);

    }

    class MyErrorListener implements ErrorListener {
        public void licenseError(String message) {
            mapComponent.setMap(storedMap);
            System.out.println("Nutiteq License Error!! Map is disabled...");
        }

        public void networkError(String message) {
            mapComponent.setMap(storedMap);
            System.out.println("Network Error!! Map is disabled...");
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        // Unregister since the activity is not visible
        LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver);
        locationManager.removeUpdates(locationListener);
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
            locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 30000, (float) 10.0,
                    locationListener);
        }

        // Register mMessageReceiver to receive messages. (from http://www.vogella.com/articles/AndroidBroadcastReceiver/article.html)
        LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver, new IntentFilter("refresh"));

        refreshTaskOverlay();

    }

    @Override
    public Object onRetainNonConfigurationInstance() {
        onRetainCalled = true;
        return mapComponent;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        if (!onRetainCalled) {
            mapComponent.stopMapping();
            mapComponent = null;
        }
    }

    private void calculateNewRoute() {

        WgsPoint userLocation = userPlace.getWgs();
        WgsPoint endCoordinates = currentPlace.getWgs();
        if (endCoordinates != null && endCoordinates.getLon() != userLocation.getLon()
                && endCoordinates.getLat() != userLocation.getLat()) {
            // TODO close the old routing thread ?
            Thread routingThread = new Thread(
                    new RoutingThread(new RoutingHandler(), RoutingHandler.ROUTING_YOURNAVIGATION));

            routingThread.start();
            routingInprogress = true;
        }
    }

    /*
     * The following options menu functions are replicated from MainListActivity - they should be shared!
     */
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);
        menu.add(0, MENU_ZOOMTODATA, 5, getString(R.string.smap_zoom_data));

        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case MENU_ZOOMTODATA:
            setMaxBoundingBox();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    private void processSyncTask() {
        mProgressMsg = getString(R.string.smap_synchronising);
        showDialog(PROGRESS_DIALOG);
        mDownloadTasks = new DownloadTasksTask();
        mDownloadTasks.setDownloaderListener(this, mContext);
        mDownloadTasks.execute();
    }

    @Override
    protected Dialog onCreateDialog(int id) {
        switch (id) {
        case PROGRESS_DIALOG:
            mProgressDialog = new ProgressDialog(this);
            DialogInterface.OnClickListener loadingButtonListener = new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    dialog.dismiss();
                    mDownloadTasks.setDownloaderListener(null, mContext);
                    mDownloadTasks.cancel(true);
                    refreshTaskOverlay();
                }
            };
            mProgressDialog.setTitle(getString(R.string.downloading_data));
            mProgressDialog.setMessage(mProgressMsg);
            mProgressDialog.setIcon(android.R.drawable.ic_dialog_info);
            mProgressDialog.setIndeterminate(true);
            mProgressDialog.setCancelable(false);
            mProgressDialog.setButton(getString(R.string.cancel), loadingButtonListener);
            return mProgressDialog;
        case PROGRESS_ROUTING:
            mProgressDialog = new ProgressDialog(this);
            mProgressDialog.setTitle(getString(R.string.smap_finding_route));
            mProgressDialog.setMessage(getString(R.string.smap_route_finding_inprogress));
            mProgressDialog.setIcon(android.R.drawable.ic_dialog_info);
            mProgressDialog.setIndeterminate(true);
            mProgressDialog.setCancelable(false);
            return mProgressDialog;
        }
        return null;
    }

    /*
     * (non-Javadoc)
     * @see org.smap.smapTask.android.listeners.TaskDownloaderListener#progressUpdate(int)
     */
    public void progressUpdate(String progress) {
        mProgressMsg = progress;
        mProgressDialog.setMessage(mProgressMsg);
    }

    /*
     * (non-Javadoc)
     * @see org.smap.smapTask.android.listeners.TaskDownloaderListener#taskDownloadingComplete()
     */
    public void taskDownloadingComplete(HashMap<String, String> result) {
        dismissDialog(PROGRESS_DIALOG);

        refreshTaskOverlay();
    }

    /**
     * Upon return, check intent for data needed to launch other activities.
     */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
        if (resultCode == RESULT_CANCELED) {
            return;
        }

        String formPath = null;
        Intent i = null;
        switch (requestCode) {
        // returns with a form path, start entry
        case FORM_CHOOSER:
            formChooser(intent, null);
            break;
        // returns with an instance path, start entry
        case INSTANCE_CHOOSER:
            formPath = intent.getStringExtra(FormEntryActivity.KEY_FORMPATH);
            String instancePath = intent.getStringExtra(FormEntryActivity.KEY_INSTANCEPATH);
            i = new Intent("org.smap.smapTask.android.action.FormEntry");
            i.putExtra(FormEntryActivity.KEY_FORMPATH, formPath);
            i.putExtra(FormEntryActivity.KEY_INSTANCEPATH, instancePath);
            startActivity(i);
            break;
        case REQUEST_AUTHENTICATE: // TODO what if this returns use not permitted?
            processSyncTask();
        default:
            break;
        }
        super.onActivityResult(requestCode, resultCode, intent);
    }

    /*
     * Add the current tasks to the map
     */
    private void refreshTaskOverlay() {

        Vector<OnMapElement> places = new Vector<OnMapElement>();
        Vector<OnMapElement> polygons = new Vector<OnMapElement>();
        Vector<OnMapElement> linestrings = new Vector<OnMapElement>();
        PolyStyle polyStyle = null;
        LineStyle lineStyle = null;
        mapComponent.removeOnMapElements(taskPlaces); // Clear the existing tasks
        mapComponent.removeOnMapElements(polyArray); // Clear the existing tasks
        mapComponent.removeOnMapElements(lineArray); // Clear the existing tasks
        for (Line line : routeLines) {
            mapComponent.removeLine(line);
        }
        routeLines.clear();

        // get all tasks
        try {
            FileDbAdapter fda = new FileDbAdapter();
            fda.open();
            taskListCursor = fda.fetchTasksForSource(getSource(), true);
            fda.close();
        } catch (Exception e) {
            e.printStackTrace(); // TODO handle exception
        }

        if (taskListCursor.moveToFirst()) {

            do {
                int taskIdIndex = taskListCursor.getColumnIndex(FileDbAdapter.KEY_T_ID);
                int taskLonIndex = taskListCursor.getColumnIndex(FileDbAdapter.KEY_T_LON);
                int taskLatIndex = taskListCursor.getColumnIndex(FileDbAdapter.KEY_T_LAT);
                int taskNameIndex = taskListCursor.getColumnIndex(FileDbAdapter.KEY_T_TITLE);
                int taskStatusIndex = taskListCursor.getColumnIndex(FileDbAdapter.KEY_T_STATUS);
                int taskALonIndex = taskListCursor.getColumnIndex(FileDbAdapter.KEY_T_ADHOC_LON);
                int taskALatIndex = taskListCursor.getColumnIndex(FileDbAdapter.KEY_T_ADHOC_LAT);
                int taskLocationIndex = taskListCursor.getColumnIndex(FileDbAdapter.KEY_T_LOCATION);
                int taskGeomTypeIndex = taskListCursor.getColumnIndex(FileDbAdapter.KEY_T_GEOM_TYPE);

                int taskId = taskListCursor.getInt(taskIdIndex);
                String taskLongitude = taskListCursor.getString(taskLonIndex);
                String taskLatitude = taskListCursor.getString(taskLatIndex);
                String taskALongitude = taskListCursor.getString(taskALonIndex);
                String taskALatitude = taskListCursor.getString(taskALatIndex);
                String taskName = taskListCursor.getString(taskNameIndex);
                String taskStatus = taskListCursor.getString(taskStatusIndex);
                String taskLocation = taskListCursor.getString(taskLocationIndex);
                String taskGeomType = taskListCursor.getString(taskGeomTypeIndex);

                if (taskLongitude == null || taskLatitude == null) {
                    continue;
                }

                try {
                    double lon = Double.parseDouble(taskLongitude);
                    double lat = Double.parseDouble(taskLatitude);

                    Bitmap icon = null;
                    if (taskStatus.equals(FileDbAdapter.STATUS_T_ACCEPTED)) {
                        icon = BitmapFactory.decodeResource(getResources(), R.drawable.task_marker_accepted);
                        polyStyle = POLY_ACCEPTED;
                        lineStyle = LINE_ACCEPTED;
                    } else if (taskStatus.equals(FileDbAdapter.STATUS_T_DONE)) {
                        icon = BitmapFactory.decodeResource(getResources(), R.drawable.task_marker_done);
                        polyStyle = POLY_DONE;
                        lineStyle = LINE_DONE;
                    } else if (taskStatus.equals(FileDbAdapter.STATUS_T_MISSED)
                            || taskStatus.equals(FileDbAdapter.STATUS_T_CANCELLED)
                            || taskStatus.equals(FileDbAdapter.STATUS_T_REJECTED)
                            || taskStatus.equals(FileDbAdapter.STATUS_T_DELETED)) {
                        icon = BitmapFactory.decodeResource(getResources(), R.drawable.task_marker_missed);
                        polyStyle = POLY_MISSED;
                        lineStyle = LINE_MISSED;
                    } else if (taskStatus.equals(FileDbAdapter.STATUS_T_PENDING)) {
                        icon = BitmapFactory.decodeResource(getResources(), R.drawable.task_marker_pending);
                        polyStyle = POLY_PENDING;
                        lineStyle = LINE_PENDING;
                    }

                    if (icon != null) { // Ignore if an icon was not found for the status
                        final Placemark defaultIcon = new PlaceIcon(Image.createImage(icon), icon.getWidth() / 2,
                                icon.getHeight());

                        // Add an extra marker to show actual location of completed task
                        if (taskStatus.equals(FileDbAdapter.STATUS_T_DONE)) {
                            try {
                                if (taskALongitude != null && taskALatitude != null) {
                                    double lonA = Double.parseDouble(taskALongitude);
                                    double latA = Double.parseDouble(taskALatitude);
                                    WgsPoint donePoint = new WgsPoint(lonA, latA);
                                    Place donePlace = new Place((int) taskId, new PlaceLabel(taskName), pMarkDone,
                                            donePoint);
                                    places.add(donePlace);
                                }
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }

                        WgsPoint taskPoint = new WgsPoint(lon, lat);

                        Place newPlace = new Place((int) taskId, new PlaceLabel(taskName), defaultIcon, taskPoint);
                        if (taskGeomType != null && !taskGeomType.equals("POINT")) {
                            String[] coords = taskLocation.split(",");
                            if (coords.length > 1) {
                                Vector<WgsPoint> points = new Vector<WgsPoint>();
                                for (String c : coords) {
                                    String[] cElem = c.split(" ");
                                    if (cElem.length == 2) {
                                        WgsPoint p = new WgsPoint(Double.parseDouble(cElem[0]),
                                                Double.parseDouble(cElem[1]));
                                        points.add(p);
                                    }
                                }
                                WgsPoint[] pa = new WgsPoint[points.size()];
                                points.copyInto(pa);

                                if (taskGeomType.equals("POLYGON")) {
                                    Polygon aPolygon = new Polygon(pa, polyStyle);
                                    polygons.add(aPolygon);
                                } else if (taskGeomType.equals("LINESTRING")) {
                                    Line aLine = new Line(pa, lineStyle);
                                    linestrings.add(aLine);
                                }
                            }

                        }
                        places.add(newPlace);
                    }

                } catch (NumberFormatException numberFormatException) {
                    numberFormatException.printStackTrace();
                }

            } while (taskListCursor.moveToNext());
        }

        taskListCursor.close();

        // Save a copy of the task places so that they can be removed easily
        taskPlaces = new OnMapElement[places.size()];
        places.copyInto(taskPlaces);
        mapComponent.addOnMapElements(taskPlaces);

        if (polygons.size() > 0) {
            polyArray = new Polygon[polygons.size()];
            polygons.copyInto(polyArray);
            mapComponent.addOnMapElements(polyArray);
        }
        if (linestrings.size() > 0) {
            lineArray = new Line[linestrings.size()];
            linestrings.copyInto(lineArray);
            mapComponent.addOnMapElements(lineArray);
        }

        if (!justCreated) { // Prevent nutiteq zooming to to whole world first time onResume is called
            setMaxBoundingBox();
        }
        justCreated = false;

    }

    /*
     * Sets the bounding box to the maximum of the current tasks and the current location
     */
    private boolean setMaxBoundingBox() {

        WgsBoundingBox bbox = new WgsBoundingBox(180.0, 90.0, -180.0, -90.0);
        boolean setBbox = false;

        // Add the tasks
        int count = 0;
        for (int i = 0; i < taskPlaces.length; i++) {
            bbox = addToBoundingBox(bbox, ((Place) taskPlaces[i]).getWgs());
            count++;
        }
        // Add the current location to boundary (only if routing is enabled)
        if (routingEnabled && userPlace != null) {
            bbox = addToBoundingBox(bbox, userPlace.getWgs());
            count++;
        }

        if (count > 0) {
            // Make sure we do have a box
            bbox = nudgeBoxSize(bbox);
            mapComponent.loosePlaceFocus();
            mapComponent.setBoundingBox(bbox);
            setBbox = true;
        }
        return setBbox;

    }

    // Add a point to a bounding box
    private WgsBoundingBox addToBoundingBox(WgsBoundingBox bbox, WgsPoint point) {
        double lon = point.getLon();
        double lat = point.getLat();

        double maxLon = bbox.getWgsMax().getLon();
        double maxLat = bbox.getWgsMax().getLat();
        double minLon = bbox.getWgsMin().getLon();
        double minLat = bbox.getWgsMin().getLat();

        if (lon > maxLon) {
            maxLon = lon;
        }

        if (lon < minLon) {
            minLon = lon;
        }

        if (lat > maxLat) {
            maxLat = lat;
        }

        if (lat < minLat) {
            minLat = lat;
        }
        return new WgsBoundingBox(minLon, minLat, maxLon, maxLat);
    }

    /*
     * 1) Ensure the box is actually a box and not a point or a line.
     * 2) Add a 10% margin to the box
     */
    private WgsBoundingBox nudgeBoxSize(WgsBoundingBox bbox) {
        double maxLon = bbox.getWgsMax().getLon();
        double maxLat = bbox.getWgsMax().getLat();
        double minLon = bbox.getWgsMin().getLon();
        double minLat = bbox.getWgsMin().getLat();

        if (maxLon == minLon) {
            maxLon += 0.1;
            minLon -= 0.1;
        }
        if (maxLat == minLat) {
            maxLat += 0.1;
            minLat -= 0.1;
        }
        maxLat = maxLat + (maxLat - minLat) * 0.1;
        minLat = minLat - (maxLat - minLat) * 0.1;
        maxLon = maxLon + (maxLon - minLon) * 0.1;
        minLon = minLon - (maxLon - minLon) * 0.1;

        return new WgsBoundingBox(minLon, minLat, maxLon, maxLat);
    }

    private void formChooser(Intent intent, String instancePath) {
        String formPath = intent.getStringExtra(FormEntryActivity.KEY_FORMPATH);
        Intent i = null;

        // Create the local task and get the task id as a string to pass to the form entry activity
        Time t = new Time();
        t.setToNow();
        long tid = -1;
        try {
            TaskAssignment ta = new TaskAssignment();
            /*
             * TODO Implement
            task.scheduledStart = t.toMillis(false);
            task.status = "open";
            task.taskForm = STFileUtils.getFileName(formPath);
            tid = mTda.createTask(-1, "local", null, task);
            */
        } catch (Exception e) {
            e.printStackTrace(); // TODO handle exception
        }

        i = new Intent("org.smap.smapTask.android.action.FormEntry");
        i.putExtra(FormEntryActivity.KEY_FORMPATH, formPath);
        i.putExtra(FormEntryActivity.KEY_TASK, tid);
        if (instancePath != null) {
            i.putExtra(FormEntryActivity.KEY_INSTANCEPATH, instancePath);
        }
        startActivity(i);
    }

    @Override
    public void elementLeft(OnMapElement element) {
        currentPlace = null;
    }

    @Override
    public void elementEntered(OnMapElement element) {

        if (element != null && element instanceof Place) {
            currentPlace = (Place) element;

            if (routingEnabled) {
                placeSelected(element);
            } else {
                openContextMenu(mMapView); // No routing lets just look at the menu
            }
        }
    }

    @Override
    public void elementClicked(OnMapElement element) {

        if (element != null && element instanceof Place) {
            currentPlace = (Place) element;
            longClickOnMap(mMapView);
        }
    }

    /*
     * Calculate the route to the selected place 
     */
    private void placeSelected(OnMapElement element) {

        if (element != null && element instanceof Place) {

            // Calculate a new route to this assignment
            if (!routingInprogress && userPlace != null) {
                calculateNewRoute();
            }

            // Center map
            //mapComponent.loosePlaceFocus();
            //if(startCoordinates != null) {
            //   System.out.println("Setting bounding box");
            //   mapComponent.setBoundingBox(new WgsBoundingBox(startCoordinates, endCoordinates));
            //} else {
            //   mapComponent.focusOnPlace(selectedPlace, true);
            //}
        }
    }

    class RoutingHandler implements DirectionsWaiter {

        public static final int ROUTING_CLOUDMADE = 1;
        public static final int ROUTING_YOURNAVIGATION = 2;

        private void clearLines() {
            for (Line line : routeLines) {
                mapComponent.removeLine(line);
            }
            routeLines.clear();
        }

        private void addLine(Line line) {
            WgsPoint[] points = line.getPoints();

            mapComponent.addLine(line);
            routeLines.add(line);
        }

        @Override
        public void networkError() {
            Log.error("networkError:" + getString(R.string.smap_routing_network_error));
            routingInprogress = false;
        }

        @Override
        public void routeFound(Route route) {

            clearLines();
            // add route as line to map
            Line routeLine = route.getRouteLine();
            int[] lineColors = { 0xFF0000FF, 0xFF00FF00 }; // 0 - blue, 1 - green

            routeLine.setStyle(new LineStyle(lineColors[0], 4)); // set non-default style for the route

            addLine(routeLine);

            final RouteInstruction[] instructions = route.getInstructions();
            if (instructions.length > 0) {
                Place[] instructionPlaces = new Place[instructions.length];

                for (int i = 0; i < instructions.length; i++) {
                    instructionPlaces[i] = new Place(i, instructions[i].getInstruction(),
                            icons[instructions[i].getInstructionType()], instructions[i].getPoint());
                }

                mapComponent.addPlaces(instructionPlaces);
                //if(instructionPlaces[0] != null){
                //   mapComponent.setMiddlePoint(instructionPlaces[0].getWgs()); 
                //}
            }
            routingInprogress = false;
        }

        @Override
        public void routingErrors(int errorCodes) {
            final StringBuffer errors = new StringBuffer("Errors: ");
            if ((errorCodes & DirectionsService.ERROR_DESTINATION_ADDRESS_NOT_FOUND) != 0) {
                errors.append(getString(R.string.smap_destination_not_found));
            }
            if ((errorCodes & DirectionsService.ERROR_FROM_ADDRESS_NOT_FOUND) != 0) {
                errors.append(getString(R.string.smap_destination_not_found));
            }
            if ((errorCodes & DirectionsService.ERROR_FROM_AND_DESTINATION_ADDRESS_SAME) != 0) {
                errors.append(getString(R.string.smap_destination_not_found));
            }
            if ((errorCodes & DirectionsService.ERROR_ROUTE_NOT_FOUND) != 0) {
                errors.append(getString(R.string.smap_destination_not_found));
            }
            Log.error("routingErrors:" + errors.toString());
            routingInprogress = false;

        }

        @Override
        public void routingParsingError(String arg0) {
            Log.error("routingParsingError:" + getString(R.string.smap_route_parse_error) + " - " + arg0);
            routingInprogress = false;
        }

    }

    class RoutingThread implements Runnable {
        private RoutingHandler routingListener;
        private int routingService;

        public RoutingThread(RoutingHandler routingListener, int routingService) {
            this.routingListener = routingListener;
            this.routingService = routingService;
        }

        public void run() {
            if (currentPlace != null) {
                switch (routingService) {
                case RoutingHandler.ROUTING_CLOUDMADE:
                    //new CloudMadeDirections(nutiteqRouteWaiter,nutiteqRouteWaiter.getStartCoordinates(), nutiteqRouteWaiter.getEndCoordinates(), "Foot",CloudMadeDirections.ROUTE_TYPE_MODIFIER_SHORTEST ,"222c0ceb31794934a888ed9403a005d8",userId).execute();
                    break;
                case RoutingHandler.ROUTING_YOURNAVIGATION:
                    new YourNavigationDirections(routingListener, userPlace.getWgs(), currentPlace.getWgs(),
                            YourNavigationDirections.MOVE_METHOD_CAR, YourNavigationDirections.ROUTE_TYPE_FASTEST)
                                    .execute();
                    break;

                }
            }
        }
    }

    /*
      * Handle a long click on the map
      */
    protected boolean longClickOnMap(View v) {

        Intent i = new Intent(getApplicationContext(),
                org.smap.smapTask.android.activities.TaskAddressActivity.class);
        long taskId = currentPlace.getId();
        i.putExtra("id", taskId);
        if (userPlace != null) {
            WgsPoint userLocation = userPlace.getWgs();
            i.putExtra("lon", String.valueOf(userLocation.getLon()));
            i.putExtra("lat", String.valueOf(userLocation.getLat()));
        }

        startActivity(i);
        return true;
    }

    /*
     * ====================================================================================
     * Functions that should be shared with MainListActivity
     * Copy and paste from MainListActivity any tweaking should be noted in the comments before
     *  each function.
     */

    /*
     * (non-Javadoc)
     * @see android.app.Activity#onCreateContextMenu(android.view.ContextMenu, android.view.View, android.view.ContextMenu.ContextMenuInfo)
     * Changes:
     *    1) Task id is obtained from current element instead of context menu
     */
    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo);

        if (currentPlace != null) {
            int taskId = currentPlace.getId(); // Use currentPlace to get task Id
            Cursor c = null;
            fda = new FileDbAdapter();
            try {
                fda.open();
                c = fda.fetchTaskForId((long) taskId);
                String taskStatus = c.getString(c.getColumnIndex(FileDbAdapter.KEY_T_STATUS));
                String taskTitle = c.getString(c.getColumnIndex(FileDbAdapter.KEY_T_TITLE));
                menu.setHeaderTitle(taskTitle);
                if (fda.canReject(taskStatus)) {
                    menu.add(0, R.id.reject, 0, R.string.smap_reject_task);
                }
                if (fda.canComplete(c.getString(c.getColumnIndex(FileDbAdapter.KEY_T_STATUS)))) {
                    menu.add(0, R.id.complete_task, 0, R.string.smap_complete_task);
                }
                if (fda.canAccept(taskStatus)) {
                    menu.add(0, R.id.accept_task, 0, R.string.smap_accept_task);
                }
                menu.add(0, R.id.cancel_task, 0, R.string.cancel);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (fda != null) {
                    fda.close();
                }
                if (c != null) {
                    c.close();
                }
            }
        } else {

        }
    }

    /*
     * (non-Javadoc)
     * @see android.app.Activity#onContextItemSelected(android.view.MenuItem)
     * Changes:
     *   1) refreshList() replaced by refreshTaskOverlay()
     *   2) Task id is obtained from current element instead of context menu
     */
    @Override
    public boolean onContextItemSelected(MenuItem item) {

        int taskId = currentPlace.getId(); // Use currentPlace to get task Id
        Cursor c = null;
        switch (item.getItemId()) {

        case R.id.reject:
            try {
                fda = new FileDbAdapter();
                fda.open();
                c = fda.fetchTaskForId(taskId);
                String taskStatus = c.getString(c.getColumnIndex(FileDbAdapter.KEY_T_STATUS));
                if (fda.canReject(taskStatus)) {
                    fda.updateTaskStatus(taskId, fda.STATUS_T_REJECTED);
                } else {
                    Toast.makeText(getApplicationContext(), getString(R.string.smap_cannot_reject),
                            Toast.LENGTH_SHORT).show();
                }
                fda.close();
                refreshTaskOverlay();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (fda != null) {
                    fda.close();
                }
                if (c != null) {
                    c.close();
                }
            }
            return true;
        case R.id.complete_task:

            try {

                fda = new FileDbAdapter();
                fda.open();
                c = fda.fetchTaskForId(taskId);
                boolean canComplete = fda.canComplete(c.getString(c.getColumnIndex(FileDbAdapter.KEY_T_STATUS)));
                String taskForm = c.getString(c.getColumnIndex(FileDbAdapter.KEY_T_TASKFORM));
                String formPath = Collect.FORMS_PATH + taskForm;
                String instancePath = c.getString(c.getColumnIndex(FileDbAdapter.KEY_T_INSTANCE));
                fda.close();

                if (canComplete) {
                    completeTask(instancePath, formPath, taskId);
                } else {
                    Toast.makeText(getApplicationContext(), getString(R.string.smap_cannot_complete),
                            Toast.LENGTH_SHORT).show();
                }

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (fda != null) {
                    fda.close();
                }
                if (c != null) {
                    c.close();
                }
            }

            return true;
        case R.id.accept_task:

            try {
                fda = new FileDbAdapter();
                fda.open();
                c = fda.fetchTaskForId(taskId);
                String taskStatus = c.getString(c.getColumnIndex(FileDbAdapter.KEY_T_STATUS));
                if (fda.canAccept(taskStatus)) {
                    fda.updateTaskStatus(taskId, fda.STATUS_T_ACCEPTED);
                } else {
                    Toast.makeText(getApplicationContext(), getString(R.string.smap_cannot_accept),
                            Toast.LENGTH_SHORT).show();
                }
                fda.close();
                refreshTaskOverlay();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (fda != null) {
                    fda.close();
                }
                if (c != null) {
                    c.close();
                }
            }

            return true;
        default:
            return super.onContextItemSelected(item);
        }
    }

    public void completeTask(String instancePath, String formPath, long taskId) {

        // set the adhoc location
        try {
            fda = new FileDbAdapter();
            fda.open();
            if (userPlace != null) {
                WgsPoint userLocation = userPlace.getWgs();
                fda.updateAdhocLocation(taskId, String.valueOf(userLocation.getLon()),
                        String.valueOf(userLocation.getLat()));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (fda != null) {
                fda.close();
            }
        }

        // Get the provider URI of the instance 
        String where = InstanceColumns.INSTANCE_FILE_PATH + "=?";
        String[] whereArgs = { instancePath };
        Cursor cInstanceProvider = managedQuery(InstanceColumns.CONTENT_URI, null, where, whereArgs, null);
        if (cInstanceProvider.getCount() != 1) {

        } else {
            cInstanceProvider.moveToFirst();
            Uri instanceUri = ContentUris.withAppendedId(InstanceColumns.CONTENT_URI,
                    cInstanceProvider.getLong(cInstanceProvider.getColumnIndex(InstanceColumns._ID)));
            // Start activity to complete form
            Intent i = new Intent(Intent.ACTION_EDIT, instanceUri);

            i.putExtra(FormEntryActivity.KEY_FORMPATH, formPath); // TODO Don't think this is needed
            i.putExtra(FormEntryActivity.KEY_TASK, taskId);
            if (instancePath != null) { // TODO Don't think this is needed
                i.putExtra(FormEntryActivity.KEY_INSTANCEPATH, instancePath);
            }
            startActivity(i);
        }
        cInstanceProvider.close();
    }

    // Get the task source
    private String getSource() {
        SharedPreferences settings = PreferenceManager
                .getDefaultSharedPreferences(Collect.getInstance().getBaseContext());
        String serverUrl = settings.getString(PreferencesActivity.KEY_SERVER_URL, null);
        String source = null;
        // Remove the protocol
        if (serverUrl.startsWith("http")) {
            int idx = serverUrl.indexOf("//");
            if (idx > 0) {
                source = serverUrl.substring(idx + 2);
            } else {
                source = serverUrl;
            }
        }

        return source;
    }

}