nl.sogeti.android.gpstracker.viewer.LoggerMap.java Source code

Java tutorial

Introduction

Here is the source code for nl.sogeti.android.gpstracker.viewer.LoggerMap.java

Source

/*------------------------------------------------------------------------------
 **     Ident: Sogeti Smart Mobile Solutions
 **    Author: rene
 ** Copyright: (c) Apr 24, 2011 Sogeti Nederland B.V. All Rights Reserved.
 **------------------------------------------------------------------------------
 ** Sogeti Nederland B.V.            |  No part of this file may be reproduced  
 ** Distributed Software Engineering |  or transmitted in any form or by any        
 ** Lange Dreef 17                   |  means, electronic or mechanical, for the      
 ** 4131 NJ Vianen                   |  purpose, without the express written    
 ** The Netherlands                  |  permission of the copyright holder.
 *------------------------------------------------------------------------------
 *
 *   This file is part of OpenGPSTracker.
 *
 *   OpenGPSTracker is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   OpenGPSTracker is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with OpenGPSTracker.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package nl.sogeti.android.gpstracker.viewer;

import android.app.Dialog;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.database.ContentObserver;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.location.Location;
import android.location.LocationManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.preference.PreferenceManager;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AlertDialog.Builder;
import android.support.v7.widget.Toolbar;
import android.view.ContextMenu;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.Gallery;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;

import java.util.List;
import java.util.concurrent.Semaphore;

import nl.sogeti.android.gpstracker.R;
import nl.sogeti.android.gpstracker.actions.ControlTracking;
import nl.sogeti.android.gpstracker.actions.InsertNote;
import nl.sogeti.android.gpstracker.actions.ShareTrack;
import nl.sogeti.android.gpstracker.actions.Statistics;
import nl.sogeti.android.gpstracker.integration.ExternalConstants;
import nl.sogeti.android.gpstracker.integration.GPSLoggerServiceManager;
import nl.sogeti.android.gpstracker.service.db.GPStracking.Media;
import nl.sogeti.android.gpstracker.service.db.GPStracking.Segments;
import nl.sogeti.android.gpstracker.service.db.GPStracking.Tracks;
import nl.sogeti.android.gpstracker.service.db.GPStracking.Waypoints;
import nl.sogeti.android.gpstracker.service.logger.GPSLoggerService;
import nl.sogeti.android.gpstracker.settings.SettingsActivity;
import nl.sogeti.android.gpstracker.support.AppCompatMapActivity;
import nl.sogeti.android.gpstracker.util.Constants;
import nl.sogeti.android.gpstracker.util.UnitsI18n;
import nl.sogeti.android.log.Log;

/**
 * Main activity showing a track and allowing logging control
 *
 * @author rene (c) Jan 18, 2009, Sogeti B.V.
 * @version $Id$
 */
public class LoggerMap extends AppCompatMapActivity {

    private static final String INSTANCE_E6LONG = "e6long";
    private static final String INSTANCE_E6LAT = "e6lat";
    private static final String INSTANCE_ZOOM = "zoom";
    private static final String INSTANCE_SPEED = "averagespeed";
    private static final String INSTANCE_TRACK = "track";
    private static final int ZOOM_LEVEL = 16;
    // MENU'S
    private static final int MENU_TRACKING = 15;
    private static final int MENU_SETTINGS = 1;
    private static final int MENU_TRACKLIST = 3;
    private static final int MENU_STATS = 4;
    private static final int MENU_LAYERS = 6;
    private static final int MENU_NOTE = 7;
    private static final int MENU_SHARE = 13;
    private static final int MENU_CONTRIB = 14;
    private static final int DIALOG_NOTRACK = 24;
    private static final int DIALOG_LAYERS = 31;
    private static final int DIALOG_URIS = 34;
    private static final int DIALOG_CONTRIB = 35;
    private static final String WAKELOCK_TAG = "LoggerMap";
    // UI's
    private CheckBox mTraffic;
    private CheckBox mSpeed;
    private CheckBox mAltitude;
    private CheckBox mDistance;
    private CheckBox mCompass;
    private CheckBox mLocation;
    private TextView[] mSpeedtexts = new TextView[0];
    private TextView mLastGPSSpeedView = null;
    private TextView mLastGPSAltitudeView = null;
    private TextView mDistanceView = null;
    private Gallery mGallery;

    private double mAverageSpeed = 33.33d / 3d;
    private long mTrackId = -1;
    private long mLastSegment = -1;
    private UnitsI18n mUnits;
    private WakeLock mWakeLock = null;
    private SharedPreferences mSharedPreferences;
    private GPSLoggerServiceManager mLoggerServiceManager;
    private SegmentOverlay mLastSegmentOverlay;
    private BaseAdapter mMediaAdapter;

    private MapView mMapView = null;
    private FixedMyLocationOverlay mMylocation;
    private Handler mHandler;

    private ContentObserver mTrackSegmentsObserver;
    private ContentObserver mSegmentWaypointsObserver;
    private ContentObserver mTrackMediasObserver;
    private DialogInterface.OnClickListener mNoTrackDialogListener;
    private OnClickListener mNoteSelectDialogListener;
    private OnCheckedChangeListener mCheckedChangeListener;
    private android.widget.RadioGroup.OnCheckedChangeListener mGroupCheckedChangeListener;
    private OnSharedPreferenceChangeListener mSharedPreferenceChangeListener;
    private UnitsI18n.UnitsChangeListener mUnitsChangeListener;

    /**
     * Run after the ServiceManager completes the binding to the remote service
     */
    private Runnable mServiceConnected;
    private Runnable speedCalculator;
    private FloatingActionButton mFab;

