com.cellbots.logger.LoggerActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.cellbots.logger.LoggerActivity.java

Source

/*
 * Copyright (C) 2011 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.cellbots.logger;

import android.app.Activity;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Color;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.BatteryManager;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.StatFs;
import android.text.format.DateUtils;
import android.util.Log;
import android.view.KeyEvent;
import android.view.SurfaceHolder;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.SlidingDrawer;
import android.widget.TextView;
import android.widget.Toast;

import com.cellbots.logger.GpsManager.GpsManagerListener;
import com.cellbots.logger.RemoteControl.Command;
import com.cellbots.logger.WapManager.ScanResults;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.zip.Deflater;

/**
 * Main activity for a data gathering tool. This tool enables recording video
 * and collecting data from sensors. Data is stored in:
 * /sdcard/SmartphoneLoggerData/.
 * 
 * @author clchen@google.com (Charles L. Chen)
 */

public class LoggerActivity extends Activity {
    /*
     * Constants
     */
    public static final String TAG = "CELLBOTS LOGGER";

    public static final String EXTRA_MODE = "MODE";

    public static final String EXTRA_PICTURE_DELAY = "PICTURE_DELAY";

    public static final String EXTRA_USE_ZIP = "USE_ZIP";

    public static final int MODE_VIDEO_FRONT = 0;

    public static final int MODE_VIDEO_BACK = 1;

    public static final int MODE_PICTURES = 2;

    private static final int UI_BAR_MAX_TOP_PADDING = 275;

    private static final float TEMPERATURE_MAX = 500;

    // max file size. if this is set to zero, only 1 .zip file is created
    protected static final int MAX_OUTPUT_ZIP_CHUNK_SIZE = 50 * 1024 * 1024;

    private static final int PROGRESS_ID = 123122312;

    /*
     * App state
     */
    private volatile Boolean mIsRecording;

    private boolean useZip;

    private long startRecTime = 0;

    private Thread zipperThread;

    private long mDelay = 0;

    private LoggerApplication application;

    /*
     * UI Elements
     */
    private AbstractCamcorderPreview mCamcorderView;

    private CameraPreview mCameraView;

    private TextView mAccelXTextView;

    private TextView mAccelYTextView;

    private TextView mAccelZTextView;

    private TextView mGyroXTextView;

    private TextView mGyroYTextView;

    private TextView mGyroZTextView;

    private TextView mMagXTextView;

    private TextView mMagYTextView;

    private TextView mMagZTextView;

    private BarImageView mBatteryTempBarImageView;

    private TextView mBatteryTempTextView;

    private TextView mBatteryTempSpacerTextView;

    private LinearLayout mFlashingRecGroup;

    private TextView mRecTimeTextView;

    private BarImageView mStorageBarImageView;

    private TextView mStorageTextView;

    private TextView mStorageSpacerTextView;

    private SlidingDrawer mDiagnosticsDrawer;

    private SlidingDrawer mDataDrawer;

    private TextView mPictureCountView;

    private TextView mGpsLocationView;

    /*
     * Sensors
     */
    private List<Sensor> sensors;

    private SensorManager mSensorManager;

    private int mBatteryTemp = 0;

    private BufferedWriter mBatteryTempWriter;

    private BufferedWriter mBatteryLevelWriter;

    private BufferedWriter mBatteryVoltageWriter;

    private BufferedWriter mWifiWriter;

    private HashMap<String, BufferedWriter> sensorLogFileWriters;

    private StatFs mStatFs;

    private int mFreeSpacePct;

    private BufferedWriter mGpsLocationWriter;

    private BufferedWriter mGpsStatusWriter;

    private BufferedWriter mGpsNmeaWriter;

    private GpsManager mGpsManager;

    private WapManager mWapManager;

    private RemoteControl mRemoteControl;

