com.androzic.MapActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.androzic.MapActivity.java

Source

/*
 * Androzic - android navigation client that uses OziExplorer maps (ozf2, ozfx3).
 * Copyright (C) 2010-2013 Andrey Novikov <http://andreynovikov.info/>
 * 
 * This file is part of Androzic application.
 * 
 * Androzic 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.
 * 
 * Androzic 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 Androzic. If not, see <http://www.gnu.org/licenses/>.
 */

package com.androzic;

import java.io.File;
import java.io.Serializable;
import java.lang.ref.WeakReference;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.TimeZone;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import net.londatiga.android.ActionItem;
import net.londatiga.android.QuickAction3D;
import net.londatiga.android.QuickAction3D.OnActionItemClickListener;

import org.miscwidgets.interpolator.EasingType.Type;
import org.miscwidgets.interpolator.ExpoInterpolator;
import org.miscwidgets.widget.Panel;

import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.LightingColorFilter;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.location.Location;
import android.location.LocationManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.preference.PreferenceManager;
import android.support.v4.app.FragmentManager;
import android.text.ClipboardManager;
import android.util.Log;
import android.util.Pair;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.WindowManager;
import android.view.animation.AlphaAnimation;
import android.view.animation.AnimationSet;
import android.widget.ImageButton;
import android.widget.RelativeLayout;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;

import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
import com.actionbarsherlock.view.SubMenu;
import com.actionbarsherlock.view.Window;
import com.androzic.data.MapObject;
import com.androzic.data.Route;
import com.androzic.data.Track;
import com.androzic.data.Waypoint;
import com.androzic.data.WaypointSet;
import com.androzic.gp.R;
import com.androzic.location.ILocationListener;
import com.androzic.location.ILocationService;
import com.androzic.location.LocationService;
import com.androzic.map.MapInformation;
import com.androzic.navigation.NavigationService;
import com.androzic.overlay.AccuracyOverlay;
import com.androzic.overlay.CurrentTrackOverlay;
import com.androzic.overlay.DistanceOverlay;
import com.androzic.overlay.MapObjectsOverlay;
import com.androzic.overlay.NavigationOverlay;
import com.androzic.overlay.RouteOverlay;
import com.androzic.overlay.ScaleOverlay;
import com.androzic.overlay.TrackOverlay;
import com.androzic.overlay.WaypointsOverlay;
import com.androzic.route.RouteDetails;
import com.androzic.route.RouteEdit;
import com.androzic.route.RouteList;
import com.androzic.route.RouteStart;
import com.androzic.track.TrackExportDialog;
import com.androzic.track.TrackList;
import com.androzic.util.Astro;
import com.androzic.util.CoordinateParser;
import com.androzic.util.OziExplorerFiles;
import com.androzic.util.StringFormatter;
import com.androzic.waypoint.OnWaypointActionListener;
import com.androzic.waypoint.WaypointFileList;
import com.androzic.waypoint.WaypointInfo;
import com.androzic.waypoint.WaypointList;
import com.androzic.waypoint.WaypointProject;
import com.androzic.waypoint.WaypointProperties;