    /**
     * Called when the activity is first created.
     */
    @Override
    protected void onCreate(Bundle load) {
        try {
            super.onCreate(load);
        } catch (NullPointerException exceptions) {
            // Unrecoverable Google Maps V1 crash
            Toast.makeText(this, R.string.error_map_nullpointer, Toast.LENGTH_LONG);
        }
        setContentView(R.layout.map);
        Toolbar toolbar = (Toolbar) findViewById(R.id.support_actionbar);
        setSupportActionBar(toolbar);

        findViewById(R.id.mapScreen).setDrawingCacheEnabled(true);
        mUnits = new UnitsI18n(this);
        mLoggerServiceManager = new GPSLoggerServiceManager();

        final Semaphore calulatorSemaphore = new Semaphore(0);
        Thread calulator = new Thread("OverlayCalculator") {
            @Override
            public void run() {
                Looper.prepare();
                mHandler = new Handler();
                calulatorSemaphore.release();
                Looper.loop();
            }
        };
        calulator.start();
        try {
            calulatorSemaphore.acquire();
        } catch (InterruptedException e) {
            Log.e(this, "Failed waiting for a semaphore", e);
        }
        mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
        mMapView = (MapView) findViewById(R.id.myMapView);
        setupHardwareAcceleration();
        mMylocation = new FixedMyLocationOverlay(this, mMapView);
        mMapView.setBuiltInZoomControls(true);
        mMapView.setClickable(true);

        TextView[] speeds = { (TextView) findViewById(R.id.speedview05), (TextView) findViewById(R.id.speedview04),
                (TextView) findViewById(R.id.speedview03), (TextView) findViewById(R.id.speedview02),
                (TextView) findViewById(R.id.speedview01), (TextView) findViewById(R.id.speedview00) };
        mSpeedtexts = speeds;
        mLastGPSSpeedView = (TextView) findViewById(R.id.currentSpeed);
        mLastGPSAltitudeView = (TextView) findViewById(R.id.currentAltitude);
        mDistanceView = (TextView) findViewById(R.id.currentDistance);
        mFab = (FloatingActionButton) findViewById(R.id.tracking_control);
        createListeners();

        if (load != null) {
            onRestoreInstanceState(load);
        }
        if (getIntent() != null) {
            handleIntentData(getIntent());
        }
    }

    private void setupHardwareAcceleration() {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
            return;
        }