    /*
     * Event handlers
     */
    private SensorEventListener mSensorEventListener = new SensorEventListener() {
        @Override
        public void onSensorChanged(SensorEvent event) {
            Sensor sensor = event.sensor;
            if (sensor.getType() == Sensor.TYPE_GYROSCOPE) {
                // Gyroscope doesn't really have a notion of accuracy.
                // Due to a bug in Android, the gyroscope incorrectly returns
                // its status as unreliable. This can be safely ignored and does
                // not impact the accuracy of the readings.
                event.accuracy = SensorManager.SENSOR_STATUS_ACCURACY_HIGH;
            }
            updateSensorUi(sensor.getType(), event.accuracy, event.values);
            synchronized (mIsRecording) {
                if (mIsRecording) {
                    String valuesStr = "";
                    for (int i = 0; i < event.values.length; i++) {
                        valuesStr = valuesStr + event.values[i] + ",";
                    }
                    BufferedWriter writer = sensorLogFileWriters.get(sensor.getName());
                    try {
                        writer.write(event.timestamp + "," + event.accuracy + "," + valuesStr + "\n");
                        writer.flush();
                    } catch (IOException e) {
                        e.printStackTrace();
                    } catch (NullPointerException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

        @Override
        public void onAccuracyChanged(Sensor sensor, int accuracy) {
        }
    };

    private BroadcastReceiver batteryBroadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            // Display and log the temperature
            mBatteryTemp = intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, 0);
            float percentage = mBatteryTemp / TEMPERATURE_MAX;

            mBatteryTempBarImageView.setPercentage(percentage);
            int paddingTop = (int) ((1.0 - percentage) * UI_BAR_MAX_TOP_PADDING);
            mBatteryTempTextView.setText((mBatteryTemp / 10) + "C");
            mBatteryTempSpacerTextView.setPadding(mBatteryTempSpacerTextView.getPaddingLeft(), paddingTop,
                    mBatteryTempSpacerTextView.getPaddingRight(), mBatteryTempSpacerTextView.getPaddingBottom());

            synchronized (mIsRecording) {
                if (!mIsRecording)
                    return;

                try {
                    mBatteryTempWriter.write(System.currentTimeMillis() + "," + mBatteryTemp + "\n");
                } catch (IOException e) {
                    e.printStackTrace();
                }

                // Log the battery level
                int batteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
                try {
                    mBatteryLevelWriter.write(System.currentTimeMillis() + "," + batteryLevel + "\n");
                } catch (IOException e) {
                    e.printStackTrace();
                }

                // Log the battery voltage level
                int batteryVoltage = intent.getIntExtra(BatteryManager.EXTRA_VOLTAGE, 0);
                try {
                    mBatteryVoltageWriter.write(System.currentTimeMillis() + "," + batteryVoltage + "\n");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    };

    private WapManager.WapManagerListener mWifiListener = new WapManager.WapManagerListener() {
        @Override
        public void onScanResults(long timestamp, ScanResults results) {
            synchronized (mIsRecording) {
                if (!mIsRecording)
                    return;
            }

            try {
                // Convert results to a json object
                JSONObject obj = new JSONObject();
                obj.put("timestamp", timestamp);
                obj.put("results", new JSONObject(results));

                // Write that object to a file
                mWifiWriter.write(obj.toString());
                mWifiWriter.write("\n");
            } catch (JSONException e) {
                Log.e("LoggerActivity", "Error logging wifi results. JSON Error");
                e.printStackTrace();
            } catch (IOException e) {
                Log.e("LoggerActivity", "Error logging wifi results. IO error");
                e.printStackTrace();
            }
        }
    };

    private RemoteControl.CommandListener mCommandListener = new RemoteControl.CommandListener() {

        @Override
        public boolean onCommandReceived(Command c) throws Exception {
            if (c.command.equals("start")) {
                return onStartStopCommandReceived(c, true);
            } else if (c.command.equals("stop")) {
                return onStartStopCommandReceived(c, false);
            } else if (c.command.equals("status")) {
                if (mIsRecording)
                    c.sendResponse("Status: RECORDING\n");
                else
                    c.sendResponse("Status: STOPPED\n");
            }
            return false;
        }

        private boolean onStartStopCommandReceived(Command c, boolean start) {
            if (mIsRecording == start) {
                c.sendResponse("Recording already ");
                if (start)
                    c.sendResponse("started.\n");
                else
                    c.sendResponse("stopped.\n");
                return true;
            }

            final ImageButton recordButton = (ImageButton) findViewById(R.id.button_record);
            recordButton.performClick();

            return false;
        }

    };

    /*
     * Runnables
     */

    private Runnable updateRecTimeDisplay = new Runnable() {
        // @Override
        @Override
        public void run() {
            boolean isRecording;
            synchronized (mIsRecording) {
                isRecording = mIsRecording;
            }
            while (isRecording) {
                mStatFs = new StatFs(Environment.getExternalStorageDirectory().toString());
                final float percentage = (float) (mStatFs.getBlockCount() - mStatFs.getAvailableBlocks())
                        / (float) mStatFs.getBlockCount();
                final int paddingTop = (int) ((1.0 - percentage) * UI_BAR_MAX_TOP_PADDING);
                mFreeSpacePct = (int) (percentage * 100);
                runOnUiThread(new Runnable() {
                    // @Override
                    @Override
                    public void run() {
                        if (mFlashingRecGroup != null) {
                            if (mFlashingRecGroup.getVisibility() == View.VISIBLE) {
                                mFlashingRecGroup.setVisibility(View.INVISIBLE);
                            } else {
                                mFlashingRecGroup.setVisibility(View.VISIBLE);
                            }
                        }
                        if (mRecTimeTextView != null) {
                            mRecTimeTextView.setText(DateUtils
                                    .formatElapsedTime((System.currentTimeMillis() - startRecTime) / 1000));
                        }
                        if ((mPictureCountView != null) && (mCameraView != null)) {
                            mPictureCountView.setText("Pictures taken: " + mCameraView.getPictureCount());
                        }
                        mStorageBarImageView.setPercentage(percentage);
                        mStorageTextView = (TextView) findViewById(R.id.storage_text);
                        mStorageTextView.setText(mFreeSpacePct + "%");
                        mStorageSpacerTextView.setPadding(mStorageSpacerTextView.getPaddingLeft(), paddingTop,
                                mStorageSpacerTextView.getPaddingRight(),
                                mStorageSpacerTextView.getPaddingBottom());
                    }
                });
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (mIsRecording) {
                    isRecording = mIsRecording;
                }
            }

            runOnUiThread(new Runnable() {
                // @Override
                @Override
                public void run() {
                    if (mFlashingRecGroup != null) {
                        mFlashingRecGroup.setVisibility(View.INVISIBLE);
                    }
                }
            });
        }
    };

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        application = (LoggerApplication) getApplication();

        // Keep the screen on to make sure the phone stays awake.
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
                WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

        mIsRecording = false;

        useZip = getIntent().getBooleanExtra(EXTRA_USE_ZIP, true);

        final int mode = getIntent().getIntExtra(EXTRA_MODE, MODE_VIDEO_FRONT);

        if ((mode == MODE_VIDEO_FRONT) || (mode == MODE_VIDEO_BACK)) {
            if (mode == MODE_VIDEO_FRONT) {
                setContentView(R.layout.video_front_mode);
            } else {
                setContentView(R.layout.video_back_mode);
            }
            mCamcorderView = (AbstractCamcorderPreview) findViewById(R.id.surface);
            final SurfaceHolder previewHolder = mCamcorderView.getHolder();
            previewHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        } else {
            mDelay = getIntent().getIntExtra(EXTRA_PICTURE_DELAY, 30) * 1000;
            if (mDelay < 0) {
                mDelay = 0;
            }
            setContentView(R.layout.pictures_mode);
            mCameraView = (CameraPreview) findViewById(R.id.surface);
        }

        // Setup the initial available space
        mStatFs = new StatFs(Environment.getExternalStorageDirectory().toString());
        float percentage = (float) (mStatFs.getBlockCount() - mStatFs.getAvailableBlocks())
                / (float) mStatFs.getBlockCount();
        mFreeSpacePct = (int) (percentage * 100);
        mStorageBarImageView = (BarImageView) findViewById(R.id.storage_barImageView);
        mStorageBarImageView.setPercentage(percentage);
        mStorageTextView = (TextView) findViewById(R.id.storage_text);
        mStorageSpacerTextView = (TextView) findViewById(R.id.storage_text_spacer);
        mStorageTextView.setText(mFreeSpacePct + "%");
        mStorageSpacerTextView.setPadding(mStorageSpacerTextView.getPaddingLeft(),
                (int) ((1 - percentage) * UI_BAR_MAX_TOP_PADDING), mStorageSpacerTextView.getPaddingRight(),
                mStorageSpacerTextView.getPaddingBottom());
        mFlashingRecGroup = (LinearLayout) findViewById(R.id.flashingRecGroup);
        mRecTimeTextView = (TextView) findViewById(R.id.recTime);
        mGpsLocationView = (TextView) findViewById(R.id.gpsLocation);
        mPictureCountView = (TextView) findViewById(R.id.pictureCount);

        mDataDrawer = (SlidingDrawer) findViewById(R.id.dataDrawer);
        mDiagnosticsDrawer = (SlidingDrawer) findViewById(R.id.diagnosticsDrawer);

        final ImageButton recordButton = (ImageButton) findViewById(R.id.button_record);
        recordButton.setOnClickListener(new View.OnClickListener() {
            // @Override
            @Override
            public void onClick(View v) {
                synchronized (mIsRecording) {
                    if ((mode == MODE_VIDEO_FRONT) || (mode == MODE_VIDEO_BACK)) {
                        if (!mIsRecording) {
                            try {
                                mIsRecording = true;
                                recordButton.setImageResource(R.drawable.rec_button_pressed);
                                // initializes recording
                                mCamcorderView.initializeRecording();
                                // starts recording
                                mCamcorderView.startRecording();
                                startRecTime = System.currentTimeMillis();
                                new Thread(updateRecTimeDisplay).start();
                                if (mRemoteControl != null)
                                    mRemoteControl.broadcastMessage("*** Recording Started ***\n");
                            } catch (Exception e) {
                                Log.e("ls", "Recording has failed...", e);
                                Toast.makeText(getApplicationContext(),
                                        "Camera hardware error. Please restart the application.", Toast.LENGTH_LONG)
                                        .show();
                                finish();
                                return;
                            }
                        } else {
                            cleanup();
                        }
                    } else {
                        if (!mIsRecording) {
                            try {
                                mIsRecording = true;
                                recordButton.setImageResource(R.drawable.rec_button_pressed);
                                mCameraView.takePictures(mDelay);
                                new Thread(updateRecTimeDisplay).start();
                            } catch (Exception e) {
                                Log.e("ls", "Taking pictures has failed...", e);
                                Toast.makeText(getApplicationContext(),
                                        "Taking pictures is not possible at the moment: " + e.toString(),
                                        Toast.LENGTH_SHORT).show();
                            }
                        } else {
                            cleanup();
                        }
                    }
                }
            }
        });

        final ImageButton dataButton = (ImageButton) findViewById(R.id.button_data);
        dataButton.setOnClickListener(new View.OnClickListener() {
            // @Override
            @Override
            public void onClick(View v) {
                if (mDataDrawer.isOpened()) {
                    dataButton.setImageResource(R.drawable.data_button_up);
                    mDataDrawer.animateClose();
                } else {
                    dataButton.setImageResource(R.drawable.data_button_pressed);
                    mDataDrawer.animateOpen();
                }
            }
        });

        final ImageButton diagnosticsButton = (ImageButton) findViewById(R.id.button_diagnostics);
        diagnosticsButton.setOnClickListener(new View.OnClickListener() {
            // @Override
            @Override
            public void onClick(View v) {
                if (mDiagnosticsDrawer.isOpened()) {
                    diagnosticsButton.setImageResource(R.drawable.diagnostics_button_up);
                    mDiagnosticsDrawer.animateClose();
                } else {
                    diagnosticsButton.setImageResource(R.drawable.diagnostics_button_pressed);
                    mDiagnosticsDrawer.animateOpen();
                }
            }
        });

        // setupSensors();
        initSensorUi();
    }

    @Override
    protected void onResume() {
        super.onResume();
        setupSensors();
        NetworkHelper.startConfiguration(getApplicationContext());
        mRemoteControl = new RemoteControl(getApplicationContext());

        mRemoteControl.registerCommandListener("start", mCommandListener);
        mRemoteControl.registerCommandListener("stop", mCommandListener);
        mRemoteControl.registerCommandListener("status", mCommandListener);

        mRemoteControl.start();
    }

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

        Log.i("LoggerActivity", "onPause called");

        // Unregister sensor listeners
        for (Sensor s : sensors) {
            mSensorManager.unregisterListener(mSensorEventListener, s);
        }

        // Does the gps cleanup/file closing
        cleanup();

        // Unregister battery
        unregisterReceiver(batteryBroadcastReceiver);

        // Unregister WiFi
        mWapManager.unregisterReceiver();

        // Stop the remote commanding
        mRemoteControl.shutdown();
        mRemoteControl = null;

        Log.i("LoggerActivity", "onPause finished.");
    }

    @Override
    protected Dialog onCreateDialog(int id) {
        if (id != PROGRESS_ID) {
            return super.onCreateDialog(id);
        }
        final ProgressDialog progressDialog = new ProgressDialog(this);
        progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        progressDialog.setCancelable(false);

        // The setMessage call must be in both onCreateDialog and
        // onPrepareDialog otherwise it will
        // fail to update the dialog in onPrepareDialog.
        progressDialog.setMessage("Processing...");

        return progressDialog;
    }

    @Override
    public void onPrepareDialog(int id, Dialog dialog, Bundle bundle) {
        super.onPrepareDialog(id, dialog, bundle);

        if (id != PROGRESS_ID) {
            return;
        }

        final ProgressDialog progressDialog = (ProgressDialog) dialog;
        progressDialog.setMessage("Processing...");
        final Handler handler = new Handler() {
            @Override
            public void handleMessage(android.os.Message msg) {
                int done = msg.getData().getInt("percentageDone");
                String status = msg.getData().getString("status");
                progressDialog.setProgress(done);
                progressDialog.setMessage(status);

                if (mRemoteControl != null) {
                    mRemoteControl.broadcastMessage("Zipping Progress: " + done + "%\n");
                }
            }
        };

        zipperThread = new Thread() {
            @Override
            public void run() {
                if (useZip) {
                    ZipItUpRequest request = new ZipItUpRequest();
                    String directoryName = application.getLoggerPathPrefix();
                    request.setInputFiles(new FileListFetcher().getFilesAndDirectoriesInDir(directoryName));
                    request.setOutputFile(directoryName + "/logged-data.zip");
                    request.setMaxOutputFileSize(MAX_OUTPUT_ZIP_CHUNK_SIZE);
                    request.setDeleteInputfiles(true);
                    request.setCompressionLevel(Deflater.NO_COMPRESSION);

                    try {
                        new ZipItUpProcessor(request).chunkIt(handler);
                    } catch (IOException e) {
                        Log.e("Oh Crap!", "IoEx", e);
                    }
                }
                // closing dialog
                progressDialog.dismiss();
                application.generateNewFilePathUniqueIdentifier();

                // TODO: Need to deal with empty directories that are created if
                // another recording
                // session is never started.
                initSensorLogFiles();

                if (mCamcorderView != null) {
                    try {
                        mCamcorderView.startPreview();
                        if (mRemoteControl != null)
                            mRemoteControl.broadcastMessage("*** Packaging Finished: OK to start ***\n");
                    } catch (RuntimeException e) {
                        e.printStackTrace();
                        runOnUiThread(new Runnable() {
                            // @Override
                            @Override
                            public void run() {
                                Toast.makeText(getApplicationContext(),
                                        "Camera hardware error. Please restart the application.", Toast.LENGTH_LONG)
                                        .show();
                            }
                        });
                        finish();
                        return;
                    }
                }
            }
        };
        zipperThread.start();
    }

    private void setupSensors() {
        // initSensorUi();
        mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        sensors = mSensorManager.getSensorList(Sensor.TYPE_ALL);
        initSensorLogFiles();
        // initBattery(); // *Note* This function deals with UI, not actual
        // readings.
        registerBattery(); // This is the actual function that deals with
                           // receivers.
        initGps();
        initWifi();

        printSensors();
    }

    private void initSensorUi() {
        mAccelXTextView = (TextView) findViewById(R.id.accelerometerX_text);
        mAccelYTextView = (TextView) findViewById(R.id.accelerometerY_text);
        mAccelZTextView = (TextView) findViewById(R.id.accelerometerZ_text);

        mGyroXTextView = (TextView) findViewById(R.id.gyroX_text);
        mGyroYTextView = (TextView) findViewById(R.id.gyroY_text);
        mGyroZTextView = (TextView) findViewById(R.id.gyroZ_text);

        mMagXTextView = (TextView) findViewById(R.id.magneticFieldX_text);
        mMagYTextView = (TextView) findViewById(R.id.magneticFieldY_text);
        mMagZTextView = (TextView) findViewById(R.id.magneticFieldZ_text);

        initBattery();
    }

    private void initSensorLogFiles() {
        sensorLogFileWriters = new HashMap<String, BufferedWriter>();

        String directoryName = application.getDataLoggerPath();
        File directory = new File(directoryName);
        if (!directory.exists() && !directory.mkdirs()) {
            try {
                throw new IOException("Path to file could not be created. " + directory.getAbsolutePath());
            } catch (IOException e) {
                Log.e(TAG, "Directory could not be created. " + e.toString());
            }
        }

        for (int i = 0; i < sensors.size(); i++) {
            Sensor s = sensors.get(i);
            String sensorFilename = directoryName + s.getName().replaceAll(" ", "_") + "_"
                    + application.getFilePathUniqueIdentifier() + ".txt";
            File file = new File(sensorFilename);
            try {
                BufferedWriter writer = new BufferedWriter(new FileWriter(file));
                sensorLogFileWriters.put(s.getName(), writer);
            } catch (IOException e) {
                e.printStackTrace();
            }
            mSensorManager.registerListener(mSensorEventListener, s, SensorManager.SENSOR_DELAY_GAME);
        }

        // The battery is a special case since it is not a real sensor
        mBatteryTempWriter = createBufferedWriter("/BatteryTemp_", directoryName);
        mBatteryLevelWriter = createBufferedWriter("/BatteryLevel_", directoryName);
        mBatteryVoltageWriter = createBufferedWriter("/BatteryVoltage_", directoryName);

        // GPS is another special case since it is not a real sensor
        mGpsLocationWriter = createBufferedWriter("/GpsLocation_", directoryName);
        mGpsStatusWriter = createBufferedWriter("/GpsStatus_", directoryName);
        mGpsNmeaWriter = createBufferedWriter("/GpsNmea_", directoryName);

        // Wifi is another special case
        mWifiWriter = createBufferedWriter("/Wifi_", directoryName);
    }

    /**
     * Creates a new BufferedWriter.
     * 
     * @param prefix The prefix for the file that we're writing to.
     * @return A BufferedWriter for a file in the specified directory. Null if
     *         creation failed.
     */
    private BufferedWriter createBufferedWriter(String prefix, String directoryName) {
        String filename = directoryName + prefix + application.getFilePathUniqueIdentifier() + ".txt";
        File file = new File(filename);
        BufferedWriter bufferedWriter = null;
        try {
            bufferedWriter = new BufferedWriter(new FileWriter(file));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return bufferedWriter;
    }

    private void updateSensorUi(int sensorType, int accuracy, float[] values) {
        // IMPORTANT: DO NOT UPDATE THE CONTENTS INSIDE A DRAWER IF IT IS BEING
        // ANIMATED VIA A CALL TO animateOpen/animateClose!!!
        // Updating anything inside will stop the animation from running.
        // Note that this does not seem to affect the animation if it had been
        // triggered by dragging the drawer instead of being called
        // programatically.
        if (mDataDrawer.isMoving()) {
            return;
        }
        TextView xTextView;
        TextView yTextView;
        TextView zTextView;
        if (sensorType == Sensor.TYPE_ACCELEROMETER) {
            xTextView = mAccelXTextView;
            yTextView = mAccelYTextView;
            zTextView = mAccelZTextView;
        } else if (sensorType == Sensor.TYPE_GYROSCOPE) {
            xTextView = mGyroXTextView;
            yTextView = mGyroYTextView;
            zTextView = mGyroZTextView;
        } else if (sensorType == Sensor.TYPE_MAGNETIC_FIELD) {
            xTextView = mMagXTextView;
            yTextView = mMagYTextView;
            zTextView = mMagZTextView;
        } else {
            return;
        }

        int textColor = Color.WHITE;
        String prefix = "";
        switch (accuracy) {
        case SensorManager.SENSOR_STATUS_ACCURACY_HIGH:
            prefix = "  ";
            break;
        case SensorManager.SENSOR_STATUS_ACCURACY_MEDIUM:
            prefix = "  *";
            break;
        case SensorManager.SENSOR_STATUS_ACCURACY_LOW:
            prefix = "  **";
            break;
        case SensorManager.SENSOR_STATUS_UNRELIABLE:
            prefix = "  ***";
            break;
        }

        xTextView.setTextColor(textColor);
        yTextView.setTextColor(textColor);
        zTextView.setTextColor(textColor);
        xTextView.setText(prefix + numberDisplayFormatter(values[0]));
        yTextView.setText(prefix + numberDisplayFormatter(values[1]));
        zTextView.setText(prefix + numberDisplayFormatter(values[2]));

    }

    private void initGps() {
        mGpsManager = new GpsManager(this, new GpsManagerListener() {

            // @Override
            @Override
            public void onGpsLocationUpdate(long time, float accuracy, double latitude, double longitude,
                    double altitude, float bearing, float speed) {
                synchronized (mIsRecording) {
                    if (!mIsRecording)
                        return;
                }

                try {
                    if (mGpsLocationView != null) {
                        mGpsLocationView.setText("Lat: " + latitude + "\nLon: " + longitude);
                    }
                    mGpsLocationWriter.write(time + "," + accuracy + "," + latitude + "," + longitude + ","
                            + altitude + "," + bearing + "," + speed + "\n");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            // @Override
            @Override
            public void onGpsNmeaUpdate(long time, String nmeaString) {
                synchronized (mIsRecording) {
                    if (!mIsRecording)
                        return;
                }

                try {
                    mGpsNmeaWriter.write(time + "," + nmeaString + "\n");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            // @Override
            @Override
            public void onGpsStatusUpdate(long time, int maxSatellites, int actualSatellites, int timeToFirstFix) {
                synchronized (mIsRecording) {
                    if (!mIsRecording)
                        return;
                }

                try {
                    mGpsStatusWriter.write(
                            time + "," + maxSatellites + "," + actualSatellites + "," + timeToFirstFix + "\n");
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
        });
    }

    private void initBattery() {
        // Battery isn't a regular sensor; instead we have to use a Broadcast
        // receiver.
        //
        // We always write this file since the battery changed event isn't
        // called that often; otherwise, we might miss the initial battery
        // reading.
        //
        // Note that we are reading the current time in MILLISECONDS for this,
        // as opposed to NANOSECONDS for regular sensors.
        mBatteryTempBarImageView = (BarImageView) findViewById(R.id.temperature_barImageView);
        mBatteryTempTextView = (TextView) findViewById(R.id.batteryTemp_text);
        mBatteryTempSpacerTextView = (TextView) findViewById(R.id.batteryTemp_text_spacer);
    }

    private void initWifi() {
        if (mWapManager == null)
            mWapManager = new WapManager(getApplicationContext(), mWifiListener);
        mWapManager.registerReceiver();
    }

    private void registerBattery() {
        IntentFilter batteryFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
        registerReceiver(batteryBroadcastReceiver, batteryFilter);
    }

    /**
     * Prints all the sensors to LogCat.
     */
    private void printSensors() {
        List<Sensor> sensors = mSensorManager.getSensorList(Sensor.TYPE_ALL);
        for (int i = 0; i < sensors.size(); i++) {
            Sensor s = sensors.get(i);
            Log.d("DEBUG", s.getName());
        }
        Log.d("DEBUG", "Also logging wifi. You're welcome.");
    }

    private void closeFiles() {
        try {
            Collection<BufferedWriter> writers = sensorLogFileWriters.values();
            BufferedWriter[] w = new BufferedWriter[0];
            w = writers.toArray(w);
            for (int i = 0; i < w.length; i++) {
                w.clone();
            }
            if (mBatteryTempWriter != null) {
                mBatteryTempWriter.close();
            }
            if (mBatteryLevelWriter != null) {
                mBatteryLevelWriter.close();
            }
            if (mBatteryVoltageWriter != null) {
                mBatteryVoltageWriter.close();
            }
            if (mGpsLocationWriter != null) {
                mGpsLocationWriter.close();
            }
            if (mGpsStatusWriter != null) {
                mGpsStatusWriter.close();
            }
            if (mGpsNmeaWriter != null) {
                mGpsNmeaWriter.close();
            }
            if (mWifiWriter != null) {
                mWifiWriter.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private String numberDisplayFormatter(float value) {
        String displayedText = Float.toString(value);
        if (value >= 0) {
            displayedText = " " + displayedText;
        }
        if (displayedText.length() > 8) {
            displayedText = displayedText.substring(0, 8);
        }
        while (displayedText.length() < 8) {
            displayedText = displayedText + " ";
        }
        return displayedText;
    }

    private void cleanup() {
        boolean wasRecording = false;
        try {
            mGpsManager.shutdown();
            synchronized (mIsRecording) {
                wasRecording = mIsRecording;
                if (!mIsRecording) {
                    cleanupEmptyFiles();
                }
                if (mCamcorderView != null) {
                    if (mIsRecording) {
                        mCamcorderView.stopRecording();
                    }
                    mCamcorderView.releaseRecorder();
                }
                if (mCameraView != null) {
                    if (mIsRecording) {
                        mCameraView.stop();
                    }
                    mCameraView.releaseCamera();
                }
                mIsRecording = false;
            }
            startRecTime = 0;
            ((ImageButton) findViewById(R.id.button_record)).setImageResource(R.drawable.rec_button_up);
            if (mRecTimeTextView != null) {
                mRecTimeTextView.setText(R.string.start_rec_time);
            }
        } catch (RuntimeException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            closeFiles();
            if (wasRecording) {
                if (mRemoteControl != null)
                    mRemoteControl.broadcastMessage("*** Recording Stopped ***\n");
                showDialog(PROGRESS_ID);
            }
        }
    }

    private void cleanupEmptyFiles() {
        Log.i(TAG, "cleaning up empty dirs and zero byte files");
        String logPath = application.getLoggerPathPrefix();
        List<String> filesAndDirs = new FileListFetcher().getFilesAndDirectoriesInDir(logPath);
        List<String> allFilesAndDir = new ArrayList<String>(filesAndDirs.size() + 1);
        allFilesAndDir.addAll(filesAndDirs);
        allFilesAndDir.add(logPath);

        // make sure that all files in this list are zero byte files
        for (String name : allFilesAndDir) {
            File f = new File(name);
            if (f.isFile() && f.length() != 0) {
                // encountered a non-zero length file, abort deletes
                Log.i(TAG, "File: " + name + " has length: " + f.length() + "; aborting cleanup");
                return;
            }
        }

        // delete all files and dirs
        boolean atLeastOneFileWasDeleted = true;
        while (atLeastOneFileWasDeleted) {
            atLeastOneFileWasDeleted = false;
            for (String name : allFilesAndDir) {
                File f = new File(name);
                if (f.exists() && f.delete()) {
                    Log.d(TAG, "deleted " + name);
                    atLeastOneFileWasDeleted = true;
                }
            }
        }
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            boolean shouldExitApp;
            synchronized (mIsRecording) {
                shouldExitApp = !mIsRecording;
            }
            // cleanup();
            if (shouldExitApp) {
                finish();
            }
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }
}