public class MapActivity extends SherlockFragmentActivity
        implements View.OnClickListener, OnSharedPreferenceChangeListener, OnWaypointActionListener,
        SeekBar.OnSeekBarChangeListener, Panel.OnPanelListener {
    private static final String TAG = "MapActivity";

    private static final int RESULT_MANAGE_WAYPOINTS = 0x200;
    private static final int RESULT_LOAD_WAYPOINTS = 0x300;
    private static final int RESULT_SAVE_WAYPOINT = 0x400;
    private static final int RESULT_LOAD_MAP = 0x500;
    private static final int RESULT_MANAGE_TRACKS = 0x600;
    private static final int RESULT_MANAGE_ROUTES = 0x900;
    private static final int RESULT_EDIT_ROUTE = 0x110;
    private static final int RESULT_LOAD_MAP_ATPOSITION = 0x120;
    private static final int RESULT_SAVE_WAYPOINTS = 0x140;

    private static final int qaAddWaypointToRoute = 1;
    private static final int qaNavigateToWaypoint = 2;
    private static final int qaNavigateToMapObject = 2;

    // main preferences
    protected String precisionFormat = "%.0f";
    protected double speedFactor;
    protected String speedAbbr;
    protected double elevationFactor;
    protected String elevationAbbr;
    protected int renderInterval;
    protected int magInterval;
    protected boolean autoDim;
    protected int dimInterval;
    protected int dimValue;
    protected int showDistance;
    protected boolean showAccuracy;
    protected boolean followOnLocation;
    protected int exitConfirmation;
    private boolean secondBack;
    private Toast backToast;

    private TextView coordinates;
    private TextView satInfo;

    private TextView waypointName;
    private TextView waypointExtra;
    private TextView routeName;
    private TextView routeExtra;

    private TextView distanceValue;
    private TextView distanceUnit;
    private TextView bearingValue;
    private TextView bearingUnit;
    private TextView turnValue;

    private TextView speedValue;
    private TextView speedUnit;
    private TextView trackValue;
    private TextView trackUnit;
    private TextView elevationValue;
    private TextView elevationUnit;
    private TextView xtkValue;
    private TextView xtkUnit;

    private TextView currentFile;
    private TextView mapZoom;

    protected SeekBar trackBar;
    protected TextView waitBar;
    protected MapView map;
    protected QuickAction3D wptQuickAction;
    protected QuickAction3D rteQuickAction;
    protected QuickAction3D mobQuickAction;
    private ViewGroup dimView;

    protected Androzic application;

    protected ExecutorService executorThread = Executors.newSingleThreadExecutor();
    private FinishHandler finishHandler;

    private int waypointSelected = -1;
    private int routeSelected = -1;
    private long mapObjectSelected = -1;

    private ILocationService locationService = null;
    public NavigationService navigationService = null;

    private Location lastKnownLocation;
    protected long lastRenderTime = 0;
    protected long lastDim = 0;
    protected long lastMagnetic = 0;
    private boolean lastGeoid = true;

    private boolean animationSet;
    private boolean isFullscreen;
    private boolean keepScreenOn;
    private String[] panelActions;
    private List<String> activeActions;
    LightingColorFilter disable = new LightingColorFilter(0xFFFFFFFF, 0xFF555555);

    protected boolean ready = false;
    private boolean restarting = false;

    /** Called when the activity is first created. */
    @SuppressLint("ShowToast")
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Log.e(TAG, "onCreate()");

        ready = false;
        isFullscreen = false;

        backToast = Toast.makeText(this, R.string.backQuit, Toast.LENGTH_SHORT);
        finishHandler = new FinishHandler(this);

        application = (Androzic) getApplication();

        // FIXME Should find a better place for this
        application.mapObjectsOverlay = new MapObjectsOverlay(this);

        // check if called after crash
        if (!application.mapsInited) {
            restarting = true;
            startActivity(new Intent(this, Splash.class)
                    .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK)
                    .putExtras(getIntent()));
            finish();
            return;
        }

        application.setMapActivity(this);

        SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
        setRequestedOrientation(Integer.parseInt(settings.getString(getString(R.string.pref_orientation), "-1")));
        settings.registerOnSharedPreferenceChangeListener(this);
        Resources resources = getResources();

        if (settings.getBoolean(getString(R.string.pref_hideactionbar),
                resources.getBoolean(R.bool.def_hideactionbar))) {
            requestWindowFeature(Window.FEATURE_NO_TITLE);
        }

        panelActions = getResources().getStringArray(R.array.panel_action_values);

        setContentView(R.layout.act_main);
        coordinates = (TextView) findViewById(R.id.coordinates);
        satInfo = (TextView) findViewById(R.id.sats);
        currentFile = (TextView) findViewById(R.id.currentfile);
        mapZoom = (TextView) findViewById(R.id.currentzoom);
        waypointName = (TextView) findViewById(R.id.waypointname);
        waypointExtra = (TextView) findViewById(R.id.waypointextra);
        routeName = (TextView) findViewById(R.id.routename);
        routeExtra = (TextView) findViewById(R.id.routeextra);
        speedValue = (TextView) findViewById(R.id.speed);
        speedUnit = (TextView) findViewById(R.id.speedunit);
        trackValue = (TextView) findViewById(R.id.track);
        trackUnit = (TextView) findViewById(R.id.trackunit);
        elevationValue = (TextView) findViewById(R.id.elevation);
        elevationUnit = (TextView) findViewById(R.id.elevationunit);
        distanceValue = (TextView) findViewById(R.id.distance);
        distanceUnit = (TextView) findViewById(R.id.distanceunit);
        xtkValue = (TextView) findViewById(R.id.xtk);
        xtkUnit = (TextView) findViewById(R.id.xtkunit);
        bearingValue = (TextView) findViewById(R.id.bearing);
        bearingUnit = (TextView) findViewById(R.id.bearingunit);
        turnValue = (TextView) findViewById(R.id.turn);
        trackBar = (SeekBar) findViewById(R.id.trackbar);
        waitBar = (TextView) findViewById(R.id.waitbar);
        map = (MapView) findViewById(R.id.mapview);

        // set button actions
        findViewById(R.id.zoomin).setOnClickListener(this);
        findViewById(R.id.zoomout).setOnClickListener(this);
        findViewById(R.id.nextmap).setOnClickListener(this);
        findViewById(R.id.prevmap).setOnClickListener(this);
        findViewById(R.id.maps).setOnClickListener(this);
        findViewById(R.id.waypoints).setOnClickListener(this);
        findViewById(R.id.info).setOnClickListener(this);
        findViewById(R.id.follow).setOnClickListener(this);
        findViewById(R.id.locate).setOnClickListener(this);
        findViewById(R.id.tracking).setOnClickListener(this);
        findViewById(R.id.expand).setOnClickListener(this);
        findViewById(R.id.finishedit).setOnClickListener(this);
        findViewById(R.id.addpoint).setOnClickListener(this);
        findViewById(R.id.insertpoint).setOnClickListener(this);
        findViewById(R.id.removepoint).setOnClickListener(this);
        findViewById(R.id.orderpoints).setOnClickListener(this);
        findViewById(R.id.finishtrackedit).setOnClickListener(this);
        findViewById(R.id.cutafter).setOnClickListener(this);
        findViewById(R.id.cutbefore).setOnClickListener(this);

        Panel panel = (Panel) findViewById(R.id.panel);
        panel.setOnPanelListener(this);
        panel.setInterpolator(new ExpoInterpolator(Type.OUT));

        wptQuickAction = new QuickAction3D(this, QuickAction3D.VERTICAL);
        wptQuickAction.addActionItem(new ActionItem(qaAddWaypointToRoute, getString(R.string.menu_addtoroute),
                resources.getDrawable(R.drawable.ic_action_add)));
        wptQuickAction.setOnActionItemClickListener(waypointActionItemClickListener);

        rteQuickAction = new QuickAction3D(this, QuickAction3D.VERTICAL);
        rteQuickAction.addActionItem(new ActionItem(qaNavigateToWaypoint, getString(R.string.menu_thisnavpoint),
                resources.getDrawable(R.drawable.ic_action_directions)));
        rteQuickAction.setOnActionItemClickListener(routeActionItemClickListener);

        mobQuickAction = new QuickAction3D(this, QuickAction3D.VERTICAL);
        mobQuickAction.addActionItem(new ActionItem(qaNavigateToMapObject, getString(R.string.menu_navigate),
                resources.getDrawable(R.drawable.ic_action_directions)));
        mobQuickAction.setOnActionItemClickListener(mapObjectActionItemClickListener);

        trackBar.setOnSeekBarChangeListener(this);

        map.initialize(application);

        dimView = new RelativeLayout(this);

        String navWpt = settings.getString(getString(R.string.nav_wpt), "");
        if (!"".equals(navWpt) && savedInstanceState == null) {
            Intent intent = new Intent(getApplicationContext(), NavigationService.class)
                    .setAction(NavigationService.NAVIGATE_MAPOBJECT);
            intent.putExtra(NavigationService.EXTRA_NAME, navWpt);
            intent.putExtra(NavigationService.EXTRA_LATITUDE,
                    (double) settings.getFloat(getString(R.string.nav_wpt_lat), 0));
            intent.putExtra(NavigationService.EXTRA_LONGITUDE,
                    (double) settings.getFloat(getString(R.string.nav_wpt_lon), 0));
            intent.putExtra(NavigationService.EXTRA_PROXIMITY, settings.getInt(getString(R.string.nav_wpt_prx), 0));
            startService(intent);
        }

        String navRoute = settings.getString(getString(R.string.nav_route), "");
        if (!"".equals(navRoute) && settings.getBoolean(getString(R.string.pref_navigation_loadlast),
                getResources().getBoolean(R.bool.def_navigation_loadlast)) && savedInstanceState == null) {
            int ndir = settings.getInt(getString(R.string.nav_route_dir), 0);
            int nwpt = settings.getInt(getString(R.string.nav_route_wpt), -1);
            try {
                int rt = -1;
                Route route = application.getRouteByFile(navRoute);
                if (route != null) {
                    route.show = true;
                    rt = application.getRouteIndex(route);
                } else {
                    File rtf = new File(navRoute);
                    // FIXME It's bad - it can be not a first route in a file
                    route = OziExplorerFiles.loadRoutesFromFile(rtf, application.charset).get(0);
                    rt = application.addRoute(route);
                }
                RouteOverlay newRoute = new RouteOverlay(this, route);
                application.routeOverlays.add(newRoute);
                startService(new Intent(this, NavigationService.class).setAction(NavigationService.NAVIGATE_ROUTE)
                        .putExtra(NavigationService.EXTRA_ROUTE_INDEX, rt)
                        .putExtra(NavigationService.EXTRA_ROUTE_DIRECTION, ndir)
                        .putExtra(NavigationService.EXTRA_ROUTE_START, nwpt));
            } catch (Exception e) {
                Log.e(TAG, "Failed to start navigation", e);
            }
        }

        // set activity preferences
        onSharedPreferenceChanged(settings, getString(R.string.pref_exit));
        onSharedPreferenceChanged(settings, getString(R.string.pref_unitprecision));
        // set map preferences
        onSharedPreferenceChanged(settings, getString(R.string.pref_mapadjacent));
        onSharedPreferenceChanged(settings, getString(R.string.pref_mapcropborder));
        onSharedPreferenceChanged(settings, getString(R.string.pref_mapdrawborder));
        onSharedPreferenceChanged(settings, getString(R.string.pref_cursorcolor));
        onSharedPreferenceChanged(settings, getString(R.string.pref_grid_mapshow));
        onSharedPreferenceChanged(settings, getString(R.string.pref_grid_usershow));
        onSharedPreferenceChanged(settings, getString(R.string.pref_grid_preference));
        onSharedPreferenceChanged(settings, getString(R.string.pref_panelactions));

        if (getIntent().getExtras() != null)
            onNewIntent(getIntent());

        ready = true;
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.e(TAG, "onStart()");
        ((ViewGroup) getWindow().getDecorView()).addView(dimView);
    }

    @Override
    protected void onNewIntent(Intent intent) {
        Log.e(TAG, "onNewIntent()");
        if (intent.hasExtra("launch")) {
            Serializable object = intent.getExtras().getSerializable("launch");
            if (Class.class.isInstance(object)) {
                Intent launch = new Intent(this, (Class<?>) object);
                launch.putExtras(intent);
                launch.removeExtra("launch");
                startActivity(launch);
            }
        } else if (intent.hasExtra("lat") && intent.hasExtra("lon")) {
            Androzic application = (Androzic) getApplication();
            application.ensureVisible(intent.getExtras().getDouble("lat"), intent.getExtras().getDouble("lon"));
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.e(TAG, "onResume()");

        SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
        Resources resources = getResources();

        // update some preferences
        int speedIdx = Integer.parseInt(settings.getString(getString(R.string.pref_unitspeed), "0"));
        speedFactor = Double.parseDouble(resources.getStringArray(R.array.speed_factors)[speedIdx]);
        speedAbbr = resources.getStringArray(R.array.speed_abbrs)[speedIdx];
        speedUnit.setText(speedAbbr);
        int distanceIdx = Integer.parseInt(settings.getString(getString(R.string.pref_unitdistance), "0"));
        StringFormatter.distanceFactor = Double
                .parseDouble(resources.getStringArray(R.array.distance_factors)[distanceIdx]);
        StringFormatter.distanceAbbr = resources.getStringArray(R.array.distance_abbrs)[distanceIdx];
        StringFormatter.distanceShortFactor = Double
                .parseDouble(resources.getStringArray(R.array.distance_factors_short)[distanceIdx]);
        StringFormatter.distanceShortAbbr = resources.getStringArray(R.array.distance_abbrs_short)[distanceIdx];
        int elevationIdx = Integer.parseInt(settings.getString(getString(R.string.pref_unitelevation), "0"));
        elevationFactor = Double.parseDouble(resources.getStringArray(R.array.elevation_factors)[elevationIdx]);
        elevationAbbr = resources.getStringArray(R.array.elevation_abbrs)[elevationIdx];
        elevationUnit.setText(elevationAbbr);
        application.angleType = Integer.parseInt(settings.getString(getString(R.string.pref_unitangle), "0"));
        trackUnit.setText((application.angleType == 0 ? "deg" : getString(R.string.degmag)));
        bearingUnit.setText((application.angleType == 0 ? "deg" : getString(R.string.degmag)));
        application.coordinateFormat = Integer
                .parseInt(settings.getString(getString(R.string.pref_unitcoordinate), "0"));
        application.sunriseType = Integer.parseInt(settings.getString(getString(R.string.pref_unitsunrise), "0"));

        renderInterval = settings.getInt(getString(R.string.pref_maprenderinterval),
                resources.getInteger(R.integer.def_maprenderinterval)) * 100;
        followOnLocation = settings.getBoolean(getString(R.string.pref_mapfollowonloc),
                resources.getBoolean(R.bool.def_mapfollowonloc));
        magInterval = resources.getInteger(R.integer.def_maginterval) * 1000;
        showDistance = Integer.parseInt(settings.getString(getString(R.string.pref_showdistance_int),
                getString(R.string.def_showdistance)));
        showAccuracy = settings.getBoolean(getString(R.string.pref_showaccuracy), true);
        autoDim = settings.getBoolean(getString(R.string.pref_mapdim), resources.getBoolean(R.bool.def_mapdim));
        dimInterval = settings.getInt(getString(R.string.pref_mapdiminterval),
                resources.getInteger(R.integer.def_mapdiminterval)) * 1000;
        dimValue = settings.getInt(getString(R.string.pref_mapdimvalue),
                resources.getInteger(R.integer.def_mapdimvalue));

        map.setHideOnDrag(settings.getBoolean(getString(R.string.pref_maphideondrag),
                resources.getBoolean(R.bool.def_maphideondrag)));
        map.setStrictUnfollow(!settings.getBoolean(getString(R.string.pref_unfollowontap),
                resources.getBoolean(R.bool.def_unfollowontap)));
        map.setLookAhead(
                settings.getInt(getString(R.string.pref_lookahead), resources.getInteger(R.integer.def_lookahead)));
        map.setBestMapEnabled(
                settings.getBoolean(getString(R.string.pref_mapbest), resources.getBoolean(R.bool.def_mapbest)));
        map.setBestMapInterval(settings.getInt(getString(R.string.pref_mapbestinterval),
                resources.getInteger(R.integer.def_mapbestinterval)) * 1000);
        map.setCursorVector(
                Integer.parseInt(settings.getString(getString(R.string.pref_cursorvector),
                        getString(R.string.def_cursorvector))),
                settings.getInt(getString(R.string.pref_cursorvectormlpr),
                        resources.getInteger(R.integer.def_cursorvectormlpr)));
        map.setProximity(Integer.parseInt(settings.getString(getString(R.string.pref_navigation_proximity),
                getString(R.string.def_navigation_proximity))));

        // prepare views
        customizeLayout(settings);
        findViewById(R.id.editroute).setVisibility(application.editingRoute != null ? View.VISIBLE : View.GONE);
        if (application.editingTrack != null) {
            startEditTrack(application.editingTrack);
        }
        updateGPSStatus();
        updateNavigationStatus();
        // prepare overlays
        updateOverlays(settings, false);

        if (settings.getBoolean(getString(R.string.ui_drawer_open), false)) {
            Panel panel = (Panel) findViewById(R.id.panel);
            panel.setOpen(true, false);
        }

        onSharedPreferenceChanged(settings, getString(R.string.pref_wakelock));
        map.setKeepScreenOn(keepScreenOn);

        // TODO move into application
        if (lastKnownLocation != null) {
            if (lastKnownLocation.getProvider().equals(LocationManager.GPS_PROVIDER)) {
                updateMovingInfo(lastKnownLocation, true);
                updateNavigationInfo();
                dimScreen(lastKnownLocation);
            } else if (lastKnownLocation.getProvider().equals(LocationManager.NETWORK_PROVIDER)) {
                dimScreen(lastKnownLocation);
            }
        }

        bindService(new Intent(this, LocationService.class), locationConnection, BIND_AUTO_CREATE);
        bindService(new Intent(this, NavigationService.class), navigationConnection, BIND_AUTO_CREATE);

        registerReceiver(broadcastReceiver, new IntentFilter(NavigationService.BROADCAST_NAVIGATION_STATUS));
        registerReceiver(broadcastReceiver, new IntentFilter(NavigationService.BROADCAST_NAVIGATION_STATE));
        registerReceiver(broadcastReceiver, new IntentFilter(LocationService.BROADCAST_LOCATING_STATUS));
        registerReceiver(broadcastReceiver, new IntentFilter(LocationService.BROADCAST_TRACKING_STATUS));
        registerReceiver(broadcastReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));
        registerReceiver(broadcastReceiver, new IntentFilter(Intent.ACTION_SCREEN_ON));

        if (application.hasEnsureVisible()) {
            setFollowing(false);
            double[] loc = application.getEnsureVisible();
            application.setMapCenter(loc[0], loc[1], true, false);
            application.clearEnsureVisible();
        } else {
            application.updateLocationMaps(true, map.isBestMapEnabled());
        }
        updateMapViewArea();
        map.resume();
        map.updateMapInfo();
        map.update();
        map.requestFocus();
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.e(TAG, "onPause()");

        unregisterReceiver(broadcastReceiver);
        map.pause();

        // save active route
        Editor editor = PreferenceManager.getDefaultSharedPreferences(this).edit();
        editor.putString(getString(R.string.nav_route), "");
        editor.putString(getString(R.string.nav_wpt), "");
        if (navigationService != null) {
            if (navigationService.isNavigatingViaRoute()) {
                Route route = navigationService.navRoute;
                if (route.filepath != null) {
                    editor.putString(getString(R.string.nav_route), route.filepath);
                    editor.putInt(getString(R.string.nav_route_idx),
                            application.getRouteIndex(navigationService.navRoute));
                    editor.putInt(getString(R.string.nav_route_dir), navigationService.navDirection);
                    editor.putInt(getString(R.string.nav_route_wpt), navigationService.navCurrentRoutePoint);
                }
            } else if (navigationService.isNavigating()) {
                MapObject wpt = navigationService.navWaypoint;
                editor.putString(getString(R.string.nav_wpt), wpt.name);
                editor.putInt(getString(R.string.nav_wpt_prx), wpt.proximity);
                editor.putFloat(getString(R.string.nav_wpt_lat), (float) wpt.latitude);
                editor.putFloat(getString(R.string.nav_wpt_lon), (float) wpt.longitude);
            }
        }
        editor.commit();

        if (navigationService != null) {
            unbindService(navigationConnection);
            navigationService = null;
        }
        if (locationService != null) {
            locationService.unregisterLocationCallback(locationListener);
            locationService = null;
        }
        unbindService(locationConnection);
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.e(TAG, "onStop()");
        ((ViewGroup) getWindow().getDecorView()).removeView(dimView);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.e(TAG, "onDestroy()");
        ready = false;

        PreferenceManager.getDefaultSharedPreferences(this).unregisterOnSharedPreferenceChangeListener(this);

        if (isFinishing() && !restarting) {
            // clear all overlays from map
            updateOverlays(null, true);
            application.waypointsOverlay = null;
            application.navigationOverlay = null;
            application.distanceOverlay = null;

            application.clear();
        }

        restarting = false;

        application = null;

        map = null;

        coordinates = null;
        satInfo = null;
        currentFile = null;
        mapZoom = null;
        waypointName = null;
        waypointExtra = null;
        routeName = null;
        routeExtra = null;
        speedValue = null;
        speedUnit = null;
        trackValue = null;
        elevationValue = null;
        elevationUnit = null;
        distanceValue = null;
        distanceUnit = null;
        xtkValue = null;
        xtkUnit = null;
        bearingValue = null;
        turnValue = null;
        trackBar = null;
    }

    private ServiceConnection navigationConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            navigationService = ((NavigationService.LocalBinder) service).getService();
            runOnUiThread(new Runnable() {
                public void run() {
                    if (!ready)
                        return;
                    updateNavigationStatus();
                    updateNavigationInfo();
                }
            });
            Log.d(TAG, "Navigation service connected");
        }

        public void onServiceDisconnected(ComponentName className) {
            navigationService = null;
            Log.d(TAG, "Navigation service disconnected");
        }
    };

    private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            Log.e(TAG, "Broadcast: " + action);
            if (action.equals(NavigationService.BROADCAST_NAVIGATION_STATE)) {
                final int state = intent.getExtras().getInt("state");
                runOnUiThread(new Runnable() {
                    public void run() {
                        if (!ready)
                            return;
                        if (state == NavigationService.STATE_REACHED) {
                            Toast.makeText(getApplicationContext(), R.string.arrived, Toast.LENGTH_LONG).show();
                        }
                        updateNavigationStatus();
                    }
                });
            } else if (action.equals(NavigationService.BROADCAST_NAVIGATION_STATUS)) {
                runOnUiThread(new Runnable() {
                    public void run() {
                        if (!ready)
                            return;
                        updateNavigationInfo();
                    }
                });
            } else if (action.equals(LocationService.BROADCAST_TRACKING_STATUS)) {
                updateMapButtons();
            } else if (action.equals(LocationService.BROADCAST_LOCATING_STATUS)) {
                updateMapButtons();
                if (locationService != null && !locationService.isLocating())
                    map.clearLocation();
            }
            // In fact this is not needed on modern devices through activity is always
            // paused when the screen is turned off. But we will keep it, may be there
            // exist some devices (ROMs) that do not pause activities.
            else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
                map.pause();
            } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
                map.resume();
            }
        }
    };

    private ServiceConnection locationConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder binder) {
            locationService = (ILocationService) binder;
            locationService.registerLocationCallback(locationListener);
            Log.d(TAG, "Location service connected");
        }

        public void onServiceDisconnected(ComponentName className) {
            locationService = null;
            Log.d(TAG, "Location service disconnected");
        }
    };

    private ILocationListener locationListener = new ILocationListener() {
        @Override
        public void onGpsStatusChanged(String provider, final int status, final int fsats, final int tsats) {
            if (LocationManager.GPS_PROVIDER.equals(provider)) {
                runOnUiThread(new Runnable() {
                    public void run() {
                        if (!ready)
                            return;
                        switch (status) {
                        case LocationService.GPS_OK:
                            if (!map.isFixed()) {
                                satInfo.setTextColor(getResources().getColor(R.color.gpsworking));
                                map.setMoving(true);
                                map.setFixed(true);
                                updateGPSStatus();
                            }
                            satInfo.setText(String.valueOf(fsats) + "/" + String.valueOf(tsats));
                            break;
                        case LocationService.GPS_OFF:
                            satInfo.setText(R.string.sat_stop);
                            satInfo.setTextColor(getResources().getColor(R.color.gpsdisabled));
                            map.setMoving(false);
                            map.setFixed(false);
                            updateGPSStatus();
                            break;
                        case LocationService.GPS_SEARCHING:
                            if (map.isFixed()) {
                                satInfo.setTextColor(getResources().getColor(R.color.gpsenabled));
                                map.setFixed(false);
                            }
                            satInfo.setText(String.valueOf(fsats) + "/" + String.valueOf(tsats));
                            break;
                        }
                    }
                });
            }
        }

        @Override
        public void onLocationChanged(final Location location, final boolean continous, final boolean geoid,
                final float smoothspeed, final float avgspeed) {
            if (!ready)
                return;

            Log.d(TAG, "Location arrived");

            final long lastLocationMillis = location.getTime();

            boolean magnetic = false;
            if (application.angleType == 1 && lastLocationMillis - lastMagnetic >= magInterval) {
                magnetic = true;
                lastMagnetic = lastLocationMillis;
            }

            // update map
            if (lastLocationMillis - lastRenderTime >= renderInterval) {
                lastRenderTime = lastLocationMillis;

                application.setLocation(location, magnetic);
                map.setLocation(location);
                final boolean enableFollowing = followOnLocation && lastKnownLocation == null;

                lastKnownLocation = location;

                if (application.accuracyOverlay != null && location.hasAccuracy()) {
                    application.accuracyOverlay.setAccuracy(location.getAccuracy());
                }

                runOnUiThread(new Runnable() {
                    public void run() {
                        if (!LocationManager.GPS_PROVIDER.equals(location.getProvider()) && map.isMoving()) {
                            map.setMoving(false);
                            updateGPSStatus();
                        }
                        // Mock provider hack
                        if (!map.isFixed() && continous
                                && LocationManager.GPS_PROVIDER.equals(location.getProvider())) {
                            satInfo.setText(R.string.sat_start);
                            satInfo.setTextColor(getResources().getColor(R.color.gpsworking));
                            map.setMoving(true);
                            map.setFixed(true);
                            updateGPSStatus();
                        }

                        updateMovingInfo(location, geoid);

                        if (enableFollowing)
                            setFollowing(true);
                        else
                            map.update();

                        // auto dim
                        if (autoDim && dimInterval > 0 && lastLocationMillis - lastDim >= dimInterval) {
                            dimScreen(location);
                            lastDim = lastLocationMillis;
                        }
                    }
                });
            }
        }

        @Override
        public void onProviderChanged(String provider) {
        }

        @Override
        public void onProviderDisabled(String provider) {
            if (LocationManager.GPS_PROVIDER.equals(provider)) {
                runOnUiThread(new Runnable() {
                    public void run() {
                        if (!ready)
                            return;
                        satInfo.setText(R.string.sat_stop);
                        satInfo.setTextColor(getResources().getColor(R.color.gpsdisabled));
                        map.setMoving(false);
                        map.setFixed(false);
                        updateGPSStatus();
                    }
                });
            }
        }

        @Override
        public void onProviderEnabled(String provider) {
            if (LocationManager.GPS_PROVIDER.equals(provider)) {
                runOnUiThread(new Runnable() {
                    public void run() {
                        if (!ready)
                            return;
                        if (!map.isFixed()) {
                            satInfo.setText(R.string.sat_start);
                            satInfo.setTextColor(getResources().getColor(R.color.gpsenabled));
                        }
                    }
                });
            }
        }
    };

    private void updateMapViewArea() {
        final ViewTreeObserver vto = map.getViewTreeObserver();
        vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
            public void onGlobalLayout() {
                Rect area = new Rect();
                map.getLocalVisibleRect(area);
                View v = findViewById(R.id.topbar);
                if (v != null)
                    area.top = v.getBottom();
                v = findViewById(R.id.bottombar);
                if (v != null)
                    area.bottom = v.getTop();
                v = findViewById(R.id.rightbar);
                if (v != null)
                    area.right = v.getLeft();
                if (!area.isEmpty())
                    map.updateViewArea(area);
                if (vto.isAlive()) {
                    vto.removeGlobalOnLayoutListener(this);
                } else {
                    final ViewTreeObserver vto1 = map.getViewTreeObserver();
                    vto1.removeGlobalOnLayoutListener(this);
                }
            }
        });
    }

    public void updateMap() {
        if (map != null)
            map.postInvalidate();
    }

    private final void updateMapButtons() {
        ViewGroup container = (ViewGroup) findViewById(R.id.button_container);

        for (String action : panelActions) {
            int id = getResources().getIdentifier(action, "id", getPackageName());
            ImageButton aib = (ImageButton) container.findViewById(id);
            if (aib != null) {
                if (activeActions.contains(action)) {
                    aib.setVisibility(View.VISIBLE);
                    switch (id) {
                    case R.id.follow:
                        aib.setImageDrawable(getResources()
                                .getDrawable(map.isFollowing() ? R.drawable.cursor_drag_arrow : R.drawable.target));
                        break;
                    case R.id.locate:
                        boolean isLocating = locationService != null && locationService.isLocating();
                        aib.setImageDrawable(getResources()
                                .getDrawable(isLocating ? R.drawable.pin_map_no : R.drawable.pin_map));
                        break;
                    case R.id.tracking:
                        boolean isTracking = locationService != null && locationService.isTracking();
                        aib.setImageDrawable(getResources()
                                .getDrawable(isTracking ? R.drawable.doc_delete : R.drawable.doc_edit));
                        break;
                    }
                } else {
                    aib.setVisibility(View.GONE);
                }
            }
        }
    }

    protected final void updateCoordinates(final double latlon[]) {
        // TODO strange situation, needs investigation
        if (application != null) {
            final String pos = StringFormatter.coordinates(application.coordinateFormat, " ", latlon[0], latlon[1]);
            this.runOnUiThread(new Runnable() {

                @Override
                public void run() {
                    coordinates.setText(pos);
                }
            });
        }
    }

    protected final void updateFileInfo() {
        final String title = application.getMapTitle();
        this.runOnUiThread(new Runnable() {

            @Override
            public void run() {
                if (title != null) {
                    currentFile.setText(title);
                } else {
                    currentFile.setText("-no map-");
                }

                updateZoomInfo();
            }
        });
    }

    protected final void updateZoomInfo() {
        double zoom = application.getZoom() * 100;

        if (zoom == 0.0) {
            mapZoom.setText("---%");
        } else {
            int rz = (int) Math.floor(zoom);
            String zoomStr = zoom - rz != 0.0 ? String.format("%.1f", zoom) : String.valueOf(rz);
            mapZoom.setText(zoomStr + "%");
        }

        ImageButton zoomin = (ImageButton) findViewById(R.id.zoomin);
        ImageButton zoomout = (ImageButton) findViewById(R.id.zoomout);
        zoomin.setEnabled(application.getNextZoom() != 0.0);
        zoomout.setEnabled(application.getPrevZoom() != 0.0);

        LightingColorFilter disable = new LightingColorFilter(0xFFFFFFFF, 0xFF444444);

        zoomin.setColorFilter(zoomin.isEnabled() ? null : disable);
        zoomout.setColorFilter(zoomout.isEnabled() ? null : disable);
    }

    protected void updateGPSStatus() {
        int v = map.isMoving() && application.editingRoute == null && application.editingTrack == null
                ? View.VISIBLE
                : View.GONE;
        View view = findViewById(R.id.movinginfo);
        if (view.getVisibility() != v) {
            view.setVisibility(v);
            updateMapViewArea();
        }
    }

    protected void updateNavigationStatus() {
        boolean isNavigating = navigationService != null && navigationService.isNavigating();
        boolean isNavigatingViaRoute = isNavigating && navigationService.isNavigatingViaRoute();

        // waypoint panel
        findViewById(R.id.waypointinfo).setVisibility(isNavigating ? View.VISIBLE : View.GONE);
        // route panel
        findViewById(R.id.routeinfo).setVisibility(isNavigatingViaRoute ? View.VISIBLE : View.GONE);
        // distance
        distanceValue.setVisibility(isNavigating ? View.VISIBLE : View.GONE);
        findViewById(R.id.distancelt).setVisibility(isNavigating ? View.VISIBLE : View.GONE);
        // bearing
        bearingValue.setVisibility(isNavigating ? View.VISIBLE : View.GONE);
        findViewById(R.id.bearinglt).setVisibility(isNavigating ? View.VISIBLE : View.GONE);
        // turn
        turnValue.setVisibility(isNavigating ? View.VISIBLE : View.GONE);
        findViewById(R.id.turnlt).setVisibility(isNavigating ? View.VISIBLE : View.GONE);
        // xtk
        xtkValue.setVisibility(isNavigatingViaRoute ? View.VISIBLE : View.GONE);
        findViewById(R.id.xtklt).setVisibility(isNavigatingViaRoute ? View.VISIBLE : View.GONE);

        // we hide elevation in portrait mode due to lack of space
        if (getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE) {
            if (isNavigatingViaRoute && elevationValue.getVisibility() == View.VISIBLE) {
                elevationValue.setVisibility(View.GONE);
                findViewById(R.id.elevationlt).setVisibility(View.GONE);

                ViewGroup row = (ViewGroup) findViewById(R.id.movingrow);
                int pos = row.indexOfChild(elevationValue);
                View xtklt = findViewById(R.id.xtklt);
                row.removeView(xtkValue);
                row.removeView(xtklt);
                row.addView(xtklt, pos);
                row.addView(xtkValue, pos);
                row.getParent().requestLayout();
            } else if (!isNavigatingViaRoute && elevationValue.getVisibility() == View.GONE) {
                elevationValue.setVisibility(View.VISIBLE);
                findViewById(R.id.elevationlt).setVisibility(View.VISIBLE);

                ViewGroup row = (ViewGroup) findViewById(R.id.movingrow);
                int pos = row.indexOfChild(xtkValue);
                View elevationlt = findViewById(R.id.elevationlt);
                row.removeView(elevationValue);
                row.removeView(elevationlt);
                row.addView(elevationlt, pos);
                row.addView(elevationValue, pos);
                row.getParent().requestLayout();
            }
        }

        if (isNavigatingViaRoute) {
            routeName.setText(" " + navigationService.navRoute.name);
        }
        if (isNavigating) {
            waypointName.setText(" " + navigationService.navWaypoint.name);
            if (application.navigationOverlay == null) {
                application.navigationOverlay = new NavigationOverlay(this);
                application.navigationOverlay.onMapChanged();
            }
        } else if (application.navigationOverlay != null) {
            application.navigationOverlay.onBeforeDestroy();
            application.navigationOverlay = null;
        }

        updateMapViewArea();
        map.update();
    }

    protected void updateNavigationInfo() {
        if (navigationService == null || !navigationService.isNavigating())
            return;

        double distance = navigationService.navDistance;
        double bearing = navigationService.navBearing;
        long turn = navigationService.navTurn;
        double vmg = navigationService.navVMG * speedFactor;
        int ete = navigationService.navETE;

        String[] dist = StringFormatter.distanceC(distance, precisionFormat);
        String extra = String.format(precisionFormat, vmg) + " " + speedAbbr + " | " + StringFormatter.timeH(ete);

        String trnsym = "";
        if (turn > 0) {
            trnsym = "R";
        } else if (turn < 0) {
            trnsym = "L";
            turn = -turn;
        }

        bearing = application.fixDeclination(bearing);
        distanceValue.setText(dist[0]);
        distanceUnit.setText(dist[1]);
        bearingValue.setText(String.valueOf(Math.round(bearing)));
        turnValue.setText(String.valueOf(Math.round(turn)) + trnsym);
        waypointExtra.setText(extra);

        if (navigationService.isNavigatingViaRoute()) {
            boolean hasNext = navigationService.hasNextRouteWaypoint();
            if (distance < navigationService.navProximity * 3 && !animationSet) {
                AnimationSet animation = new AnimationSet(true);
                animation.addAnimation(new AlphaAnimation(1.0f, 0.3f));
                animation.addAnimation(new AlphaAnimation(0.3f, 1.0f));
                animation.setDuration(500);
                animation.setRepeatCount(10);
                findViewById(R.id.waypointinfo).startAnimation(animation);
                if (!hasNext) {
                    findViewById(R.id.routeinfo).startAnimation(animation);
                }
                animationSet = true;
            } else if (animationSet) {
                findViewById(R.id.waypointinfo).setAnimation(null);
                if (!hasNext) {
                    findViewById(R.id.routeinfo).setAnimation(null);
                }
                animationSet = false;
            }

            if (navigationService.navXTK == Double.NEGATIVE_INFINITY) {
                xtkValue.setText("--");
                xtkUnit.setText("--");
            } else {
                String xtksym = navigationService.navXTK == 0 ? "" : navigationService.navXTK > 0 ? "R" : "L";
                String[] xtks = StringFormatter.distanceC(Math.abs(navigationService.navXTK));
                xtkValue.setText(xtks[0] + xtksym);
                xtkUnit.setText(xtks[1]);
            }

            double navDistance = navigationService.navRouteDistanceLeft();
            int eta = navigationService.navRouteETE(navDistance);
            if (eta < Integer.MAX_VALUE)
                eta += navigationService.navETE;
            extra = StringFormatter.distanceH(navDistance + distance, 1000) + " | " + StringFormatter.timeH(eta);
            routeExtra.setText(extra);
        }
    }

    protected void updateMovingInfo(final Location location, final boolean geoid) {
        double s = location.getSpeed() * speedFactor;
        double e = location.getAltitude() * elevationFactor;
        double track = application.fixDeclination(location.getBearing());
        speedValue.setText(String.format(precisionFormat, s));
        trackValue.setText(String.valueOf(Math.round(track)));
        elevationValue.setText(String.valueOf(Math.round(e)));
        // TODO set separate color
        if (geoid != lastGeoid) {
            int color = geoid ? 0xffffffff : getResources().getColor(R.color.gpsenabled);
            elevationValue.setTextColor(color);
            elevationUnit.setTextColor(color);
            ((TextView) findViewById(R.id.elevationname)).setTextColor(color);
            lastGeoid = geoid;
        }
    }

    private final void customizeLayout(final SharedPreferences settings) {
        boolean slVisible = settings.getBoolean(getString(R.string.pref_showsatinfo), true);
        boolean mlVisible = settings.getBoolean(getString(R.string.pref_showmapinfo), true);

        findViewById(R.id.satinfo).setVisibility(slVisible ? View.VISIBLE : View.GONE);
        findViewById(R.id.mapinfo).setVisibility(mlVisible ? View.VISIBLE : View.GONE);

        updateMapViewArea();
    }

    private final void updateOverlays(final SharedPreferences settings, final boolean justRemove) {
        boolean ctEnabled = false;
        boolean wptEnabled = false;
        boolean navEnabled = false;
        boolean distEnabled = false;
        boolean accEnabled = false;
        boolean moEnabled = false;
        boolean scaleEnabled = false;

        if (!justRemove) {
            ctEnabled = settings.getBoolean(getString(R.string.pref_showcurrenttrack), true);
            wptEnabled = settings.getBoolean(getString(R.string.pref_showwaypoints), true);
            distEnabled = showDistance > 0;
            accEnabled = showAccuracy;
            navEnabled = navigationService != null && navigationService.isNavigating();
            moEnabled = true;
            scaleEnabled = true;
        }
        if (ctEnabled && application.currentTrackOverlay == null) {
            application.currentTrackOverlay = new CurrentTrackOverlay(this);
        } else if (!ctEnabled && application.currentTrackOverlay != null) {
            application.currentTrackOverlay.onBeforeDestroy();
            application.currentTrackOverlay = null;
        }
        if (application.waypointsOverlay == null) {
            application.waypointsOverlay = new WaypointsOverlay(this);
            application.waypointsOverlay.setWaypoints(application.getWaypoints());
        }
        application.waypointsOverlay.setEnabled(wptEnabled);
        if (navEnabled && application.navigationOverlay == null) {
            application.navigationOverlay = new NavigationOverlay(this);
        } else if (!navEnabled && application.navigationOverlay != null) {
            application.navigationOverlay.onBeforeDestroy();
            application.navigationOverlay = null;
        }
        if (distEnabled && application.distanceOverlay == null) {
            application.distanceOverlay = new DistanceOverlay(this);
        } else if (!distEnabled && application.distanceOverlay != null) {
            application.distanceOverlay.onBeforeDestroy();
            application.distanceOverlay = null;
        }
        if (!moEnabled && application.mapObjectsOverlay != null) {
            application.mapObjectsOverlay.onBeforeDestroy();
            application.mapObjectsOverlay = null;
        }
        if (scaleEnabled && application.scaleOverlay == null) {
            application.scaleOverlay = new ScaleOverlay(this);
        } else if (!scaleEnabled && application.scaleOverlay != null) {
            application.scaleOverlay.onBeforeDestroy();
            application.scaleOverlay = null;
        }
        if (accEnabled && application.accuracyOverlay == null) {
            application.accuracyOverlay = new AccuracyOverlay(this);
            application.accuracyOverlay.setAccuracy(application.getLocationAsLocation().getAccuracy());
        } else if (!accEnabled && application.accuracyOverlay != null) {
            application.accuracyOverlay.onBeforeDestroy();
            application.accuracyOverlay = null;
        }

        if (justRemove) {
            for (TrackOverlay to : application.fileTrackOverlays) {
                to.onBeforeDestroy();
            }
            application.fileTrackOverlays.clear();
            for (RouteOverlay ro : application.routeOverlays) {
                ro.onBeforeDestroy();
            }
            application.routeOverlays.clear();
            if (application.waypointsOverlay != null) {
                application.waypointsOverlay.onBeforeDestroy();
            }
        } else {
            for (TrackOverlay to : application.fileTrackOverlays) {
                to.onPreferencesChanged(settings);
            }
            for (RouteOverlay ro : application.routeOverlays) {
                ro.onPreferencesChanged(settings);
            }
            if (application.waypointsOverlay != null) {
                application.waypointsOverlay.onPreferencesChanged(settings);
            }
            if (application.navigationOverlay != null) {
                application.navigationOverlay.onPreferencesChanged(settings);
            }
            if (application.mapObjectsOverlay != null) {
                application.mapObjectsOverlay.onPreferencesChanged(settings);
            }
            if (application.distanceOverlay != null) {
                application.distanceOverlay.onPreferencesChanged(settings);
            }
            if (application.accuracyOverlay != null) {
                application.accuracyOverlay.onPreferencesChanged(settings);
            }
            if (application.scaleOverlay != null) {
                application.scaleOverlay.onPreferencesChanged(settings);
            }
            if (application.currentTrackOverlay != null) {
                application.currentTrackOverlay.onPreferencesChanged(settings);
            }
        }
    }

    private void startEditTrack(Track track) {
        setFollowing(false);
        application.editingTrack = track;
        application.editingTrack.editing = true;
        int n = application.editingTrack.getPoints().size() - 1;
        int p = application.editingTrack.editingPos >= 0 ? application.editingTrack.editingPos : n;
        application.editingTrack.editingPos = p;
        trackBar.setMax(n);
        trackBar.setProgress(0);
        trackBar.setProgress(p);
        trackBar.setKeyProgressIncrement(1);
        onProgressChanged(trackBar, p, false);
        findViewById(R.id.edittrack).setVisibility(View.VISIBLE);
        findViewById(R.id.trackdetails).setVisibility(View.VISIBLE);
        updateGPSStatus();
        if (showDistance > 0)
            application.distanceOverlay.setEnabled(false);
        map.setFocusable(false);
        map.setFocusableInTouchMode(false);
        trackBar.requestFocus();
        updateMapViewArea();
    }

    private void startEditRoute(Route route) {
        setFollowing(false);
        application.editingRoute = route;
        application.editingRoute.editing = true;

        boolean newroute = true;
        for (Iterator<RouteOverlay> iter = application.routeOverlays.iterator(); iter.hasNext();) {
            RouteOverlay ro = iter.next();
            if (ro.getRoute().editing) {
                ro.onRoutePropertiesChanged();
                newroute = false;
            }
        }
        if (newroute) {
            RouteOverlay newRoute = new RouteOverlay(this, application.editingRoute);
            application.routeOverlays.add(newRoute);
        }
        findViewById(R.id.editroute).setVisibility(View.VISIBLE);
        updateGPSStatus();
        application.routeEditingWaypoints = new Stack<Waypoint>();
        if (showDistance > 0)
            application.distanceOverlay.setEnabled(false);
        updateMapViewArea();
    }

    public void setFollowing(boolean follow) {
        if (application.editingRoute == null && application.editingTrack == null) {
            if (showDistance > 0 && application.distanceOverlay != null) {
                if (showDistance == 2 && !follow) {
                    application.distanceOverlay.setAncor(application.getLocation());
                    application.distanceOverlay.setEnabled(true);
                } else {
                    application.distanceOverlay.setEnabled(false);
                }
            }
            map.setFollowing(follow);
        }
    }

    public void zoomMap(final float factor) {
        waitBar.setVisibility(View.VISIBLE);
        waitBar.setText(R.string.msg_wait);
        executorThread.execute(new Runnable() {
            public void run() {
                synchronized (map) {
                    if (application.zoomBy(factor)) {
                        map.updateMapInfo();
                        map.update();
                    }
                }
                finishHandler.sendEmptyMessage(0);
            }
        });
    }

    protected void dimScreen(Location location) {
        int color = Color.TRANSPARENT;
        Calendar now = GregorianCalendar.getInstance(TimeZone.getDefault());
        if (autoDim && !Astro.isDaytime(application.getZenith(), location, now))
            color = dimValue << 57; // value * 2 and shifted to transparency octet
        dimView.setBackgroundColor(color);
    }

    public boolean waypointTapped(Waypoint waypoint, int x, int y) {
        try {
            if (application.editingRoute != null) {
                routeSelected = -1;
                waypointSelected = application.getWaypointIndex(waypoint);
                wptQuickAction.show(map, x, y);
                return true;
            } else {
                Location loc = application.getLocationAsLocation();
                FragmentManager fm = getSupportFragmentManager();
                WaypointInfo waypointInfo = (WaypointInfo) fm.findFragmentByTag("waypoint_info");
                if (waypointInfo == null)
                    waypointInfo = new WaypointInfo();
                waypointInfo.setWaypoint(waypoint);
                Bundle args = new Bundle();
                args.putDouble("lat", loc.getLatitude());
                args.putDouble("lon", loc.getLongitude());
                waypointInfo.setArguments(args);
                waypointInfo.show(fm, "waypoint_info");
                return true;
            }
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * Performs action on a tapped route waypoint.
     * 
     * @param route
     *            route index
     * @param index
     *            waypoint index inside route
     * @param x
     *            view X coordinate
     * @param y
     *            view Y coordinate
     * @return true if any action was performed
     */
    public boolean routeWaypointTapped(int route, int index, int x, int y) {
        if (application.editingRoute != null && application.editingRoute == application.getRoute(route)) {
            startActivityForResult(new Intent(this, WaypointProperties.class).putExtra("INDEX", index)
                    .putExtra("ROUTE", route + 1), RESULT_EDIT_ROUTE);
            return true;
        } else if (navigationService != null && navigationService.navRoute == application.getRoute(route)) {
            // routeSelected = route;
            waypointSelected = index;
            rteQuickAction.show(map, x, y);
            return true;
        } else {
            startActivity(new Intent(this, RouteDetails.class).putExtra("INDEX", route));
            return true;
        }
    }

    public boolean mapObjectTapped(long id, int x, int y) {
        mapObjectSelected = id;
        mobQuickAction.show(map, x, y);
        return true;
    }

    @Override
    public boolean onCreateOptionsMenu(final Menu menu) {
        MenuInflater inflater = getSupportMenuInflater();
        inflater.inflate(R.menu.options_menu, menu);

        // add plugins
        SubMenu views = menu.findItem(R.id.menuView).getSubMenu();
        Map<String, Pair<Drawable, Intent>> plugins = application.getPluginsViews();
        for (String plugin : plugins.keySet()) {
            MenuItem item = views.add(plugin);
            item.setIntent(plugins.get(plugin).second);
            if (plugins.get(plugin).first != null)
                item.setIcon(plugins.get(plugin).first);
        }

        return true;
    }

    @Override
    public boolean onPrepareOptionsMenu(final Menu menu) {
        if (application.editingRoute != null || application.editingTrack != null)
            return false;

        boolean wpt = application.hasWaypoints();
        boolean rts = application.hasRoutes();
        boolean nvw = navigationService != null && navigationService.isNavigating();
        boolean nvr = navigationService != null && navigationService.isNavigatingViaRoute();

        menu.findItem(R.id.menuManageWaypoints).setEnabled(wpt);
        menu.findItem(R.id.menuExportCurrentTrack).setEnabled(application.currentTrackOverlay != null);
        menu.findItem(R.id.menuClearCurrentTrack).setEnabled(application.currentTrackOverlay != null);
        menu.findItem(R.id.menuManageRoutes).setVisible(!nvr);
        menu.findItem(R.id.menuStartNavigation).setVisible(!nvr);
        menu.findItem(R.id.menuStartNavigation).setEnabled(rts);
        menu.findItem(R.id.menuNavigationDetails).setVisible(nvr);
        menu.findItem(R.id.menuNextNavPoint).setVisible(nvr);
        menu.findItem(R.id.menuPrevNavPoint).setVisible(nvr);
        menu.findItem(R.id.menuNextNavPoint)
                .setEnabled(navigationService != null && navigationService.hasNextRouteWaypoint());
        menu.findItem(R.id.menuPrevNavPoint)
                .setEnabled(navigationService != null && navigationService.hasPrevRouteWaypoint());
        menu.findItem(R.id.menuStopNavigation).setEnabled(nvw);
        menu.findItem(R.id.menuSetAnchor).setVisible(showDistance > 0 && !map.isFollowing());

        return true;
    }

    @Override
    public boolean onOptionsItemSelected(final MenuItem item) {
        switch (item.getItemId()) {
        case R.id.menuSearch:
            onSearchRequested();
            return true;
        case R.id.menuAddWaypoint: {
            double[] loc = application.getMapCenter();
            Waypoint waypoint = new Waypoint("", "", loc[0], loc[1]);
            waypoint.date = Calendar.getInstance().getTime();
            int wpt = application.addWaypoint(waypoint);
            waypoint.name = "WPT" + wpt;
            application.saveDefaultWaypoints();
            map.update();
            return true;
        }
        case R.id.menuNewWaypoint:
            startActivityForResult(new Intent(this, WaypointProperties.class).putExtra("INDEX", -1),
                    RESULT_SAVE_WAYPOINT);
            return true;
        case R.id.menuProjectWaypoint:
            startActivityForResult(new Intent(this, WaypointProject.class), RESULT_SAVE_WAYPOINT);
            return true;
        case R.id.menuManageWaypoints:
            startActivityForResult(new Intent(this, WaypointList.class), RESULT_MANAGE_WAYPOINTS);
            return true;
        case R.id.menuLoadWaypoints:
            startActivityForResult(new Intent(this, WaypointFileList.class), RESULT_LOAD_WAYPOINTS);
            return true;
        case R.id.menuManageTracks:
            startActivityForResult(new Intent(this, TrackList.class), RESULT_MANAGE_TRACKS);
            return true;
        case R.id.menuExportCurrentTrack:
            FragmentManager fm = getSupportFragmentManager();
            TrackExportDialog trackExportDialog = new TrackExportDialog(locationService);
            trackExportDialog.show(fm, "track_export");
            return true;
        case R.id.menuExpandCurrentTrack:
            new AlertDialog.Builder(this).setIcon(android.R.drawable.ic_dialog_alert).setTitle(R.string.warning)
                    .setMessage(R.string.msg_expandcurrenttrack)
                    .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            if (application.currentTrackOverlay != null) {
                                Track track = locationService.getTrack();
                                track.show = true;
                                application.currentTrackOverlay.setTrack(track);
                            }
                        }
                    }).setNegativeButton(R.string.no, null).show();
            return true;
        case R.id.menuClearCurrentTrack:
            new AlertDialog.Builder(this).setIcon(android.R.drawable.ic_dialog_alert).setTitle(R.string.warning)
                    .setMessage(R.string.msg_clearcurrenttrack)
                    .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            if (application.currentTrackOverlay != null)
                                application.currentTrackOverlay.clear();
                            locationService.clearTrack();
                        }
                    }).setNegativeButton(R.string.no, null).show();
            return true;
        case R.id.menuManageRoutes:
            startActivityForResult(new Intent(this, RouteList.class).putExtra("MODE", RouteList.MODE_MANAGE),
                    RESULT_MANAGE_ROUTES);
            return true;
        case R.id.menuStartNavigation:
            if (application.getRoutes().size() > 1) {
                startActivity(new Intent(this, RouteList.class).putExtra("MODE", RouteList.MODE_START));
            } else {
                startActivity(new Intent(this, RouteStart.class).putExtra("INDEX", 0));
            }
            return true;
        case R.id.menuNavigationDetails:
            startActivity(new Intent(this, RouteDetails.class)
                    .putExtra("index", application.getRouteIndex(navigationService.navRoute))
                    .putExtra("nav", true));
            return true;
        case R.id.menuNextNavPoint:
            navigationService.nextRouteWaypoint();
            return true;
        case R.id.menuPrevNavPoint:
            navigationService.prevRouteWaypoint();
            return true;
        case R.id.menuStopNavigation: {
            navigationService.stopNavigation();
            return true;
        }
        case R.id.menuHSI:
            startActivity(new Intent(this, HSIActivity.class));
            return true;
        case R.id.menuInformation:
            startActivity(new Intent(this, Information.class));
            return true;
        case R.id.menuMapInfo:
            startActivity(new Intent(this, MapInformation.class));
            return true;
        case R.id.menuCursorMaps:
            startActivityForResult(new Intent(this, MapList.class).putExtra("pos", true),
                    RESULT_LOAD_MAP_ATPOSITION);
            return true;
        case R.id.menuAllMaps:
            startActivityForResult(new Intent(this, MapList.class), RESULT_LOAD_MAP);
            return true;
        case R.id.menuShare:
            Intent i = new Intent(android.content.Intent.ACTION_SEND);
            i.setType("text/plain");
            i.putExtra(Intent.EXTRA_SUBJECT, R.string.currentloc);
            double[] sloc = application.getMapCenter();
            String spos = StringFormatter.coordinates(application.coordinateFormat, " ", sloc[0], sloc[1]);
            i.putExtra(Intent.EXTRA_TEXT, spos);
            startActivity(Intent.createChooser(i, getString(R.string.menu_share)));
            return true;
        case R.id.menuCopyLocation: {
            ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
            double[] cloc = application.getMapCenter();
            String cpos = StringFormatter.coordinates(application.coordinateFormat, " ", cloc[0], cloc[1]);
            clipboard.setText(cpos);
            return true;
        }
        case R.id.menuPasteLocation: {
            ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
            if (clipboard.hasText()) {
                String q = clipboard.getText().toString();
                try {
                    double c[] = CoordinateParser.parse(q);
                    if (!Double.isNaN(c[0]) && !Double.isNaN(c[1])) {
                        boolean mapChanged = application.setMapCenter(c[0], c[1], true, false);
                        if (mapChanged)
                            map.updateMapInfo();
                        map.update();
                        map.setFollowing(false);
                    }
                } catch (IllegalArgumentException e) {
                }
            }
            return true;
        }
        case R.id.menuSetAnchor:
            if (showDistance > 0) {
                application.distanceOverlay.setAncor(application.getMapCenter());
                application.distanceOverlay.setEnabled(true);
            }
            return true;
        case R.id.menuPreferences:
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
                startActivity(new Intent(this, Preferences.class));
            } else {
                startActivity(new Intent(this, PreferencesHC.class));
            }
            return true;
        }
        return false;
    }

    private OnActionItemClickListener waypointActionItemClickListener = new OnActionItemClickListener() {
        @Override
        public void onItemClick(QuickAction3D source, int pos, int actionId) {
            Waypoint wpt = application.getWaypoint(waypointSelected);

            switch (actionId) {
            case qaAddWaypointToRoute:
                application.routeEditingWaypoints
                        .push(application.editingRoute.addWaypoint(wpt.name, wpt.latitude, wpt.longitude));
                map.invalidate();
                break;
            }
            waypointSelected = -1;
        }
    };

    private OnActionItemClickListener routeActionItemClickListener = new OnActionItemClickListener() {
        @Override
        public void onItemClick(QuickAction3D source, int pos, int actionId) {
            switch (actionId) {
            case qaNavigateToWaypoint:
                navigationService.setRouteWaypoint(waypointSelected);
                break;
            }
            waypointSelected = -1;
        }
    };

    private OnActionItemClickListener mapObjectActionItemClickListener = new OnActionItemClickListener() {
        @Override
        public void onItemClick(QuickAction3D source, int pos, int actionId) {
            switch (actionId) {
            case qaNavigateToMapObject:
                navigationService.navigateTo(application.getMapObject(mapObjectSelected));
                break;
            }
            mapObjectSelected = -1;
        }
    };

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        switch (requestCode) {
        case RESULT_MANAGE_WAYPOINTS: {
            application.waypointsOverlay.clearBitmapCache();
            application.saveWaypoints();
            break;
        }
        case RESULT_LOAD_WAYPOINTS: {
            if (resultCode == RESULT_OK) {
                Bundle extras = data.getExtras();
                int count = extras.getInt("count");
                if (count > 0) {
                    application.waypointsOverlay.clearBitmapCache();
                }
            }
            break;
        }
        case RESULT_SAVE_WAYPOINT: {
            if (resultCode == RESULT_OK) {
                application.waypointsOverlay.clearBitmapCache();
                application.saveWaypoints();
                if (data != null && data.hasExtra("index")
                        && PreferenceManager.getDefaultSharedPreferences(this).getBoolean(
                                getString(R.string.pref_waypoint_visible),
                                getResources().getBoolean(R.bool.def_waypoint_visible)))
                    application.ensureVisible(application.getWaypoint(data.getIntExtra("index", -1)));
            }
            break;
        }
        case RESULT_SAVE_WAYPOINTS:
            if (resultCode == RESULT_OK) {
                application.saveDefaultWaypoints();
            }
            break;
        case RESULT_MANAGE_TRACKS:
            for (Iterator<TrackOverlay> iter = application.fileTrackOverlays.iterator(); iter.hasNext();) {
                TrackOverlay to = iter.next();
                to.onTrackPropertiesChanged();
                if (to.getTrack().removed) {
                    to.onBeforeDestroy();
                    iter.remove();
                }
            }
            if (resultCode == RESULT_OK) {
                Bundle extras = data.getExtras();
                int index = extras.getInt("index");
                startEditTrack(application.getTrack(index));
            }
            break;
        case RESULT_MANAGE_ROUTES: {
            for (Iterator<RouteOverlay> iter = application.routeOverlays.iterator(); iter.hasNext();) {
                RouteOverlay ro = iter.next();
                ro.onRoutePropertiesChanged();
                if (ro.getRoute().removed) {
                    ro.onBeforeDestroy();
                    iter.remove();
                }
            }
            if (resultCode == RESULT_OK) {
                Bundle extras = data.getExtras();
                int index = extras.getInt("index");
                int dir = extras.getInt("dir");
                if (dir != 0)
                    startService(
                            new Intent(this, NavigationService.class).setAction(NavigationService.NAVIGATE_ROUTE)
                                    .putExtra(NavigationService.EXTRA_ROUTE_INDEX, index)
                                    .putExtra(NavigationService.EXTRA_ROUTE_DIRECTION, dir));
                else
                    startEditRoute(application.getRoute(index));
            }
            break;
        }
        case RESULT_EDIT_ROUTE:
            for (Iterator<RouteOverlay> iter = application.routeOverlays.iterator(); iter.hasNext();) {
                RouteOverlay ro = iter.next();
                if (ro.getRoute().editing)
                    ro.onRoutePropertiesChanged();
            }
            break;
        case RESULT_LOAD_MAP:
            if (resultCode == RESULT_OK) {
                Bundle extras = data.getExtras();
                final int id = extras.getInt("id");
                synchronized (map) {
                    application.loadMap(id);
                    map.suspendBestMap();
                    setFollowing(false);
                    map.updateMapInfo();
                    map.update();
                }
            }
            break;
        case RESULT_LOAD_MAP_ATPOSITION:
            if (resultCode == RESULT_OK) {
                Bundle extras = data.getExtras();
                final int id = extras.getInt("id");
                if (application.selectMap(id)) {
                    map.suspendBestMap();
                    map.updateMapInfo();
                    map.update();
                } else {
                    map.update();
                }
            }
            break;
        }
    }

    final Handler backHandler = new Handler();

    @Override
    public void onBackPressed() {
        switch (exitConfirmation) {
        case 0:
            // wait for second back
            if (secondBack) {
                backToast.cancel();
                MapActivity.this.finish();
            } else {
                secondBack = true;
                backToast.show();
                backHandler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        secondBack = false;
                    }
                }, 2000);
            }
            return;
        case 1:
            // Ask the user if they want to quit
            new AlertDialog.Builder(this).setIcon(android.R.drawable.ic_dialog_alert)
                    .setTitle(R.string.quitQuestion)
                    .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            // TODO change context everywhere?
                            stopService(new Intent(MapActivity.this, NavigationService.class));
                            MapActivity.this.finish();
                        }
                    }).setNegativeButton(R.string.no, null).show();
            return;
        default:
            super.onBackPressed();
        }
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.zoomin:
            if (application.getNextZoom() == 0.0)
                break;
            waitBar.setVisibility(View.VISIBLE);
            waitBar.setText(R.string.msg_wait);
            executorThread.execute(new Runnable() {
                public void run() {
                    synchronized (map) {
                        if (application.zoomIn()) {
                            map.updateMapInfo();
                            map.update();
                        }
                    }
                    finishHandler.sendEmptyMessage(0);
                }
            });
            break;
        case R.id.zoomout:
            if (application.getPrevZoom() == 0.0)
                break;
            waitBar.setVisibility(View.VISIBLE);
            waitBar.setText(R.string.msg_wait);
            executorThread.execute(new Runnable() {
                public void run() {
                    synchronized (map) {
                        if (application.zoomOut()) {
                            map.updateMapInfo();
                            map.update();
                        }
                    }
                    finishHandler.sendEmptyMessage(0);
                }
            });
            break;
        case R.id.nextmap:
            waitBar.setVisibility(View.VISIBLE);
            waitBar.setText(R.string.msg_wait);
            executorThread.execute(new Runnable() {
                public void run() {
                    synchronized (map) {
                        if (application.prevMap()) {
                            map.suspendBestMap();
                            map.updateMapInfo();
                            map.update();
                        }
                    }
                    finishHandler.sendEmptyMessage(0);
                }
            });
            break;
        case R.id.prevmap:
            waitBar.setVisibility(View.VISIBLE);
            waitBar.setText(R.string.msg_wait);
            executorThread.execute(new Runnable() {
                public void run() {
                    synchronized (map) {
                        if (application.nextMap()) {
                            map.suspendBestMap();
                            map.updateMapInfo();
                            map.update();
                        }
                    }
                    finishHandler.sendEmptyMessage(0);
                }
            });
            break;
        case R.id.maps:
            startActivityForResult(new Intent(this, MapList.class).putExtra("pos", true),
                    RESULT_LOAD_MAP_ATPOSITION);
            break;
        case R.id.waypoints:
            startActivityForResult(new Intent(this, WaypointList.class), RESULT_MANAGE_WAYPOINTS);
            break;
        case R.id.info:
            startActivity(new Intent(this, Information.class));
            break;
        case R.id.follow:
            setFollowing(!map.isFollowing());
            break;
        case R.id.locate: {
            boolean isLocating = locationService != null && locationService.isLocating();
            application.enableLocating(!isLocating);
            Editor editor = PreferenceManager.getDefaultSharedPreferences(this).edit();
            editor.putBoolean(getString(R.string.lc_locate), !isLocating);
            editor.commit();
            break;
        }
        case R.id.tracking: {
            boolean isTracking = locationService != null && locationService.isTracking();
            application.enableTracking(!isTracking);
            Editor editor = PreferenceManager.getDefaultSharedPreferences(this).edit();
            editor.putBoolean(getString(R.string.lc_track), !isTracking);
            editor.commit();
            break;
        }
        case R.id.expand:
            ImageButton expand = (ImageButton) findViewById(R.id.expand);
            if (isFullscreen) {
                getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
                expand.setImageDrawable(getResources().getDrawable(R.drawable.expand));
            } else {
                getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                        WindowManager.LayoutParams.FLAG_FULLSCREEN);
                expand.setImageDrawable(getResources().getDrawable(R.drawable.collapse));
            }
            isFullscreen = !isFullscreen;
            break;
        case R.id.cutbefore:
            application.editingTrack.cutBefore(trackBar.getProgress());
            int nb = application.editingTrack.getPoints().size() - 1;
            trackBar.setMax(nb);
            trackBar.setProgress(0);
            break;
        case R.id.cutafter:
            application.editingTrack.cutAfter(trackBar.getProgress());
            int na = application.editingTrack.getPoints().size() - 1;
            trackBar.setMax(na);
            trackBar.setProgress(0);
            trackBar.setProgress(na);
            break;
        case R.id.addpoint:
            double[] aloc = application.getMapCenter();
            application.routeEditingWaypoints.push(application.editingRoute
                    .addWaypoint("RWPT" + application.editingRoute.length(), aloc[0], aloc[1]));
            break;
        case R.id.insertpoint:
            double[] iloc = application.getMapCenter();
            application.routeEditingWaypoints.push(application.editingRoute
                    .insertWaypoint("RWPT" + application.editingRoute.length(), iloc[0], iloc[1]));
            break;
        case R.id.removepoint:
            if (!application.routeEditingWaypoints.empty()) {
                application.editingRoute.removeWaypoint(application.routeEditingWaypoints.pop());
            }
            break;
        case R.id.orderpoints:
            startActivityForResult(new Intent(this, RouteEdit.class).putExtra("INDEX",
                    application.getRouteIndex(application.editingRoute)), RESULT_EDIT_ROUTE);
            break;
        case R.id.finishedit:
            if ("New route".equals(application.editingRoute.name)) {
                SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd_HH-mm");
                application.editingRoute.name = formatter.format(new Date());
            }
            application.editingRoute.editing = false;
            for (Iterator<RouteOverlay> iter = application.routeOverlays.iterator(); iter.hasNext();) {
                RouteOverlay ro = iter.next();
                ro.onRoutePropertiesChanged();
            }
            application.editingRoute = null;
            application.routeEditingWaypoints = null;
            findViewById(R.id.editroute).setVisibility(View.GONE);
            updateGPSStatus();
            if (showDistance == 2) {
                application.distanceOverlay.setEnabled(true);
            }
            updateMapViewArea();
            map.requestFocus();
            break;
        case R.id.finishtrackedit:
            application.editingTrack.editing = false;
            application.editingTrack.editingPos = -1;
            application.editingTrack = null;
            findViewById(R.id.edittrack).setVisibility(View.GONE);
            findViewById(R.id.trackdetails).setVisibility(View.GONE);
            updateGPSStatus();
            if (showDistance == 2) {
                application.distanceOverlay.setEnabled(true);
            }
            map.setFocusable(true);
            map.setFocusableInTouchMode(true);
            map.requestFocus();
            break;
        }
    }

    @Override
    public void onWaypointNavigate(final Waypoint waypoint) {
        Intent intent = new Intent(getApplicationContext(), NavigationService.class)
                .setAction(NavigationService.NAVIGATE_MAPOBJECT);
        intent.putExtra(NavigationService.EXTRA_NAME, waypoint.name);
        intent.putExtra(NavigationService.EXTRA_LATITUDE, waypoint.latitude);
        intent.putExtra(NavigationService.EXTRA_LONGITUDE, waypoint.longitude);
        intent.putExtra(NavigationService.EXTRA_PROXIMITY, waypoint.proximity);
        startService(intent);
    }

    @Override
    public void onWaypointEdit(final Waypoint waypoint) {
        int index = application.getWaypointIndex(waypoint);
        startActivityForResult(new Intent(this, WaypointProperties.class).putExtra("INDEX", index),
                RESULT_SAVE_WAYPOINT);
    }

    @Override
    public void onWaypointShare(final Waypoint waypoint) {
        Intent i = new Intent(android.content.Intent.ACTION_SEND);
        i.setType("text/plain");
        i.putExtra(Intent.EXTRA_SUBJECT, R.string.currentloc);
        String coords = StringFormatter.coordinates(application.coordinateFormat, " ", waypoint.latitude,
                waypoint.longitude);
        i.putExtra(Intent.EXTRA_TEXT, waypoint.name + " @ " + coords);
        startActivity(Intent.createChooser(i, getString(R.string.menu_share)));
    }

    @Override
    public void onWaypointRemove(final Waypoint waypoint) {
        new AlertDialog.Builder(this).setIcon(android.R.drawable.ic_dialog_alert)
                .setTitle(R.string.removeWaypointQuestion)
                .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {

                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        WaypointSet wptset = waypoint.set;
                        application.removeWaypoint(waypoint);
                        application.saveWaypoints(wptset);
                        map.invalidate();
                    }

                }).setNegativeButton(R.string.no, null).show();
    }

    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
        switch (seekBar.getId()) {
        case R.id.trackbar:
            if (fromUser) {
                application.editingTrack.editingPos = progress;
            }
            Track.TrackPoint tp = application.editingTrack.getPoint(progress);
            double ele = tp.elevation * elevationFactor;
            ((TextView) findViewById(R.id.tp_number)).setText("#" + (progress + 1));
            // FIXME Need UTM support here
            ((TextView) findViewById(R.id.tp_latitude))
                    .setText(StringFormatter.coordinate(application.coordinateFormat, tp.latitude));
            ((TextView) findViewById(R.id.tp_longitude))
                    .setText(StringFormatter.coordinate(application.coordinateFormat, tp.longitude));
            ((TextView) findViewById(R.id.tp_elevation))
                    .setText(String.valueOf(Math.round(ele)) + " " + elevationAbbr);
            ((TextView) findViewById(R.id.tp_time)).setText(SimpleDateFormat
                    .getDateTimeInstance(SimpleDateFormat.SHORT, SimpleDateFormat.SHORT).format(new Date(tp.time)));
            boolean mapChanged = application.setMapCenter(tp.latitude, tp.longitude, false, false);
            if (mapChanged)
                map.updateMapInfo();
            map.update();
            break;
        }
    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {
    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        Log.e(TAG, "onRestoreInstanceState()");
        lastKnownLocation = savedInstanceState.getParcelable("lastKnownLocation");
        lastRenderTime = savedInstanceState.getLong("lastRenderTime");
        lastMagnetic = savedInstanceState.getLong("lastMagnetic");
        lastDim = savedInstanceState.getLong("lastDim");
        lastGeoid = savedInstanceState.getBoolean("lastGeoid");

        waypointSelected = savedInstanceState.getInt("waypointSelected");
        routeSelected = savedInstanceState.getInt("routeSelected");
        mapObjectSelected = savedInstanceState.getLong("mapObjectSelected");

        /*
         * double[] distAncor = savedInstanceState.getDoubleArray("distAncor");
         * if (distAncor != null)
         * {
         * application.distanceOverlay = new DistanceOverlay(this);
         * application.distanceOverlay.setAncor(distAncor);
         * }
         */
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        Log.e(TAG, "onSaveInstanceState()");
        outState.putParcelable("lastKnownLocation", lastKnownLocation);
        outState.putLong("lastRenderTime", lastRenderTime);
        outState.putLong("lastMagnetic", lastMagnetic);
        outState.putLong("lastDim", lastDim);
        outState.putBoolean("lastGeoid", lastGeoid);

        outState.putInt("waypointSelected", waypointSelected);
        outState.putInt("routeSelected", routeSelected);
        outState.putLong("mapObjectSelected", mapObjectSelected);

        if (application.distanceOverlay != null) {
            outState.putDoubleArray("distAncor", application.distanceOverlay.getAncor());
        }
    }

    @Override
    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
        Resources resources = getResources();
        // application preferences
        if (getString(R.string.pref_folder_data).equals(key)) {
            application.setDataPath(Androzic.PATH_DATA,
                    sharedPreferences.getString(key, resources.getString(R.string.def_folder_data)));
        } else if (getString(R.string.pref_folder_icon).equals(key)) {
            application.setDataPath(Androzic.PATH_ICONS,
                    sharedPreferences.getString(key, resources.getString(R.string.def_folder_icon)));
        } else if (getString(R.string.pref_orientation).equals(key)) {
            setRequestedOrientation(Integer.parseInt(sharedPreferences.getString(key, "-1")));
        } else if (getString(R.string.pref_grid_mapshow).equals(key)) {
            application.mapGrid = sharedPreferences.getBoolean(key, false);
            application.initGrids();
        } else if (getString(R.string.pref_grid_usershow).equals(key)) {
            application.userGrid = sharedPreferences.getBoolean(key, false);
            application.initGrids();
        } else if (getString(R.string.pref_grid_preference).equals(key)) {
            application.gridPrefer = Integer.parseInt(sharedPreferences.getString(key, "0"));
            application.initGrids();
        } else if (getString(R.string.pref_grid_userscale).equals(key)
                || getString(R.string.pref_grid_userunit).equals(key)
                || getString(R.string.pref_grid_usermpp).equals(key)) {
            application.initGrids();
        } else if (getString(R.string.pref_useonlinemap).equals(key) && sharedPreferences.getBoolean(key, false)) {
            application.setOnlineMap(sharedPreferences.getString(getString(R.string.pref_onlinemap),
                    resources.getString(R.string.def_onlinemap)));
        } else if (getString(R.string.pref_onlinemap).equals(key)
                || getString(R.string.pref_onlinemapscale).equals(key)) {
            application.setOnlineMap(sharedPreferences.getString(getString(R.string.pref_onlinemap),
                    resources.getString(R.string.def_onlinemap)));
        } else if (getString(R.string.pref_mapadjacent).equals(key)) {
            application.adjacentMaps = sharedPreferences.getBoolean(key,
                    resources.getBoolean(R.bool.def_mapadjacent));
        } else if (getString(R.string.pref_mapcropborder).equals(key)) {
            application.cropMapBorder = sharedPreferences.getBoolean(key,
                    resources.getBoolean(R.bool.def_mapcropborder));
        } else if (getString(R.string.pref_mapdrawborder).equals(key)) {
            application.drawMapBorder = sharedPreferences.getBoolean(key,
                    resources.getBoolean(R.bool.def_mapdrawborder));
        }
        // activity preferences
        else if (getString(R.string.pref_wakelock).equals(key)) {
            keepScreenOn = sharedPreferences.getBoolean(key, resources.getBoolean(R.bool.def_wakelock));
            android.view.Window wnd = getWindow();
            if (wnd != null) {
                if (keepScreenOn)
                    wnd.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
                else
                    wnd.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
            }
        } else if (getString(R.string.pref_exit).equals(key)) {
            exitConfirmation = Integer.parseInt(sharedPreferences.getString(key, "0"));
            secondBack = false;
        } else if (getString(R.string.pref_unitprecision).equals(key)) {
            boolean precision = sharedPreferences.getBoolean(key, resources.getBoolean(R.bool.def_unitprecision));
            precisionFormat = precision ? "%.1f" : "%.0f";
        }
        // map preferences
        else if (getString(R.string.pref_cursorcolor).equals(key)) {
            map.setCursorColor(sharedPreferences.getInt(key, resources.getColor(R.color.cursor)));
        } else if (getString(R.string.pref_panelactions).equals(key)) {
            String pa = sharedPreferences.getString(key, resources.getString(R.string.def_panelactions));
            activeActions = Arrays.asList(pa.split(","));
        }
    }

    @Override
    public void onPanelClosed(Panel panel) {
        // save panel state
        Editor editor = PreferenceManager.getDefaultSharedPreferences(this).edit();
        editor.putBoolean(getString(R.string.ui_drawer_open), false);
        editor.commit();
    }

    @Override
    public void onPanelOpened(Panel panel) {
        updateMapButtons();
        // save panel state
        Editor editor = PreferenceManager.getDefaultSharedPreferences(this).edit();
        editor.putBoolean(getString(R.string.ui_drawer_open), true);
        editor.commit();
    }

    @SuppressLint("HandlerLeak")
    private class FinishHandler extends Handler {
        private final WeakReference<MapActivity> target;

        FinishHandler(MapActivity activity) {
            this.target = new WeakReference<MapActivity>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            MapActivity mapActivity = target.get();
            if (mapActivity != null) {
                mapActivity.waitBar.setVisibility(View.INVISIBLE);
                mapActivity.waitBar.setText("");
            }
        }
    }

}