com.google.android.apps.mytracks.TrackListActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.google.android.apps.mytracks.TrackListActivity.java

Source

/*
 * Copyright 2008 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package com.google.android.apps.mytracks;

import com.google.analytics.tracking.android.EasyTracker;
import com.google.android.apps.mytracks.content.TrackDataHub;
import com.google.android.apps.mytracks.content.TrackDataListener;
import com.google.android.apps.mytracks.content.TrackDataType;
import com.google.android.apps.mytracks.fragments.CheckUnitsDialogFragment;
import com.google.android.apps.mytracks.fragments.DeleteAllTrackDialogFragment;
import com.google.android.apps.mytracks.fragments.DeleteOneTrackDialogFragment;
import com.google.android.apps.mytracks.fragments.DeleteOneTrackDialogFragment.DeleteOneTrackCaller;
import com.google.android.apps.mytracks.fragments.MarketDialogFragment;
import com.google.android.apps.mytracks.fragments.ReviewDialogFragment;
import com.google.android.apps.mytracks.fragments.WelcomeDialogFragment;
import com.google.android.apps.mytracks.io.backup.BackupActivity;
import com.google.android.apps.mytracks.io.file.SaveActivity;
import com.google.android.apps.mytracks.io.file.TrackWriterFactory.TrackFileFormat;
import com.google.android.apps.mytracks.services.ITrackRecordingService;
import com.google.android.apps.mytracks.services.TrackRecordingServiceConnection;
import com.google.android.apps.mytracks.settings.SettingsActivity;
import com.google.android.apps.mytracks.util.AnalyticsUtils;
import com.google.android.apps.mytracks.util.ApiAdapterFactory;
import com.google.android.apps.mytracks.util.Constants;
import com.google.android.apps.mytracks.util.ContextualActionModeCallback;
import com.google.android.apps.mytracks.util.EulaUtils;
import com.google.android.apps.mytracks.util.FileUtils;
import com.google.android.apps.mytracks.util.IntentUtils;
import com.google.android.apps.mytracks.util.ListItemUtils;
import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.apps.mytracks.util.StringUtils;
import com.google.android.apps.mytracks.util.TrackIconUtils;
import com.google.android.apps.mytracks.util.TrackRecordingServiceConnectionUtils;
import com.nogago.android.apps.tracks.content.Track;
import com.nogago.android.apps.tracks.content.TracksColumns;
import com.nogago.android.apps.tracks.content.Waypoint;
import com.nogago.bb10.tracks.R;

import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.database.Cursor;
import android.location.Location;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcelable;
import android.speech.tts.TextToSpeech;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.widget.ResourceCursorAdapter;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Display;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.Toast;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.EnumSet;

/**
 * An activity displaying a list of tracks.
 * 
 * @author Leif Hendrik Wilden
 */
public class TrackListActivity extends FragmentActivity implements DeleteOneTrackCaller {

    private static final String TAG = TrackListActivity.class.getSimpleName();
    private static final String START_GPS_KEY = "start_gps_key";
    private static boolean started = false;

    private static final String[] PROJECTION = new String[] { TracksColumns._ID, TracksColumns.NAME,
            TracksColumns.DESCRIPTION, TracksColumns.CATEGORY, TracksColumns.STARTTIME, TracksColumns.TOTALDISTANCE,
            TracksColumns.TOTALTIME, TracksColumns.ICON };