        if (isHardwareAccelerated()) {
            mMapView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
        } else {
            mMapView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        }
    }

    @Override
    public void onNewIntent(Intent newIntent) {
        setIntent(newIntent);
        handleIntentData(newIntent);
    }

    private void handleIntentData(Intent newIntent) {
        Uri data = newIntent.getData();
        if (data != null) {
            moveToTrack(Long.parseLong(data.getLastPathSegment()), true);
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        mLoggerServiceManager.startup(this, mServiceConnected);

        mSharedPreferences.registerOnSharedPreferenceChangeListener(mSharedPreferenceChangeListener);
        mUnits.setUnitsChangeListener(mUnitsChangeListener);
        updateTitleBar();
        updateBlankingBehavior();
        restoreMap();

        if (mTrackId >= 0) {
            ContentResolver resolver = this.getContentResolver();
            Uri trackUri = Uri.withAppendedPath(Tracks.CONTENT_URI, mTrackId + "/segments");
            Uri lastSegmentUri = Uri.withAppendedPath(Tracks.CONTENT_URI,
                    mTrackId + "/segments/" + mLastSegment + "/waypoints");
            Uri mediaUri = ContentUris.withAppendedId(Media.CONTENT_URI, mTrackId);

            resolver.unregisterContentObserver(this.mTrackSegmentsObserver);
            resolver.unregisterContentObserver(this.mSegmentWaypointsObserver);
            resolver.unregisterContentObserver(this.mTrackMediasObserver);
            resolver.registerContentObserver(trackUri, false, this.mTrackSegmentsObserver);
            resolver.registerContentObserver(lastSegmentUri, true, this.mSegmentWaypointsObserver);
            resolver.registerContentObserver(mediaUri, true, this.mTrackMediasObserver);
        }
        updateDataOverlays();

        updateSpeedColoring();
        updateSpeedDisplayVisibility();
        updateAltitudeDisplayVisibility();
        updateDistanceDisplayVisibility();
        updateCompassDisplayVisibility();
        updateLocationDisplayVisibility();

    }

    @Override
    protected void onPause() {
        if (this.mWakeLock != null && this.mWakeLock.isHeld()) {
            this.mWakeLock.release();
            Log.w(this, "onPause(): Released lock to keep screen on!");
        }
        ContentResolver resolver = this.getContentResolver();
        resolver.unregisterContentObserver(this.mTrackSegmentsObserver);
        resolver.unregisterContentObserver(this.mSegmentWaypointsObserver);
        resolver.unregisterContentObserver(this.mTrackMediasObserver);
        mSharedPreferences.unregisterOnSharedPreferenceChangeListener(this.mSharedPreferenceChangeListener);
        mUnits.setUnitsChangeListener(null);
        mMylocation.disableMyLocation();
        mMylocation.disableCompass();

        this.mLoggerServiceManager.shutdown(this);

        super.onPause();
    }

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

        mLastSegmentOverlay = null;
        mMapView.getOverlays().clear();
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                Looper.myLooper().quit();
            }
        });

        if (mWakeLock != null && mWakeLock.isHeld()) {
            mWakeLock.release();
            Log.w(this, "onDestroy(): Released lock to keep screen on!");
        }
        if (mLoggerServiceManager.getLoggingState() == ExternalConstants.STATE_STOPPED) {
            stopService(new Intent(this, GPSLoggerService.class));
        }
        mUnits = null;
    }

    @Override
    protected void onSaveInstanceState(Bundle save) {
        super.onSaveInstanceState(save);

        save.putLong(INSTANCE_TRACK, mTrackId);
        save.putDouble(INSTANCE_SPEED, mAverageSpeed);
        save.putInt(INSTANCE_ZOOM, this.mMapView.getZoomLevel());
        GeoPoint point = this.mMapView.getMapCenter();
        save.putInt(INSTANCE_E6LAT, point.getLatitudeE6());
        save.putInt(INSTANCE_E6LONG, point.getLongitudeE6());
    }

    @Override
    protected void onRestoreInstanceState(Bundle load) {
        super.onRestoreInstanceState(load);

        if (load != null && load.containsKey(INSTANCE_TRACK)) {
            mTrackId = load.getLong(INSTANCE_TRACK);
            mAverageSpeed = load.getDouble(INSTANCE_SPEED);
            mMapView.getController().setZoom(LoggerMap.ZOOM_LEVEL);
            GeoPoint storedPoint = new GeoPoint(load.getInt(INSTANCE_E6LAT), load.getInt(INSTANCE_E6LONG));
            mMapView.getController().setCenter(storedPoint);
        }
    }

    private void restoreMap() {
        Uri data = this.getIntent().getData();
        if (mTrackId > -1) {
            // 1st method: track from a previous instance of this
            moveToTrack(mTrackId, false);
        } else if (getIntent().getData() != null) {
            // 2nd method: track ordered to make
            long loadTrackId = Long.parseLong(data.getLastPathSegment());
            mAverageSpeed = 0.0;
            moveToTrack(loadTrackId, true);
        } else {
            // 3rd method: just try the last track
            moveToLastTrack();
        }
    }

    /**
     * (non-Javadoc)
     *
     * @see com.google.android.maps.MapActivity#isRouteDisplayed()
     */
    @Override
    protected boolean isRouteDisplayed() {
        return true;
    }

    /**
     * (non-Javadoc)
     *
     * @see com.google.android.maps.MapActivity#isLocationDisplayed()
     */
    @Override
    protected boolean isLocationDisplayed() {
        return mSharedPreferences.getBoolean(Constants.LOCATION, false)
                || mLoggerServiceManager.getLoggingState() == ExternalConstants.STATE_LOGGING;
    }

    private void updateTitleBar() {
        ContentResolver resolver = this.getContentResolver();
        Cursor trackCursor = null;
        try {
            trackCursor = resolver.query(ContentUris.withAppendedId(Tracks.CONTENT_URI, this.mTrackId),
                    new String[] { Tracks.NAME }, null, null, null);
            if (trackCursor != null && trackCursor.moveToLast()) {
                String trackName = trackCursor.getString(0);
                setTitle(trackName);
            }
        } finally {
            if (trackCursor != null) {
                trackCursor.close();
            }
        }
    }

    private void updateBlankingBehavior() {
        boolean disableblanking = mSharedPreferences.getBoolean(Constants.DISABLEBLANKING, false);
        boolean disabledimming = mSharedPreferences.getBoolean(Constants.DISABLEDIMMING, false);
        if (disableblanking) {
            if (mWakeLock == null) {
                PowerManager pm = (PowerManager) this.getSystemService(Context.POWER_SERVICE);
                if (disabledimming) {
                    mWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, WAKELOCK_TAG);
                } else {
                    mWakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, WAKELOCK_TAG);
                }
            }
            if (mLoggerServiceManager.getLoggingState() == ExternalConstants.STATE_LOGGING && !mWakeLock.isHeld()) {
                mWakeLock.acquire();
                Log.w(this, "Acquired lock to keep screen on!");
            }
        }
    }

    private void updateDataOverlays() {
        ContentResolver resolver = this.getContentResolver();
        Uri segmentsUri = Uri.withAppendedPath(Tracks.CONTENT_URI, this.mTrackId + "/segments");
        Cursor segmentsCursor = null;
        List<?> overlays = this.mMapView.getOverlays();
        int segmentOverlaysCount = 0;

        for (Object overlay : overlays) {
            if (overlay instanceof SegmentOverlay) {
                segmentOverlaysCount++;
            }
        }
        try {
            segmentsCursor = resolver.query(segmentsUri, new String[] { Segments._ID }, null, null, null);
            if (segmentsCursor != null && segmentsCursor.getCount() == segmentOverlaysCount) {
                //            Log.d( TAG, "Alignment of segments" );
            } else {
                createDataOverlays();
            }
        } finally {
            if (segmentsCursor != null) {
                segmentsCursor.close();
            }
        }
    }

    private boolean isHardwareAccelerated() {
        return false;
    }

    private void updateSpeedColoring() {
        int trackColoringMethod = Integer.valueOf(mSharedPreferences.getString(Constants.TRACKCOLORING, "3"))
                .intValue();
        View speedbar = findViewById(R.id.speedbar);

        if (trackColoringMethod == SegmentOverlay.DRAW_MEASURED
                || trackColoringMethod == SegmentOverlay.DRAW_CALCULATED) {
            // mAverageSpeed is set to 0 if unknown or to trigger an recalculation here
            if (mAverageSpeed == 0.0) {
                mHandler.removeCallbacks(speedCalculator);
                mHandler.post(speedCalculator);
            } else {
                drawSpeedTexts(mAverageSpeed);

                speedbar.setVisibility(View.VISIBLE);
                for (int i = 0; i < mSpeedtexts.length; i++) {
                    mSpeedtexts[i].setVisibility(View.VISIBLE);
                }
            }
        } else {
            speedbar.setVisibility(View.INVISIBLE);
            for (int i = 0; i < mSpeedtexts.length; i++) {
                mSpeedtexts[i].setVisibility(View.INVISIBLE);
            }
        }
        List<?> overlays = mMapView.getOverlays();
        for (Object overlay : overlays) {
            if (overlay instanceof SegmentOverlay) {
                ((SegmentOverlay) overlay).setTrackColoringMethod(trackColoringMethod, mAverageSpeed);
            }
        }
    }

    private void updateSpeedDisplayVisibility() {
        boolean showspeed = mSharedPreferences.getBoolean(Constants.SPEED, false);
        if (showspeed) {
            mLastGPSSpeedView.setVisibility(View.VISIBLE);
        } else {
            mLastGPSSpeedView.setVisibility(View.GONE);
        }
    }

    private void updateAltitudeDisplayVisibility() {
        boolean showaltitude = mSharedPreferences.getBoolean(Constants.ALTITUDE, false);
        if (showaltitude) {
            mLastGPSAltitudeView.setVisibility(View.VISIBLE);
        } else {
            mLastGPSAltitudeView.setVisibility(View.GONE);
        }
    }

    private void updateDistanceDisplayVisibility() {
        boolean showdistance = mSharedPreferences.getBoolean(Constants.DISTANCE, false);
        if (showdistance) {
            mDistanceView.setVisibility(View.VISIBLE);
        } else {
            mDistanceView.setVisibility(View.GONE);
        }
    }

    private void updateCompassDisplayVisibility() {
        boolean compass = mSharedPreferences.getBoolean(Constants.COMPASS, false);
        if (compass) {
            mMylocation.enableCompass();
        } else {
            mMylocation.disableCompass();
        }
    }

    private void updateLocationDisplayVisibility() {
        boolean location = mSharedPreferences.getBoolean(Constants.LOCATION, false);
        if (location) {
            mMylocation.enableMyLocation();
        } else {
            mMylocation.disableMyLocation();
        }
    }

    private void updateGoogleOverlays() {
        LoggerMap.this.mMapView.setSatellite(mSharedPreferences.getBoolean(Constants.SATELLITE, false));
        LoggerMap.this.mMapView.setTraffic(mSharedPreferences.getBoolean(Constants.TRAFFIC, false));
    }

    /**
     * For the current track identifier the route of that track is drawn by
     * adding a OverLay for each segments in the track
     */
    private void createDataOverlays() {
        mLastSegmentOverlay = null;
        resetOverlay();
        setupHardwareAcceleration();

        ContentResolver resolver = this.getContentResolver();
        Cursor segments = null;
        int trackColoringMethod = Integer.valueOf(mSharedPreferences.getString(Constants.TRACKCOLORING, "2"))
                .intValue();

        try {
            Uri segmentsUri = Uri.withAppendedPath(Tracks.CONTENT_URI, this.mTrackId + "/segments");
            segments = resolver.query(segmentsUri, new String[] { Segments._ID }, null, null, null);
            if (segments != null && segments.moveToFirst()) {
                do {
                    long segmentsId = segments.getLong(0);
                    Uri segmentUri = ContentUris.withAppendedId(segmentsUri, segmentsId);
                    SegmentOverlay segmentOverlay = new SegmentOverlay(this, segmentUri, trackColoringMethod,
                            mAverageSpeed, this.mMapView, mHandler);
                    mMapView.getOverlays().add(segmentOverlay);
                    mLastSegmentOverlay = segmentOverlay;
                    if (segments.isFirst()) {
                        segmentOverlay.addPlacement(SegmentOverlay.FIRST_SEGMENT);
                    }
                    if (segments.isLast()) {
                        segmentOverlay.addPlacement(SegmentOverlay.LAST_SEGMENT);
                    }
                    mLastSegment = segmentsId;
                } while (segments.moveToNext());
            }
        } finally {
            if (segments != null) {
                segments.close();
            }
        }

        Uri lastSegmentUri = Uri.withAppendedPath(Tracks.CONTENT_URI,
                mTrackId + "/segments/" + mLastSegment + "/waypoints");
        resolver.unregisterContentObserver(this.mSegmentWaypointsObserver);
        resolver.registerContentObserver(lastSegmentUri, false, this.mSegmentWaypointsObserver);
    }

    private void resetOverlay() {
        for (Overlay overlay : mMapView.getOverlays()) {
            if (overlay instanceof SegmentOverlay) {
                ((SegmentOverlay) overlay).closeResources();
            }
        }
        mMapView.getOverlays().clear();
        mMapView.getOverlays().add(mMylocation);
    }

    /**
     * @param avgSpeed avgSpeed in m/s
     */
    private void drawSpeedTexts(double avgSpeed) {
        UnitsI18n units = mUnits;
        if (units != null) {
            avgSpeed = units.conversionFromMetersPerSecond(avgSpeed);
            for (int i = 0; i < mSpeedtexts.length; i++) {
                mSpeedtexts[i].setVisibility(View.VISIBLE);
                double speed;
                if (mUnits.isUnitFlipped()) {
                    speed = ((avgSpeed * 2d) / 5d) * (mSpeedtexts.length - i - 1);
                } else {
                    speed = ((avgSpeed * 2d) / 5d) * i;
                }
                String speedText = units.formatSpeed(speed, false);
                mSpeedtexts[i].setText(speedText);
            }
        }
    }

    /**
     * Alter this to set a new track as current.
     *
     * @param trackId
     * @param center  center on the end of the track
     */
    private void moveToTrack(long trackId, boolean center) {
        Cursor track = null;
        try {
            ContentResolver resolver = this.getContentResolver();
            Uri trackUri = ContentUris.withAppendedId(Tracks.CONTENT_URI, trackId);
            track = resolver.query(trackUri, new String[] { Tracks.NAME }, null, null, null);
            if (track != null && track.moveToFirst()) {
                this.mTrackId = trackId;
                mLastSegment = -1;
                resolver.unregisterContentObserver(this.mTrackSegmentsObserver);
                resolver.unregisterContentObserver(this.mTrackMediasObserver);
                Uri tracksegmentsUri = Uri.withAppendedPath(Tracks.CONTENT_URI, trackId + "/segments");

                resolver.registerContentObserver(tracksegmentsUri, false, this.mTrackSegmentsObserver);
                resolver.registerContentObserver(Media.CONTENT_URI, true, this.mTrackMediasObserver);

                resetOverlay();

                updateTitleBar();
                updateDataOverlays();
                updateSpeedColoring();
                if (center) {
                    GeoPoint lastPoint = getLastTrackPoint();
                    this.mMapView.getController().animateTo(lastPoint);
                }
            }
        } finally {
            if (track != null) {
                track.close();
            }
        }
    }

    /**
     * Retrieve the last point of the current track
     */
    private GeoPoint getLastTrackPoint() {
        Cursor waypoint = null;
        GeoPoint lastPoint = null;
        // First try the service which might have a cached version
        Location lastLoc = mLoggerServiceManager.getLastWaypoint();
        if (lastLoc != null) {
            int microLatitude = (int) (lastLoc.getLatitude() * 1E6d);
            int microLongitude = (int) (lastLoc.getLongitude() * 1E6d);
            lastPoint = new GeoPoint(microLatitude, microLongitude);
        }

        // If nothing yet, try the content resolver and query the track
        if (lastPoint == null || lastPoint.getLatitudeE6() == 0 || lastPoint.getLongitudeE6() == 0) {
            try {
                ContentResolver resolver = this.getContentResolver();
                waypoint = resolver.query(Uri.withAppendedPath(Tracks.CONTENT_URI, mTrackId + "/waypoints"),
                        new String[] { Waypoints.LATITUDE, Waypoints.LONGITUDE,
                                "max(" + Waypoints.TABLE + "." + Waypoints._ID + ")" },
                        null, null, null);
                if (waypoint != null && waypoint.moveToLast()) {
                    int microLatitude = (int) (waypoint.getDouble(0) * 1E6d);
                    int microLongitude = (int) (waypoint.getDouble(1) * 1E6d);
                    lastPoint = new GeoPoint(microLatitude, microLongitude);
                }
            } finally {
                if (waypoint != null) {
                    waypoint.close();
                }
            }
        }

        // If nothing yet, try the last generally known location
        if (lastPoint == null || lastPoint.getLatitudeE6() == 0 || lastPoint.getLongitudeE6() == 0) {
            lastPoint = getLastKnowGeopointLocation();
        }
        return lastPoint;
    }

    /**
     * Get the last know position from the GPS provider and return that
     * information wrapped in a GeoPoint to which the Map can navigate.
     *
     * @return
     * @see GeoPoint
     */
    private GeoPoint getLastKnowGeopointLocation() {
        int microLatitude = 0;
        int microLongitude = 0;
        LocationManager locationManager = (LocationManager) this.getApplication()
                .getSystemService(Context.LOCATION_SERVICE);
        Location locationFine = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
        if (locationFine != null) {
            microLatitude = (int) (locationFine.getLatitude() * 1E6d);
            microLongitude = (int) (locationFine.getLongitude() * 1E6d);
        }
        if (locationFine == null || microLatitude == 0 || microLongitude == 0) {
            Location locationCoarse = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
            if (locationCoarse != null) {
                microLatitude = (int) (locationCoarse.getLatitude() * 1E6d);
                microLongitude = (int) (locationCoarse.getLongitude() * 1E6d);
            }
            if (locationCoarse == null || microLatitude == 0 || microLongitude == 0) {
                microLatitude = 51985105;
                microLongitude = 5106132;
            }
        }
        GeoPoint geoPoint = new GeoPoint(microLatitude, microLongitude);
        return geoPoint;
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        boolean propagate = true;
        switch (keyCode) {
        case KeyEvent.KEYCODE_T:
            propagate = this.mMapView.getController().zoomIn();
            break;
        case KeyEvent.KEYCODE_G:
            propagate = this.mMapView.getController().zoomOut();
            break;
        case KeyEvent.KEYCODE_S:
            setSatelliteOverlay(!this.mMapView.isSatellite());
            propagate = false;
            break;
        case KeyEvent.KEYCODE_A:
            setTrafficOverlay(!this.mMapView.isTraffic());
            propagate = false;
            break;
        case KeyEvent.KEYCODE_F:
            mAverageSpeed = 0.0;
            moveToTrack(this.mTrackId - 1, true);
            propagate = false;
            break;
        case KeyEvent.KEYCODE_H:
            mAverageSpeed = 0.0;
            moveToTrack(this.mTrackId + 1, true);
            propagate = false;
            break;
        default:
            propagate = super.onKeyDown(keyCode, event);
            break;
        }
        return propagate;
    }

    private void setSatelliteOverlay(boolean b) {
        Editor editor = mSharedPreferences.edit();
        editor.putBoolean(Constants.SATELLITE, b);
        editor.commit();
    }

    private void setTrafficOverlay(boolean b) {
        Editor editor = mSharedPreferences.edit();
        editor.putBoolean(Constants.TRAFFIC, b);
        editor.commit();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        boolean result = super.onCreateOptionsMenu(menu);
        MenuItem item;

        item = menu.add(ContextMenu.NONE, MENU_TRACKLIST, ContextMenu.NONE, R.string.menu_tracklist)
                .setIcon(R.drawable.ic_list_24dp).setAlphabeticShortcut('P');
        MenuItemCompat.setShowAsAction(item, MenuItem.SHOW_AS_ACTION_ALWAYS);

        item = menu.add(ContextMenu.NONE, MENU_STATS, ContextMenu.NONE, R.string.menu_statistics)
                .setIcon(R.drawable.ic_graph_24dp).setAlphabeticShortcut('S');
        MenuItemCompat.setShowAsAction(item, MenuItem.SHOW_AS_ACTION_ALWAYS);

        item = menu.add(ContextMenu.NONE, MENU_SHARE, ContextMenu.NONE, R.string.menu_shareTrack)
                .setIcon(R.drawable.ic_share_24dp).setAlphabeticShortcut('I');
        MenuItemCompat.setShowAsAction(item, MenuItem.SHOW_AS_ACTION_IF_ROOM);

        item = menu.add(ContextMenu.NONE, MENU_LAYERS, ContextMenu.NONE, R.string.menu_showLayers)
                .setIcon(R.drawable.ic_layers_24dp).setAlphabeticShortcut('L');
        MenuItemCompat.setShowAsAction(item, MenuItem.SHOW_AS_ACTION_IF_ROOM);

        item = menu.add(ContextMenu.NONE, MENU_NOTE, ContextMenu.NONE, R.string.menu_insertnote)
                .setIcon(R.drawable.ic_pin_drop_24dp);
        MenuItemCompat.setShowAsAction(item, MenuItem.SHOW_AS_ACTION_IF_ROOM);

        item = menu.add(ContextMenu.NONE, MENU_SETTINGS, ContextMenu.NONE, R.string.menu_settings)
                .setIcon(R.drawable.ic_menu_preferences).setAlphabeticShortcut('C');
        MenuItemCompat.setShowAsAction(item, MenuItem.SHOW_AS_ACTION_NEVER);

        return result;
    }

    /*
     * (non-Javadoc)
     * @see android.app.Activity#onPrepareOptionsMenu(android.view.Menu)
     */
    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        MenuItem noteMenu = menu.findItem(MENU_NOTE);
        noteMenu.setEnabled(mLoggerServiceManager.isMediaPrepared());

        return super.onPrepareOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        boolean handled = false;

        Uri trackUri;
        Intent intent;
        switch (item.getItemId()) {
        case MENU_LAYERS:
            showDialog(DIALOG_LAYERS);
            handled = true;
            break;
        case MENU_NOTE:
            intent = new Intent(this, InsertNote.class);
            startActivityForResult(intent, MENU_NOTE);
            handled = true;
            break;
        case MENU_SETTINGS:
            intent = new Intent(this, SettingsActivity.class);
            startActivity(intent);
            handled = true;
            break;
        case MENU_TRACKLIST:
            intent = new Intent(this, TrackList.class);
            intent.putExtra(Tracks._ID, this.mTrackId);
            startActivityForResult(intent, MENU_TRACKLIST);
            break;
        case MENU_STATS:
            if (this.mTrackId >= 0) {
                intent = new Intent(this, Statistics.class);
                trackUri = ContentUris.withAppendedId(Tracks.CONTENT_URI, mTrackId);
                intent.setData(trackUri);
                startActivity(intent);
                handled = true;
                break;
            } else {
                showDialog(DIALOG_NOTRACK);
            }
            handled = true;
            break;
        case MENU_SHARE:
            if (this.mTrackId >= 0) {
                intent = new Intent(Intent.ACTION_RUN);
                trackUri = ContentUris.withAppendedId(Tracks.CONTENT_URI, mTrackId);
                intent.setDataAndType(trackUri, Tracks.CONTENT_ITEM_TYPE);
                intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                Bitmap bm = findViewById(R.id.mapScreen).getDrawingCache();
                if (bm != null) {
                    Uri screenStreamUri = ShareTrack.storeScreenBitmap(bm);
                    intent.putExtra(Intent.EXTRA_STREAM, screenStreamUri);
                }
                startActivityForResult(Intent.createChooser(intent, getString(R.string.share_track)), MENU_SHARE);
            } else {
                showDialog(DIALOG_NOTRACK);
            }
            handled = true;
            break;
        case MENU_CONTRIB:
            showDialog(DIALOG_CONTRIB);
        default:
            handled = super.onOptionsItemSelected(item);
            break;
        }
        return handled;
    }

    @Override
    protected Dialog onCreateDialog(int id) {
        Dialog dialog = null;
        LayoutInflater factory = null;
        View view = null;
        Builder builder = null;
        switch (id) {
        case DIALOG_LAYERS:
            builder = new AlertDialog.Builder(this);
            factory = LayoutInflater.from(this);
            view = factory.inflate(R.layout.layerdialog, (ViewGroup) findViewById(android.R.id.content), false);

            mTraffic = (CheckBox) view.findViewById(R.id.layer_traffic);
            mSpeed = (CheckBox) view.findViewById(R.id.layer_speed);
            mAltitude = (CheckBox) view.findViewById(R.id.layer_altitude);
            mDistance = (CheckBox) view.findViewById(R.id.layer_distance);
            mCompass = (CheckBox) view.findViewById(R.id.layer_compass);
            mLocation = (CheckBox) view.findViewById(R.id.layer_location);

            ((RadioGroup) view.findViewById(R.id.google_backgrounds))
                    .setOnCheckedChangeListener(mGroupCheckedChangeListener);

            mTraffic.setOnCheckedChangeListener(mCheckedChangeListener);
            mSpeed.setOnCheckedChangeListener(mCheckedChangeListener);
            mAltitude.setOnCheckedChangeListener(mCheckedChangeListener);
            mDistance.setOnCheckedChangeListener(mCheckedChangeListener);
            mCompass.setOnCheckedChangeListener(mCheckedChangeListener);
            mLocation.setOnCheckedChangeListener(mCheckedChangeListener);

            builder.setTitle(R.string.dialog_layer_title).setIcon(android.R.drawable.ic_dialog_map)
                    .setPositiveButton(android.R.string.ok, null).setView(view);
            dialog = builder.create();
            return dialog;
        case DIALOG_NOTRACK:
            builder = new AlertDialog.Builder(this);
            builder.setTitle(R.string.dialog_notrack_title).setMessage(R.string.dialog_notrack_message)
                    .setIcon(android.R.drawable.ic_dialog_alert)
                    .setPositiveButton(R.string.btn_selecttrack, mNoTrackDialogListener)
                    .setNegativeButton(R.string.btn_cancel, null);
            dialog = builder.create();
            return dialog;
        case DIALOG_URIS:
            builder = new AlertDialog.Builder(this);
            factory = LayoutInflater.from(this);
            view = factory.inflate(R.layout.mediachooser, (ViewGroup) findViewById(android.R.id.content), false);
            mGallery = (Gallery) view.findViewById(R.id.gallery);
            builder.setTitle(R.string.dialog_select_media_title).setMessage(R.string.dialog_select_media_message)
                    .setIcon(android.R.drawable.ic_dialog_alert).setNegativeButton(R.string.btn_cancel, null)
                    .setPositiveButton(R.string.btn_okay, mNoteSelectDialogListener).setView(view);
            dialog = builder.create();
            return dialog;
        case DIALOG_CONTRIB:
            builder = new AlertDialog.Builder(this);
            factory = LayoutInflater.from(this);
            view = factory.inflate(R.layout.contrib, (ViewGroup) findViewById(android.R.id.content), false);
            TextView contribView = (TextView) view.findViewById(R.id.contrib_view);
            contribView.setText(R.string.dialog_contrib_message);
            builder.setTitle(R.string.dialog_contrib_title).setView(view)
                    .setIcon(android.R.drawable.ic_dialog_email).setPositiveButton(R.string.btn_okay, null);
            dialog = builder.create();
            return dialog;
        default:
            return super.onCreateDialog(id);
        }
    }

    /*
     * (non-Javadoc)
     * @see android.app.Activity#onPrepareDialog(int, android.app.Dialog)
     */
    @Override
    protected void onPrepareDialog(int id, Dialog dialog) {
        RadioButton satellite;
        RadioButton regular;
        switch (id) {
        case DIALOG_LAYERS:
            satellite = (RadioButton) dialog.findViewById(R.id.layer_google_satellite);
            regular = (RadioButton) dialog.findViewById(R.id.layer_google_regular);
            satellite.setChecked(mSharedPreferences.getBoolean(Constants.SATELLITE, false));
            regular.setChecked(!mSharedPreferences.getBoolean(Constants.SATELLITE, false));

            mTraffic.setChecked(mSharedPreferences.getBoolean(Constants.TRAFFIC, false));
            mSpeed.setChecked(mSharedPreferences.getBoolean(Constants.SPEED, false));
            mAltitude.setChecked(mSharedPreferences.getBoolean(Constants.ALTITUDE, false));
            mDistance.setChecked(mSharedPreferences.getBoolean(Constants.DISTANCE, false));
            mCompass.setChecked(mSharedPreferences.getBoolean(Constants.COMPASS, false));
            mLocation.setChecked(mSharedPreferences.getBoolean(Constants.LOCATION, false));
            break;
        case DIALOG_URIS:
            mGallery.setAdapter(mMediaAdapter);
        default:
            break;
        }
        super.onPrepareDialog(id, dialog);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
        super.onActivityResult(requestCode, resultCode, intent);
        long trackId;
        switch (requestCode) {
        case MENU_TRACKLIST:
            if (resultCode == RESULT_OK) {
                Uri trackUri = intent.getData();
                trackId = Long.parseLong(trackUri.getLastPathSegment());
                mAverageSpeed = 0.0;
                moveToTrack(trackId, true);
            }
            break;
        case MENU_TRACKING:
            if (resultCode == RESULT_OK) {
                trackId = mLoggerServiceManager.getTrackId();
                mAverageSpeed = 0.0;
                moveToTrack(trackId, true);
            }
            break;
        case MENU_SHARE:
            ShareTrack.clearScreenBitmap();
            break;
        default:
            Log.e(this, "Returned form unknown activity: " + requestCode);
            break;
        }
    }

    private void setSpeedOverlay(boolean b) {
        Editor editor = mSharedPreferences.edit();
        editor.putBoolean(Constants.SPEED, b);
        editor.commit();
    }

    private void setAltitudeOverlay(boolean b) {
        Editor editor = mSharedPreferences.edit();
        editor.putBoolean(Constants.ALTITUDE, b);
        editor.commit();
    }

    private void setDistanceOverlay(boolean b) {
        Editor editor = mSharedPreferences.edit();
        editor.putBoolean(Constants.DISTANCE, b);
        editor.commit();
    }

    private void setCompassOverlay(boolean b) {
        Editor editor = mSharedPreferences.edit();
        editor.putBoolean(Constants.COMPASS, b);
        editor.commit();
    }

    private void setLocationOverlay(boolean b) {
        Editor editor = mSharedPreferences.edit();
        editor.putBoolean(Constants.LOCATION, b);
        editor.commit();
    }

    private void createListeners() {
        mFab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(LoggerMap.this, ControlTracking.class);
                startActivityForResult(intent, MENU_TRACKING);
            }
        });
        /*******************************************************
         * 8 Runnable listener actions
         */
        speedCalculator = new Runnable() {
            @Override
            public void run() {
                double avgspeed = 0.0;
                ContentResolver resolver = LoggerMap.this.getContentResolver();
                Cursor waypointsCursor = null;
                try {
                    waypointsCursor = resolver.query(
                            Uri.withAppendedPath(Tracks.CONTENT_URI, LoggerMap.this.mTrackId + "/waypoints"),
                            new String[] { "avg(" + Waypoints.SPEED + ")", "max(" + Waypoints.SPEED + ")" }, null,
                            null, null);

                    if (waypointsCursor != null && waypointsCursor.moveToLast()) {
                        double average = waypointsCursor.getDouble(0);
                        double maxBasedAverage = waypointsCursor.getDouble(1) / 2;
                        avgspeed = Math.min(average, maxBasedAverage);
                    }
                    if (avgspeed < 2) {
                        avgspeed = 5.55d / 2;
                    }
                } finally {
                    if (waypointsCursor != null) {
                        waypointsCursor.close();
                    }
                }
                mAverageSpeed = avgspeed;
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        updateSpeedColoring();
                    }
                });
            }
        };
        mServiceConnected = new Runnable() {
            @Override
            public void run() {
                updateBlankingBehavior();
            }
        };
        /*******************************************************
         * 8 Various dialog listeners
         */
        mNoteSelectDialogListener = new DialogInterface.OnClickListener() {

            @Override
            public void onClick(DialogInterface dialog, int which) {
                Uri selected = (Uri) mGallery.getSelectedItem();
                SegmentOverlay.handleMedia(LoggerMap.this, selected);
            }
        };
        mGroupCheckedChangeListener = new android.widget.RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup group, int checkedId) {
                switch (checkedId) {
                case R.id.layer_google_satellite:
                    setSatelliteOverlay(true);
                    break;
                case R.id.layer_google_regular:
                    setSatelliteOverlay(false);
                    break;
                default:
                    break;
                }
            }
        };
        mCheckedChangeListener = new OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                int checkedId;
                checkedId = buttonView.getId();
                switch (checkedId) {
                case R.id.layer_traffic:
                    setTrafficOverlay(isChecked);
                    break;
                case R.id.layer_speed:
                    setSpeedOverlay(isChecked);
                    break;
                case R.id.layer_altitude:
                    setAltitudeOverlay(isChecked);
                    break;
                case R.id.layer_distance:
                    setDistanceOverlay(isChecked);
                    break;
                case R.id.layer_compass:
                    setCompassOverlay(isChecked);
                    break;
                case R.id.layer_location:
                    setLocationOverlay(isChecked);
                    break;
                default:
                    break;
                }
            }
        };
        mNoTrackDialogListener = new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                //            Log.d( TAG, "mNoTrackDialogListener" + which);
                Intent tracklistIntent = new Intent(LoggerMap.this, TrackList.class);
                tracklistIntent.putExtra(Tracks._ID, LoggerMap.this.mTrackId);
                startActivityForResult(tracklistIntent, MENU_TRACKLIST);
            }
        };
        /**
         * Listeners to events outside this mapview
         */
        mSharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
            @Override
            public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
                if (key.equals(Constants.TRACKCOLORING)) {
                    mAverageSpeed = 0.0;
                    updateSpeedColoring();
                } else if (key.equals(Constants.DISABLEBLANKING) || key.equals(Constants.DISABLEDIMMING)) {
                    updateBlankingBehavior();
                } else if (key.equals(Constants.SPEED)) {
                    updateSpeedDisplayVisibility();
                } else if (key.equals(Constants.ALTITUDE)) {
                    updateAltitudeDisplayVisibility();
                } else if (key.equals(Constants.DISTANCE)) {
                    updateDistanceDisplayVisibility();
                } else if (key.equals(Constants.COMPASS)) {
                    updateCompassDisplayVisibility();
                } else if (key.equals(Constants.TRAFFIC)) {
                    updateGoogleOverlays();
                } else if (key.equals(Constants.SATELLITE)) {
                    updateGoogleOverlays();
                } else if (key.equals(Constants.LOCATION)) {
                    updateLocationDisplayVisibility();
                }
            }
        };
        mTrackMediasObserver = new ContentObserver(new Handler()) {
            @Override
            public void onChange(boolean selfUpdate) {
                if (!selfUpdate) {
                    if (mLastSegmentOverlay != null) {
                        mLastSegmentOverlay.calculateMedia();
                        mMapView.postInvalidate();
                    }
                } else {
                    Log.w(this, "mTrackMediasObserver skipping change on " + mLastSegment);
                }
            }
        };
        mTrackSegmentsObserver = new ContentObserver(new Handler()) {
            @Override
            public void onChange(boolean selfUpdate) {
                if (!selfUpdate) {
                    LoggerMap.this.updateDataOverlays();
                } else {
                    Log.w(this, "mTrackSegmentsObserver skipping change on " + mLastSegment);
                }
            }
        };
        mSegmentWaypointsObserver = new ContentObserver(new Handler()) {
            @Override
            public void onChange(boolean selfUpdate) {
                if (!selfUpdate) {
                    LoggerMap.this.updateTrackNumbers();
                    if (mLastSegmentOverlay != null) {
                        moveActiveViewWindow();
                    } else {
                        Log.e(this, "Error the last segment changed but it is not on screen! " + mLastSegment);
                    }
                } else {
                    Log.w(this, "mSegmentWaypointsObserver skipping change on " + mLastSegment);
                }
            }
        };
        mUnitsChangeListener = new UnitsI18n.UnitsChangeListener() {
            @Override
            public void onUnitsChange() {
                mAverageSpeed = 0.0;
                updateTrackNumbers();
                updateSpeedColoring();
            }
        };
    }

    /**
     * Retrieves the numbers of the measured speed and altitude from the most
     * recent waypoint and updates UI components with this latest bit of
     * information.
     */
    private void updateTrackNumbers() {
        Location lastWaypoint = mLoggerServiceManager.getLastWaypoint();
        UnitsI18n units = mUnits;
        if (lastWaypoint != null && units != null) {
            // Speed number
            double speed = lastWaypoint.getSpeed();
            speed = units.conversionFromMetersPerSecond(speed);
            String speedText = units.formatSpeed(speed, false);
            mLastGPSSpeedView.setText(speedText);

            // Speed color bar and refrence numbers
            if (speed > 2 * mAverageSpeed) {
                mAverageSpeed = 0.0;
                updateSpeedColoring();
                mMapView.postInvalidate();
            }

            //Altitude number
            double altitude = lastWaypoint.getAltitude();
            altitude = units.conversionFromMeterToHeight(altitude);
            String altitudeText = String.format("%.0f %s", altitude, units.getHeightUnit());
            mLastGPSAltitudeView.setText(altitudeText);

            //Distance number
            double distance = units.conversionFromMeter(mLoggerServiceManager.getTrackedDistance());
            String distanceText = String.format("%.2f %s", distance, units.getDistanceUnit());
            mDistanceView.setText(distanceText);
        }
    }

    /**
     * Call when an overlay has recalulated and has new information to be redrawn
     */
    public void onDateOverlayChanged() {
        this.mMapView.postInvalidate();
    }

    private void moveActiveViewWindow() {
        GeoPoint lastPoint = getLastTrackPoint();
        if (lastPoint != null && mLoggerServiceManager.getLoggingState() == ExternalConstants.STATE_LOGGING) {
            Point out = new Point();
            this.mMapView.getProjection().toPixels(lastPoint, out);
            int height = this.mMapView.getHeight();
            int width = this.mMapView.getWidth();
            if (out.x < 0 || out.y < 0 || out.y > height || out.x > width) {

                this.mMapView.clearAnimation();
                this.mMapView.getController().setCenter(lastPoint);
                //            Log.d( TAG, "mMapView.setCenter()" );
            } else if (out.x < width / 4 || out.y < height / 4 || out.x > (width / 4) * 3
                    || out.y > (height / 4) * 3) {
                this.mMapView.clearAnimation();
                this.mMapView.getController().animateTo(lastPoint);
                //            Log.d( TAG, "mMapView.animateTo()" );
            }
        }
    }

    private void moveToLastTrack() {
        int trackId = -1;
        Cursor track = null;
        try {
            ContentResolver resolver = this.getContentResolver();
            track = resolver.query(Tracks.CONTENT_URI, new String[] { "max(" + Tracks._ID + ")", Tracks.NAME, },
                    null, null, null);
            if (track != null && track.moveToLast()) {
                trackId = track.getInt(0);
                mAverageSpeed = 0.0;
                moveToTrack(trackId, false);
            }
        } finally {
            if (track != null) {
                track.close();
            }
        }
    }

    /**
     * Enables a SegmentOverlay to call back to the MapActivity to show a dialog
     * with choices of media
     *
     * @param mediaAdapter
     */
    public void showDialog(BaseAdapter mediaAdapter) {
        mMediaAdapter = mediaAdapter;
        showDialog(LoggerMap.DIALOG_URIS);
    }
}