    // Callback when the trackRecordingServiceConnection binding changes.
    private final Runnable bindChangedCallback = new Runnable() {
        @Override
        public void run() {
            // After binding changes (is available), update the total time in
            // trackController.
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    trackController.update(recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT,
                            recordingTrackPaused);
                }
            });

            if (!startNewRecording) {
                return;
            }

            ITrackRecordingService service = trackRecordingServiceConnection.getServiceIfBound();
            if (service == null) {
                Log.d(TAG, "service not available to start a new recording");
                return;
            }
            try {
                recordingTrackId = service.startNewTrack();
                startNewRecording = false;
                Intent intent = IntentUtils.newIntent(TrackListActivity.this, TrackDetailActivity.class)
                        .putExtra(TrackDetailActivity.EXTRA_TRACK_ID, recordingTrackId);
                startActivity(intent);
                Toast.makeText(TrackListActivity.this, R.string.track_list_record_success, Toast.LENGTH_SHORT)
                        .show();
            } catch (Exception e) {
                Toast.makeText(TrackListActivity.this, R.string.track_list_record_error, Toast.LENGTH_LONG).show();
                Log.e(TAG, "Unable to start a new recording.", e);
            }
        }
    };

    /*
     * Note that sharedPreferenceChangeListenr cannot be an anonymous inner class.
     * Anonymous inner class will get garbage collected.
     */
    private final OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
        @Override
        public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
            if (key == null
                    || key.equals(PreferencesUtils.getKey(TrackListActivity.this, R.string.metric_units_key))) {
                metricUnits = PreferencesUtils.getBoolean(TrackListActivity.this, R.string.metric_units_key,
                        PreferencesUtils.METRIC_UNITS_DEFAULT);
                if (key != null) {
                    resourceCursorAdapter.notifyDataSetChanged();
                    return;
                }
            }
            if (key == null || key
                    .equals(PreferencesUtils.getKey(TrackListActivity.this, R.string.recording_track_id_key))) {
                recordingTrackId = PreferencesUtils.getLong(TrackListActivity.this,
                        R.string.recording_track_id_key);
                if (key != null) {
                    boolean isRecording = recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
                    if (isRecording) {
                        trackRecordingServiceConnection.startAndBind();
                    }
                    updateMenuItems(isRecording);
                    resourceCursorAdapter.notifyDataSetChanged();
                    trackController.update(isRecording, recordingTrackPaused);
                    return;
                }
            }
            if (key == null || key
                    .equals(PreferencesUtils.getKey(TrackListActivity.this, R.string.recording_track_paused_key))) {
                recordingTrackPaused = PreferencesUtils.getBoolean(TrackListActivity.this,
                        R.string.recording_track_paused_key, PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT);
                if (key != null) {
                    boolean success = true;
                    try {
                        resourceCursorAdapter.notifyDataSetChanged();
                    } catch (android.database.StaleDataException e) {
                        success = false;
                    }
                    if (success)
                        trackController.update(recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT,
                                recordingTrackPaused);
                    return;
                }
            }
        }
    };

    // Callback when an item is selected in the contextual action mode
    private final ContextualActionModeCallback contextualActionModeCallback = new ContextualActionModeCallback() {
        @Override
        public boolean onClick(int itemId, int position, long id) {
            return handleContextItem(itemId, id);
        }
    };

    private final OnClickListener recordListener = new OnClickListener() {
        public void onClick(View v) {
            if (recordingTrackId == PreferencesUtils.RECORDING_TRACK_ID_DEFAULT) {
                // Not recording -> Recording
                AnalyticsUtils.sendPageViews(TrackListActivity.this, "/action/record_track");
                startGps = false;
                handleStartGps();
                updateMenuItems(true);
                startRecording();
            } else {
                if (recordingTrackPaused) {
                    // Paused -> Resume
                    AnalyticsUtils.sendPageViews(TrackListActivity.this, "/action/resume_track");
                    updateMenuItems(true);
                    TrackRecordingServiceConnectionUtils.resumeTrack(trackRecordingServiceConnection);
                    trackController.update(true, false);
                } else {
                    // Recording -> Paused
                    AnalyticsUtils.sendPageViews(TrackListActivity.this, "/action/pause_track");
                    updateMenuItems(true);
                    TrackRecordingServiceConnectionUtils.pauseTrack(trackRecordingServiceConnection);
                    trackController.update(true, true);
                }
            }
        }
    };

    private final OnClickListener stopListener = new OnClickListener() {
        @Override
        public void onClick(View v) {

            if (recordingTrackId == PreferencesUtils.RECORDING_TRACK_ID_DEFAULT) {
                Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://m.gpsies.com"));
                startActivity(browserIntent);
            } else {
                AnalyticsUtils.sendPageViews(TrackListActivity.this, "/action/stop_recording");
                updateMenuItems(false);
                TrackRecordingServiceConnectionUtils.stopRecording(TrackListActivity.this,
                        trackRecordingServiceConnection, true);
            }
        }
    };

    private final TrackDataListener trackDataListener = new TrackDataListener() {
        @Override
        public void onTrackUpdated(Track track) {
            // Ignore
        }

        @Override
        public void onSelectedTrackChanged(Track track) {
            // Ignore
        }

        @Override
        public void onSegmentSplit(Location location) {
            // Ignore
        }

        @Override
        public void onSampledOutTrackPoint(Location location) {
            // Ignore
        }

        @Override
        public void onSampledInTrackPoint(Location location) {
            // Ignore
        }

        @Override
        public boolean onReportSpeedChanged(boolean reportSpeed) {
            return false;
        }

        @Override
        public void onNewWaypointsDone() {
            // Ignore
        }

        @Override
        public void onNewWaypoint(Waypoint waypoint) {
            // Ignore
        }

        @Override
        public void onNewTrackPointsDone() {
            // Ignore
        }

        @Override
        public boolean onMinRecordingDistanceChanged(int minRecordingDistance) {
            return false;
        }

        @Override
        public boolean onMetricUnitsChanged(boolean isMetricUnits) {
            return false;
        }

        @Override
        public void onLocationStateChanged(LocationState locationState) {
            // Ignore
        }

        @Override
        public void onLocationChanged(Location location) {
            // Ignore
        }

        @Override
        public void onHeadingChanged(double heading) {
            // Ignore
        }

        @Override
        public void clearWaypoints() {
            // Ignore
        }

        @Override
        public void clearTrackPoints() {
            // Ignore
        }
    };

    // The following are set in onCreate
    private TrackRecordingServiceConnection trackRecordingServiceConnection;
    private TrackController trackController;
    private ListView listView;
    private ResourceCursorAdapter resourceCursorAdapter;
    private TrackDataHub trackDataHub;

    // Preferences
    private boolean metricUnits;
    private long recordingTrackId;
    private boolean recordingTrackPaused;

    // Menu items
    private MenuItem searchMenuItem;
    private MenuItem startGpsMenuItem;
    private MenuItem importMenuItem;
    private MenuItem saveAllMenuItem;
    private MenuItem deleteAllMenuItem;

    private boolean startNewRecording = false; // true to start a new recording
    private boolean startGps = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setVolumeControlStream(TextToSpeech.Engine.DEFAULT_STREAM);
        setContentView(R.layout.track_list);

        AnalyticsUtils.sendPageViews(this, this.getLocalClassName() + "/create");

        ApiAdapterFactory.getApiAdapter().hideActionBar(this);

        Display display = getWindowManager().getDefaultDisplay();
        boolean devicesZ = display.getWidth() > 720 || display.getHeight() > 720;
        if (devicesZ) {
            // Disable the Keyboard help link
            View v = findViewById(R.id.help_keyboard_q);
            if (v != null)
                v.setVisibility(View.GONE);
            v = findViewById(R.id.help_keyboard_a);
            if (v != null)
                v.setVisibility(View.GONE);
        }
        trackRecordingServiceConnection = new TrackRecordingServiceConnection(this, bindChangedCallback);

        SharedPreferences sharedPreferences = getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
        sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
        sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, null);

        trackController = new TrackController(this, trackRecordingServiceConnection, true, recordListener,
                stopListener);

        // START MOD
        ImageButton helpButton = (ImageButton) findViewById(R.id.listBtnBarHelp);
        if (helpButton != null)
            helpButton.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    Intent intent = IntentUtils.newIntent(TrackListActivity.this, HelpActivity.class);
                    startActivity(intent);
                }
            });
        /*
         * Record = Pause and Stop managed by track controller ImageButton
         * recordButton = (ImageButton) findViewById(R.id.listBtnBarRecord);
         * recordButton.setOnClickListener(recordListener); ImageButton stopButton =
         * (ImageButton) findViewById(R.id.listBtnBarStop);
         * stopButton.setOnClickListener(stopListener);
         */
        ImageButton searchButton = (ImageButton) findViewById(R.id.listBtnBarSearch);
        if (searchButton != null)
            searchButton.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {

                    onSearchRequested();
                }
            });
        ImageButton settingsButton = (ImageButton) findViewById(R.id.listBtnBarSettings);
        if (settingsButton != null)
            settingsButton.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    Intent intent = IntentUtils.newIntent(TrackListActivity.this, SettingsActivity.class);
                    startActivity(intent);
                }
            });

        // END MOD
        listView = (ListView) findViewById(R.id.track_list);
        listView.setEmptyView(findViewById(R.id.track_list_empty_view));
        listView.setOnItemClickListener(new OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Intent intent = IntentUtils.newIntent(TrackListActivity.this, TrackDetailActivity.class)
                        .putExtra(TrackDetailActivity.EXTRA_TRACK_ID, id);
                startActivity(intent);
            }
        });
        resourceCursorAdapter = new ResourceCursorAdapter(this, R.layout.list_item, null, 0) {
            @Override
            public void bindView(View view, Context context, Cursor cursor) {
                int idIndex = cursor.getColumnIndex(TracksColumns._ID);
                int iconIndex = cursor.getColumnIndex(TracksColumns.ICON);
                int nameIndex = cursor.getColumnIndex(TracksColumns.NAME);
                int categoryIndex = cursor.getColumnIndex(TracksColumns.CATEGORY);
                int totalTimeIndex = cursor.getColumnIndexOrThrow(TracksColumns.TOTALTIME);
                int totalDistanceIndex = cursor.getColumnIndexOrThrow(TracksColumns.TOTALDISTANCE);
                int startTimeIndex = cursor.getColumnIndexOrThrow(TracksColumns.STARTTIME);
                int descriptionIndex = cursor.getColumnIndex(TracksColumns.DESCRIPTION);

                boolean isRecording = cursor.getLong(idIndex) == recordingTrackId;
                int iconId = TrackIconUtils.getIconDrawable(cursor.getString(iconIndex));
                String name = cursor.getString(nameIndex);
                String totalTime = StringUtils.formatElapsedTime(cursor.getLong(totalTimeIndex));
                String totalDistance = StringUtils.formatDistance(TrackListActivity.this,
                        cursor.getDouble(totalDistanceIndex), metricUnits);
                long startTime = cursor.getLong(startTimeIndex);
                String startTimeDisplay = StringUtils.formatDateTime(context, startTime).equals(name) ? null
                        : StringUtils.formatRelativeDateTime(context, startTime);

                ListItemUtils.setListItem(TrackListActivity.this, view, isRecording, recordingTrackPaused, iconId,
                        R.string.icon_track, name, cursor.getString(categoryIndex), totalTime, totalDistance,
                        startTimeDisplay, cursor.getString(descriptionIndex));
            }
        };
        listView.setAdapter(resourceCursorAdapter);
        ApiAdapterFactory.getApiAdapter().configureListViewContextualMenu(this, listView,
                contextualActionModeCallback);

        getSupportLoaderManager().initLoader(0, null, new LoaderCallbacks<Cursor>() {
            @Override
            public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
                return new CursorLoader(TrackListActivity.this, TracksColumns.CONTENT_URI, PROJECTION, null, null,
                        TracksColumns._ID + " DESC");
            }

            @Override
            public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
                resourceCursorAdapter.swapCursor(cursor);
            }

            @Override
            public void onLoaderReset(Loader<Cursor> loader) {
                resourceCursorAdapter.swapCursor(null);
            }
        });
        trackDataHub = TrackDataHub.newInstance(this);
        if (savedInstanceState != null) {
            startGps = savedInstanceState.getBoolean(START_GPS_KEY);
        } // Test repeated messaging
        if (!started)
            showStartupDialogs();
    }

    @Override
    protected void onStart() {
        super.onStart();
        trackDataHub.start();
        EasyTracker.getInstance(this).activityStart(this); // Add this method.
    }

    @Override
    protected void onPause() {
        super.onPause();
        trackController.stop();
        trackDataHub.unregisterTrackDataListener(trackDataListener);
    }

    @Override
    protected void onResume() {
        super.onResume();
        TrackRecordingServiceConnectionUtils.resumeConnection(this, trackRecordingServiceConnection);
        trackController.update(recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT,
                recordingTrackPaused);
        handleStartGps();
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putBoolean(START_GPS_KEY, startGps);
    }

    @Override
    protected void onStop() {
        super.onStop();
        trackDataHub.stop();
        EasyTracker.getInstance(this).activityStop(this); // Add this method.
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        trackRecordingServiceConnection.unbind();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.track_list, menu);
        String fileTypes[] = getResources().getStringArray(R.array.file_types);
        menu.findItem(R.id.track_list_save_all_gpx).setTitle(getString(R.string.menu_save_format, fileTypes[0]));
        menu.findItem(R.id.track_list_save_all_kml).setTitle(getString(R.string.menu_save_format, fileTypes[1]));
        menu.findItem(R.id.track_list_save_all_csv).setTitle(getString(R.string.menu_save_format, fileTypes[2]));
        // menu.findItem(R.id.track_list_save_all_tcx)
        // .setTitle(getString(R.string.menu_save_format, fileTypes[3]));

        // searchMenuItem = menu.findItem(R.id.track_list_search);
        startGpsMenuItem = menu.findItem(R.id.track_list_start_gps);
        importMenuItem = menu.findItem(R.id.track_list_import);
        saveAllMenuItem = menu.findItem(R.id.track_list_save_all);
        deleteAllMenuItem = menu.findItem(R.id.track_list_delete_all);

        ApiAdapterFactory.getApiAdapter().configureSearchWidget(this, searchMenuItem);
        updateMenuItems(recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        Intent intent;
        switch (item.getItemId()) {
        /*
         * case R.id.track_list_search: return
         * ApiAdapterFactory.getApiAdapter().handleSearchMenuSelection(this);
         */
        case R.id.track_list_start_gps:
            startGps = !startGps;
            handleStartGps();
            updateMenuItems(recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT);
            return true;
        case R.id.track_list_import:
            importTrackAction();
            return true;
        case R.id.track_list_save_all_gpx:
            startSaveActivity(TrackFileFormat.GPX);
            return true;
        case R.id.track_list_save_all_kml:
            startSaveActivity(TrackFileFormat.KML);
            return true;
        case R.id.track_list_save_all_csv:
            startSaveActivity(TrackFileFormat.CSV);
            return true;
        // case R.id.track_list_save_all_tcx:
        // startSaveActivity(TrackFileFormat.TCX);
        // return true;
        case R.id.track_list_delete_all:
            new DeleteAllTrackDialogFragment().show(getSupportFragmentManager(),
                    DeleteAllTrackDialogFragment.DELETE_ALL_TRACK_DIALOG_TAG);
            return true;
        case R.id.track_list_aggregated_statistics:
            intent = IntentUtils.newIntent(this, AggregatedStatsActivity.class);
            startActivity(intent);
            return true;
        case R.id.track_list_settings:
            intent = IntentUtils.newIntent(this, SettingsActivity.class);
            startActivity(intent);
            return true;
        /*
         * case R.id.track_list_help: intent = IntentUtils.newIntent(this,
         * HelpActivity.class); startActivity(intent); return true;
         */
        default:
            return super.onOptionsItemSelected(item);
        }
    }

    private void importTrackAction() {
        AnalyticsUtils.sendPageViews(this, "/action/import");
        String msg = String.format(getResources().getString(R.string.dlg_import), Constants.IS_BLACKBERRY
                ? FileUtils.buildExternalDirectoryPath("gpx").toString().replace("/mnt/sdcard", "/misc/android")
                : FileUtils.buildExternalDirectoryPath("gpx").toString());
        Builder builder = new AlertDialog.Builder(TrackListActivity.this);
        builder.setMessage(msg).setNeutralButton(getString(android.R.string.cancel), null);
        builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                Intent myintent = IntentUtils.newIntent(TrackListActivity.this, ImportActivity.class)
                        .putExtra(ImportActivity.EXTRA_IMPORT_ALL, true);
                startActivity(myintent);
            }
        });
        builder.show();
    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo);
        getMenuInflater().inflate(R.menu.list_context_menu, menu);
    }

    @Override
    public boolean onContextItemSelected(MenuItem item) {
        if (handleContextItem(item.getItemId(), ((AdapterContextMenuInfo) item.getMenuInfo()).id)) {
            return true;
        }
        return super.onContextItemSelected(item);
    }

    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        // T,B, Space are already mapped by BB on Q10!
        Intent intent;
        boolean isRecording = recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
        switch (keyCode) {
        case KeyEvent.KEYCODE_SEARCH:
            if (ApiAdapterFactory.getApiAdapter().handleSearchKey(searchMenuItem)) {
                return true;
            }
            break;
        case KeyEvent.KEYCODE_H:
            // Help
            intent = IntentUtils.newIntent(TrackListActivity.this, HelpActivity.class);
            startActivity(intent);
            break;
        case KeyEvent.KEYCODE_N:
            if (!isRecording) {
                // Not recording -> Recording
                AnalyticsUtils.sendPageViews(TrackListActivity.this, "/action/record_track");
                startGps = false;
                handleStartGps();
                updateMenuItems(true);
                startRecording();
            }
            break;
        case KeyEvent.KEYCODE_P:
            if (isRecording) {
                if (recordingTrackPaused) {
                    // Paused -> Resume
                    AnalyticsUtils.sendPageViews(TrackListActivity.this, "/action/resume_track");
                    updateMenuItems(true);
                    TrackRecordingServiceConnectionUtils.resumeTrack(trackRecordingServiceConnection);
                    trackController.update(true, false);
                } else {
                    // Recording -> Paused
                    AnalyticsUtils.sendPageViews(TrackListActivity.this, "/action/pause_track");
                    updateMenuItems(true);
                    TrackRecordingServiceConnectionUtils.pauseTrack(trackRecordingServiceConnection);
                    trackController.update(true, true);
                }
            } else {
                Toast.makeText(getApplicationContext(), "Cannot pause track that is not recorded.",
                        Toast.LENGTH_LONG).show();
            }
            break;
        case KeyEvent.KEYCODE_S:
            if (isRecording) {

                // Stop Recording
                updateMenuItems(false);
                TrackRecordingServiceConnectionUtils.stopRecording(TrackListActivity.this,
                        trackRecordingServiceConnection, true);
            } else {
                // Search
                ApiAdapterFactory.getApiAdapter().handleSearchMenuSelection(this);
            }
            break;
        case KeyEvent.KEYCODE_A:
            intent = IntentUtils.newIntent(this, SettingsActivity.class);
            startActivity(intent);
            break;

        }

        return super.onKeyUp(keyCode, event);
    }

    @Override
    public TrackRecordingServiceConnection getTrackRecordingServiceConnection() {
        return trackRecordingServiceConnection;
    }

    /**
     * Shows start up dialogs.
     */
    public void showStartupDialogs() {
        setupDirectories();
        /*
         * if (!EulaUtils.getAcceptEula(this)) { } else
         */if (EulaUtils.getShowWelcome(this)) {
            Fragment fragment = getSupportFragmentManager()
                    .findFragmentByTag(WelcomeDialogFragment.WELCOME_DIALOG_TAG);
            if (fragment == null) {
                new WelcomeDialogFragment().show(getSupportFragmentManager(),
                        WelcomeDialogFragment.WELCOME_DIALOG_TAG);
            }
        } else if (EulaUtils.getShowCheckUnits(this)) {
            Fragment fragment = getSupportFragmentManager()
                    .findFragmentByTag(CheckUnitsDialogFragment.CHECK_UNITS_DIALOG_TAG);
            if (fragment == null) {
                new CheckUnitsDialogFragment().show(getSupportFragmentManager(),
                        CheckUnitsDialogFragment.CHECK_UNITS_DIALOG_TAG);
            }
        } else if (!Constants.IS_BLACKBERRY && EulaUtils.getShowReview(this) && EulaUtils.getAppStart(this) > 7) {
            // Ask For Review at 7th start, continue bugging the user
            Fragment fragment = getSupportFragmentManager()
                    .findFragmentByTag(ReviewDialogFragment.REVIEW_DIALOG_TAG);
            if (fragment == null) {
                ReviewDialogFragment.newInstance(false).show(getSupportFragmentManager(),
                        ReviewDialogFragment.REVIEW_DIALOG_TAG);
            }
        } else if (!Constants.IS_BLACKBERRY && (EulaUtils.getAppStart(this) % 10) == 9) {
            // Show our other apps every tenth start with starting with the ninth
            // start
            Fragment fragment = getSupportFragmentManager()
                    .findFragmentByTag(MarketDialogFragment.MARKET_DIALOG_TAG);
            if (fragment == null) {
                MarketDialogFragment.newInstance(false).show(getSupportFragmentManager(),
                        MarketDialogFragment.MARKET_DIALOG_TAG);
            }
        } else if ((EulaUtils.getAppStart(this) % 10) == 0) {
            // Perform a backup at every tenth app start.
            Intent intent = IntentUtils.newIntent(this, BackupActivity.class);
            startActivity(intent);
        } else {
            /*
             * Before the welcome sequence, the empty view is not visible so that it
             * doesn't show through.
             */
            findViewById(R.id.track_list_empty_view).setVisibility(View.VISIBLE);
        }
        checkPriorExceptions(false);
        started = true;
    }

    /*
     * Checks whether previous runs of the app had an exception.
     */

    private void checkPriorExceptions(boolean firstTime) {
        final File file = new File(FileUtils.buildExternalDirectoryPath("error.log"));
        if (file != null && file.exists() && file.length() > 0) {
            String msg = getString(R.string.previous_run_crashed);
            Builder builder = new AlertDialog.Builder(TrackListActivity.this);
            // User says no
            builder.setMessage(msg).setNeutralButton(getString(R.string.donot_send_report),
                    new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            // Delete Exceptions File when user presses Ignore
                            if (!file.delete())
                                Toast.makeText(getApplicationContext(), "Exceptions file not deleted",
                                        Toast.LENGTH_LONG).show();
                        }
                    });
            // User says yes
            builder.setPositiveButton(R.string.send_report, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    Intent intent = new Intent(Intent.ACTION_SEND);
                    intent.putExtra(Intent.EXTRA_EMAIL, new String[] { Constants.BUGS_MAIL }); //$NON-NLS-1$
                    intent.setType("vnd.android.cursor.dir/email"); //$NON-NLS-1$
                    intent.putExtra(Intent.EXTRA_SUBJECT, "nogago Tracks bug"); //$NON-NLS-1$
                    StringBuilder text = new StringBuilder();
                    text.append("\nDevice : ").append(Build.DEVICE); //$NON-NLS-1$
                    text.append("\nBrand : ").append(Build.BRAND); //$NON-NLS-1$
                    text.append("\nModel : ").append(Build.MODEL); //$NON-NLS-1$
                    text.append("\nProduct : ").append("Tracks"); //$NON-NLS-1$
                    text.append("\nBuild : ").append(Build.DISPLAY); //$NON-NLS-1$
                    text.append("\nVersion : ").append(Build.VERSION.RELEASE); //$NON-NLS-1$
                    text.append("\nApp Starts : ").append(EulaUtils.getAppStart(TrackListActivity.this)); //$NON-NLS-1$

                    try {
                        PackageInfo info = getPackageManager().getPackageInfo(getPackageName(), 0);
                        if (info != null) {
                            text.append("\nApk Version : ").append(info.versionName).append(" ") //$NON-NLS-1$//$NON-NLS-2$
                                    .append(info.versionCode);
                        }
                    } catch (NameNotFoundException e) {
                    }

                    try {
                        FileReader fr = new FileReader(file);
                        BufferedReader br = new BufferedReader(fr);
                        String line;
                        while (br.read() != -1) {
                            if ((line = br.readLine()) != null) {
                                text.append(line);
                            }
                        }
                        br.close();
                        fr.close();
                    } catch (IOException e) {
                        Toast.makeText(getApplicationContext(), "Error reading exceptions file!", Toast.LENGTH_LONG)
                                .show();
                    }
                    intent.putExtra(Intent.EXTRA_TEXT, text.toString());
                    startActivity(Intent.createChooser(intent, getString(R.string.send_report)));

                    if (!file.delete())
                        Toast.makeText(getApplicationContext(), "Exceptions file not deleted", Toast.LENGTH_LONG)
                                .show();
                }

            });
            builder.show();
        }
    }

    /**
     * Creates the directories to exchange track files
     */
    private void setupDirectories() {
        if (FileUtils.isSdCardAvailable()) {
            // create a File object for the parent directory
            File directory = new File(FileUtils.buildExternalDirectoryPath("gpx"));
            // have the object build the directory structure, if needed.
            directory.mkdirs();
            directory = new File(FileUtils.buildExternalDirectoryPath("backups"));
            // have the object build the directory structure, if needed.
            directory.mkdirs();
            directory = new File(FileUtils.buildExternalDirectoryPath("kml"));
            // have the object build the directory structure, if needed.
            directory.mkdirs();
            directory = new File(FileUtils.buildExternalDirectoryPath("csv"));
            // have the object build the directory structure, if needed.
            directory.mkdirs();
        }

    }

    /**
     * Updates the menu items.
     * 
     * @param isRecording true if recording
     */
    private void updateMenuItems(boolean isRecording) {
        if (startGpsMenuItem != null) {
            startGpsMenuItem.setIcon(startGps ? R.drawable.menu_stop_gps : R.drawable.menu_start_gps);
            startGpsMenuItem.setTitle(startGps ? R.string.menu_stop_gps : R.string.menu_start_gps);
        }
        if (importMenuItem != null) {
            importMenuItem.setVisible(!isRecording);
        }
        if (saveAllMenuItem != null) {
            saveAllMenuItem.setVisible(!isRecording);
        }
        if (deleteAllMenuItem != null) {
            deleteAllMenuItem.setVisible(!isRecording);
        }
    }

    /**
     * Starts a new recording.
     */
    private void startRecording() {
        startNewRecording = true;
        trackRecordingServiceConnection.startAndBind();

        /*
         * If the binding has happened, then invoke the callback to start a new
         * recording. If the binding hasn't happened, then invoking the callback
         * will have no effect. But when the binding occurs, the callback will get
         * invoked.
         */
        bindChangedCallback.run();
    }

    /**
     * Starts the {@link SaveActivity} to save all tracks.
     * 
     * @param trackFileFormat the track file format
     */
    private void startSaveActivity(TrackFileFormat trackFileFormat) {
        AnalyticsUtils.sendPageViews(this, "/action/save_all");
        Intent intent = IntentUtils.newIntent(this, SaveActivity.class)
                .putExtra(SaveActivity.EXTRA_TRACK_FILE_FORMAT, (Parcelable) trackFileFormat);
        startActivity(intent);
    }

    /**
     * Handles a context item selection.
     * 
     * @param itemId the menu item id
     * @param trackId the track id
     * @return true if handled.
     */
    private boolean handleContextItem(int itemId, long trackId) {
        Intent intent;
        switch (itemId) {
        case R.id.list_context_menu_show_on_map:
            intent = IntentUtils.newIntent(this, TrackDetailActivity.class)
                    .putExtra(TrackDetailActivity.EXTRA_TRACK_ID, trackId);
            startActivity(intent);
            return true;
        case R.id.list_context_menu_edit:
            intent = IntentUtils.newIntent(this, TrackEditActivity.class).putExtra(TrackEditActivity.EXTRA_TRACK_ID,
                    trackId);
            startActivity(intent);
            return true;
        case R.id.list_context_menu_delete:
            DeleteOneTrackDialogFragment.newInstance(trackId).show(getSupportFragmentManager(),
                    DeleteOneTrackDialogFragment.DELETE_ONE_TRACK_DIALOG_TAG);
            return true;
        default:
            return false;
        }
    }

    /**
     * Handles starting gps.
     */
    private void handleStartGps() {
        if (startGps) {
            trackDataHub.registerTrackDataListener(trackDataListener, EnumSet.of(TrackDataType.LOCATION));
        } else {
            trackDataHub.unregisterTrackDataListener(trackDataListener);
        }
    }

}