com.updetector.MainActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.updetector.MainActivity.java

Source

/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * 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.updetector;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Random;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;

import org.apache.commons.math3.analysis.function.Constant;
import org.apache.commons.math3.analysis.solvers.NewtonRaphsonSolver;

import weka.core.converters.DatabaseConnection;

import android.R.integer;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.PendingIntent;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.location.LocationManager;
import android.net.NetworkInfo.State;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.speech.tts.TextToSpeech;
import android.speech.tts.TextToSpeech.OnInitListener;
import android.support.v4.app.FragmentActivity;
import android.support.v4.content.LocalBroadcastManager;
import android.telephony.TelephonyManager;
import android.text.Layout;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.util.SparseArray;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ToggleButton;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks;
import com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.internal.br;
import com.google.android.gms.internal.l;
import com.google.android.gms.location.DetectedActivity;
import com.google.android.gms.location.LocationClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapsInitializer;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Polyline;
import com.google.android.gms.maps.model.PolylineOptions;
import com.skyhookwireless.wps.WPSAuthentication;
import com.skyhookwireless.wps.WPSContinuation;
import com.skyhookwireless.wps.WPSLocation;
import com.skyhookwireless.wps.WPSLocationCallback;
import com.skyhookwireless.wps.WPSReturnCode;
import com.skyhookwireless.wps.XPS;
import com.updetector.MotionState.Type;
import com.updetector.R;
import com.updetector.Constants.REQUEST_TYPE;
import com.updetector.MotionState.Source;
import com.updetector.classification.ClassificationManager;
import com.updetector.classification.WekaClassifier;
import com.updetector.fusion.ConditionalProbability;
import com.updetector.fusion.FusionManager;
import com.updetector.google.activityrecognition.GoogleActivityRecognitionClientRemover;
import com.updetector.google.activityrecognition.GoogleActivityRecognitionClientRequester;
import com.updetector.indicator.accelerometerbased.AccelerometerFeature;
import com.updetector.indicator.accelerometerbased.AccelerometerFeatureExtraction.Config;
import com.updetector.indicator.iodetector.CellTowerChart;
import com.updetector.indicator.iodetector.DetectionProfile;
import com.updetector.indicator.iodetector.LightChart;
import com.updetector.indicator.iodetector.MagnetChart;
import com.updetector.managers.AudioRecordManager;
import com.updetector.managers.DetectionNotificationManager;
import com.updetector.managers.LogManager;
import com.updetector.managers.WakeLockManager;
import com.updetector.sensorlist.Sensors;
import com.updetector.viewadapters.MarkerInfoWindowAdapter;
import com.updetector.blocksmap.ParkingBlock;
import com.updetector.blocksmap.ParkingBlocks;
import com.updetector.bluetooth.BluetoothConnectionService;

/**
 * Sample application that demonstrates the use of
 * ActivityRecognitionClient}. It registers for activity detection updates
 * at a rate of 20 seconds, logs them to a file, and displays the detected
 * activities with their associated confidence levels.
 * <p>
 * An IntentService receives activity detection updates in the background
 * so that detection can continue even if the Activity is not visible.
 */
public class MainActivity extends FragmentActivity
        implements ConnectionCallbacks, OnConnectionFailedListener, OnInitListener {
    public static final String LOG_TAG = MainActivity.class.getCanonicalName();

    private static final String LOCK_TAG = "ACCELEROMETER_MONITOR";

    private static boolean geofencingOn = false;
    /**
     * UI Widgets
     */

    // Holds the text view  
    private TextView consoleTextView, environTextView, stateTextView, googleStateTextView;
    public static final String ENVIRONMENT_PREFIX = "Environment : ";
    public static final String STATE_PREFIX = "Time: "; //"Motion State Classified : ";
    public static final String GOOGLE_MOBILITY_STATE_PREFIX = "Motion State Google : ";
    public static final String INDICATOR_PREFIX = "Indicator : ";

    private GoogleMap mMap;
    Vector<ParkingBlock> parkingBlocks = null;

    private TextToSpeech mTextToSpeech;

    /**
      * Holds activity recognition data, in the form of
      * strings that can contain markup
      */
    //private ArrayAdapter<Spanned> mStatusAdapter;

    //Instance of a Bluetooth adapter
    private BluetoothAdapter mBluetoothAdapter;

    private Calendar mCalendar;

    /**
     *  Intent filter for incoming broadcasts from the
     *  IntentService.
     */
    IntentFilter mBroadcastFilter;

    // Instance of a local broadcast manager
    private LocalBroadcastManager mBroadcastManager;

    //Instance of a customized location manager
    private LocationManager mLocationManager;
    private LocationClient mLocationClient;
    private XPS mXPSHandler;

    // The logger object
    private LogManager mLogManager;

    // Instance of customized notification manager
    private DetectionNotificationManager mDetectionNotificationManager;

    // The wake lock manager object
    private WakeLockManager mWakeLockManager;

    /**
     * Google Activity Update Fields
     */
    private GoogleActivityRecognitionClientRequester mGoogleActivityDetectionRequester;
    private GoogleActivityRecognitionClientRemover mGoogleActivityDetectionRemover;
    private double[] probOfOnFootAndInVehicleOfLastUpdate = new double[2];

    /**
     * MST
     */
    private PastMotionStates mPastGoogleActivities = new PastMotionStates(Source.Google,
            Constants.GOOGLE_ACTIVITY_LAST_STATE_NO);
    private PastMotionStates mPastGoogleActivtiesUnModified = new PastMotionStates(Source.Google,
            Constants.GOOGLE_ACTIVITY_LAST_STATE_NO);
    private PastMotionStates mPastClassifiedMotionStates = new PastMotionStates(Source.Classifier,
            Constants.NO_OF_PAST_STATES_STORED);
    private CachedDetectionList mCachedUnparkingDetectionList = new CachedDetectionList(
            CachedDetection.Type.Unparking);
    private CachedDetectionList mCachedParkingDetectionList = new CachedDetectionList(CachedDetection.Type.Parking);

    private double[] lastClassifiedMotionStateDistr = null;
    private double[] lastAccReading;

    private SensorManager mSensorManager;
    private Sensor mAccelerometer;

    private AudioRecordManager mAudioRecordManager;
    private FusionManager mFusionManager;

    /**
     * Detection Interval Fields
     */
    private long lastParkingTimestamp = -1;
    private long lastUnparkingTimestamp = -1;
    private String timeOfLastGoogleOnFootorInVehicleState = "";

    /**
     * IODetector fields
     */
    private CellTowerChart cellTowerChart;
    private LightChart lightChart;
    private MagnetChart magnetChart;
    private Handler mIODectorHandler;
    private boolean aggregationFinish = true;
    private boolean phoneNotStill = false;
    private int lastEnvironment = Constants.ENVIRON_UNKNOWN;
    private double probabilityOfLastEnvironment;
    private ArrayList<Integer> pastEnvironments = new ArrayList<Integer>();

    /**
     * Indicator Fusion
     */
    @SuppressLint("UseSparseArrays")
    private HashMap<Integer, ArrayList<Double>> lastVectors = new HashMap<Integer, ArrayList<Double>>();

    // The classification manager object
    private ClassificationManager mClassificationManager;

    // Store the current request type (ADD or REMOVE)
    private REQUEST_TYPE mRequestType;

    public class LocationClientListener implements LocationListener {
        int eventCode;
        double detectionConfidence;
        long detectionTimestamp;
        int detectionTimeDelayInSec;

        public LocationClientListener(int eventCode, double confidence, long detectionTimestamp,
                int detectionTImeDelayInSec) {
            this.eventCode = eventCode;
            this.detectionConfidence = confidence;
            this.detectionTimestamp = detectionTimestamp;
            this.detectionTimeDelayInSec = detectionTImeDelayInSec;
        }

        @Override
        public void onLocationChanged(Location location) {
            //         new GetAddressTask(eventCode, requestTimestamp,detectionConfidence).execute(location);
            onLocationRetrieved(eventCode, detectionConfidence, detectionTimestamp, detectionTimeDelayInSec,
                    location, null);
        }
    }

    /**
     * A subclass of AsyncTask that calls getFromLocation() in the background.
     * The class definition has these generic types: Location - A Location
     * object containing the current location. Void - indicates that progress
     * units are not used String - An address passed to onPostExecute()
     */
    private class GetAddressTask extends AsyncTask<Location, Void, String> {
        int eventCode;
        long detectionTimestamp;
        double detectionConfidence;
        int detectionTimeDelayInSecs;

        long locationRetrievedTimestamp;//addressRequestTimestamp;
        Location mLocation;

        public GetAddressTask(int eventCode, long detectionTimestamp, int detectionTimeDelayInSecs,
                double confidence) {
            super();
            this.eventCode = eventCode;
            this.detectionConfidence = confidence;
            this.detectionTimestamp = detectionTimestamp;
            this.detectionTimeDelayInSecs = detectionTimeDelayInSecs;
            this.locationRetrievedTimestamp = System.currentTimeMillis();
        }

        /**
         * Get a Geocoder instance, get the latitude and longitude look up the
         * address, and return it
         * 
         * @params params One or more Location objects
         * @return A string containing the address of the current location, or
         *         an empty string if no address can be found, or an error
         *         message
         */
        @Override
        protected String doInBackground(Location... params) {
            Geocoder geocoder = new Geocoder(getApplicationContext(), Locale.getDefault());
            // Get the current location from the input parameter list
            Location loc = params[0];
            mLocation = loc;

            // Create a list to contain the result address
            List<Address> addresses = null;
            try {
                /*
                 * Return 1 address.
                 */
                addresses = geocoder.getFromLocation(loc.getLatitude(), loc.getLongitude(), 1);
            } catch (IOException e1) {
                Log.e("LocationSampleActivity", "IO Exception in getFromLocation()");
                e1.printStackTrace();
                return ("IO Exception trying to get address");
            } catch (IllegalArgumentException e2) {
                // Error message to post in the log
                String errorString = "Illegal arguments " + Double.toString(loc.getLatitude()) + " , "
                        + Double.toString(loc.getLongitude()) + " passed to address service";
                Log.e("LocationSampleActivity", errorString);
                e2.printStackTrace();
                return errorString;
            }
            // If the reverse geocode returned an address
            if (addresses != null && addresses.size() > 0) {
                // Get the first address
                Address address = addresses.get(0);
                /*
                 * Format the first line of address (if available), city, and
                 * country name.
                 */
                String addressText = String.format("%s, %s, %s",
                        // If there's a street address, add it
                        address.getMaxAddressLineIndex() > 0 ? address.getAddressLine(0) : "",
                        // Locality is usually a city
                        address.getLocality(),
                        // The country of the address
                        address.getCountryName());
                // Return the text
                return addressText;
            } else {
                return "No address found";
            }
        }

        /**
           * A method that's called once doInBackground() completes. Turn
           * off the indeterminate activity indicator and set
           * the text of the UI element that shows the address. If the
           * lookup failed, display the error message.
           */
        @Override
        protected void onPostExecute(String address) {
            // Display the results of the lookup.
            onLocationRetrieved(eventCode, detectionConfidence, detectionTimestamp, detectionTimeDelayInSecs,
                    mLocation, address);
        }
    }

    public static long lastGoogleAPIUpdateTimestamp = 0;
    /** Callback when a message is sent from some service */
    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        @SuppressLint("UseSparseArrays")
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            Log.e(LOG_TAG, action);
            long curTime = System.currentTimeMillis();

            if (action.equals(Constants.BLUETOOTH_CONNECTION_UPDATE)) {
                int eventCode = intent.getIntExtra(Constants.BLUETOOTH_CON_UPDATE_EVENT_CODE,
                        Constants.OUTCOME_NONE);
                System.out.println(eventCode);

                //Toast.makeText(getApplicationContext(), "\ndetected "+CommonUtils.eventCodeToString(eventCode)+" at time: "+CommonUtils.formatTimestamp( new Date(), "HH:mm:ss" ), Toast.LENGTH_LONG).show();

                // retrieve a location then
                /*mXPSHandler.getLocation(null
                     ,WPSStreetAddressLookup.WPS_FULL_STREET_ADDRESS_LOOKUP
                     , new XPSLocationCallback(eventCode) );      */

                mLocationClient.requestLocationUpdates(
                        LocationRequest.create().setNumUpdates(1)
                                .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY),
                        new LocationClientListener(eventCode, 1.0, curTime, 0));

            } else {
                //TODO return from Google activity update
                if (action.equals(Constants.GOOGLE_ACTIVITY_RECOGNITION_UPDATE)) {
                    if (curTime - lastGoogleAPIUpdateTimestamp < 1000)
                        return;//return if the new update is within one second
                    lastGoogleAPIUpdateTimestamp = curTime;

                    String mostLikelyActivity = intent
                            .getStringExtra(Constants.GOOGLE_ACT_UPDATE_MOST_LIKELY_ACTIVITY_TYPE);
                    float mostLikelyActivityConfidence = intent
                            .getFloatExtra(Constants.GOOGLE_ACT_UPDATE_MOST_LIKELY_ACTIVITY_CONFIDENCE, 0);

                    int mostLikelyActivityType = intent.getIntExtra(
                            Constants.GOOGLE_ACT_UPDATE_MOST_LIKELY_ACTIVITY_TYPE_INT, DetectedActivity.UNKNOWN);
                    MotionState.Type unModifiedActivityType = MotionState.translate(mostLikelyActivityType);
                    mPastGoogleActivtiesUnModified.add(new MotionState(Source.Google, unModifiedActivityType,
                            CommonUtils.formatTimestamp(new Date(curTime), "HH:mm:ss")));

                    //a tweak  to the UNKNOWN state
                    float onFootConfidence = intent
                            .getFloatExtra(Constants.GOOGLE_ACT_UPDATE_ON_FOOT_ACTIVITY_CONFIDENCE, 0);
                    float inVehicleConfidence = intent
                            .getFloatExtra(Constants.GOOGLE_ACT_UPDATE_IN_VEHICLE_ACTIVITY_CONFIDENCE, 0);
                    if (mostLikelyActivityType == DetectedActivity.UNKNOWN) {
                        if (inVehicleConfidence > 100 - inVehicleConfidence - mostLikelyActivityConfidence
                                && inVehicleConfidence >= Constants.CONFIDENCE_TRHSHOLD_FOR_STATE_MODIFICATION)
                            mostLikelyActivityType = DetectedActivity.IN_VEHICLE;
                        else {
                            if (onFootConfidence > 100 - onFootConfidence - mostLikelyActivityConfidence
                                    && onFootConfidence >= Constants.CONFIDENCE_TRHSHOLD_FOR_STATE_MODIFICATION)
                                mostLikelyActivityType = DetectedActivity.ON_FOOT;
                        }
                    }
                    MotionState.Type activityType = MotionState.translate(mostLikelyActivityType);
                    mPastGoogleActivities.add(new MotionState(Source.Google, activityType,
                            CommonUtils.formatTimestamp(new Date(curTime), "HH:mm:ss")));

                    if (activityType == MotionState.Type.IN_VEHICLE || activityType == MotionState.Type.ON_FOOT) {
                        int outcome;

                        CachedDetection NotExpiredCachedDetectionWithLargestConfidence,
                                firstNotExpiredCachedDetection, lastNotExpiredCachedDetection;
                        CachedDetectionList cdl = null;

                        CachedDetection selectedNotExpiredCachedDetection = null;
                        if (activityType == MotionState.Type.IN_VEHICLE) {
                            outcome = Constants.OUTCOME_UNPARKING;
                            cdl = mCachedUnparkingDetectionList;
                        } else {
                            outcome = Constants.OUTCOME_PARKING;
                            cdl = mCachedParkingDetectionList;
                        }
                        NotExpiredCachedDetectionWithLargestConfidence = cdl.getWithLargestProbability();
                        firstNotExpiredCachedDetection = cdl.get(0);
                        lastNotExpiredCachedDetection = cdl.get(cdl.list.size() - 1);
                        selectedNotExpiredCachedDetection = lastNotExpiredCachedDetection;

                        int secsSinceLastFromState;

                        /**
                         * use unmodified or modified google activities
                         */
                        //                    secsSinceLastFromState=mPastGoogleActivities.isTransitionTo(activityType);
                        secsSinceLastFromState = mPastGoogleActivtiesUnModified
                                .isTransitionTo(unModifiedActivityType);

                        //                    long detectedByG=curTime-secsSinceLastFromState*Constants.ONE_SECOND_IN_MILLISECOND;
                        if (secsSinceLastFromState != -1) {
                            if (selectedNotExpiredCachedDetection != null) {
                                Config civExtractionConf = mClassificationManager.mCIVFeatureExtraction.conf;
                                long detectionDelayInSecs = civExtractionConf.slidingStep * civExtractionConf.scope
                                        / 2;
                                onDetectionConfirmed(outcome, selectedNotExpiredCachedDetection.timestamp,
                                        selectedNotExpiredCachedDetection.location,
                                        selectedNotExpiredCachedDetection.address, detectionDelayInSecs);
                            } else {
                                //no earlier cached result, request a new location
                                mLocationClient.requestLocationUpdates(
                                        LocationRequest.create().setNumUpdates(1)
                                                .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY),
                                        new LocationClientListener(outcome, 1.0, curTime, secsSinceLastFromState));
                            }
                        }

                    }

                    //update the textview
                    googleStateTextView.setText(GOOGLE_MOBILITY_STATE_PREFIX + mostLikelyActivity + " conf:"
                            + mostLikelyActivityConfidence + "  f:" + onFootConfidence + ",v:"
                            + inVehicleConfidence);

                    //log the update
                    SharedPreferences mPrefs = getSharedPreferences(Constants.SHARED_PREFERENCES, 0);
                    boolean logOn = mPrefs.getBoolean(Constants.LOGGING_ON, false);
                    boolean logGoogActUpdatesOn = mPrefs.getBoolean(Constants.LOGGING_GOOGLE_ACTIVITY_UPDATE,
                            false);
                    if (logOn && logGoogActUpdatesOn) {
                        // log the Change-In-Variance Classifier predicated result
                        mLogManager.log(
                                new SimpleDateFormat("yyyy-MM-dd kk:mm:ss:SSS", Locale.US).format(new Date(curTime))
                                        + " " + activityType + " " + unModifiedActivityType,
                                Constants.LOG_FILE_TYPE[Constants.LOG_TYPE_GOOGLE_ACT_UPDATE]);
                    }

                    //build the new MST vector
                    /*double[] probsOfNewUpdate=null;
                    if(probOfOnFootAndInVehicleOfLastUpdate!=null){
                       probsOfNewUpdate=new double[]{onFootConfidence/100, inVehicleConfidence/100};
                       ArrayList<Double> features=new ArrayList<Double>();
                       features.add(probOfOnFootAndInVehicleOfLastUpdate[0]);
                       features.add(probOfOnFootAndInVehicleOfLastUpdate[1]);
                       features.add(probsOfNewUpdate[0]);
                       features.add(probsOfNewUpdate[0]);
                           
                       HashMap<Integer, ArrayList<Double>> mstVector=new HashMap<Integer, ArrayList<Double>>();
                       mstVector.put(Constants.INDICATOR_MST, features);
                       Log.d(LOG_TAG, "Google MST Vector: "+features.toString());
                    }
                    probOfOnFootAndInVehicleOfLastUpdate=probsOfNewUpdate;*/
                }
            }

        }
    };

    //accelerometer feature window and its neighboring windows
    private ArrayList<AccelerometerFeature> civVectorsWithinScope = new ArrayList<AccelerometerFeature>();

    //TODO mSensnorEvent
    @SuppressLint("UseSparseArrays")
    public static long acceleometerSeq = 0;
    private final SensorEventListener mSensorEventListener = new SensorEventListener() {
        @SuppressLint("UseSparseArrays")
        public void onSensorChanged(SensorEvent event) {
            // check if the accelerometer readings have changed since last sample
            boolean readingChanged = false;
            for (int i = 0; i < event.values.length; i++) {
                if (event.values[i] != lastAccReading[i]) {
                    readingChanged = true;
                    lastAccReading[i] = event.values[i];
                }
            }
            if (!readingChanged)
                return;
            acceleometerSeq = (acceleometerSeq + 1) % Integer.MAX_VALUE;

            // requires a wake lock 
            mWakeLockManager.lock(LOCK_TAG);

            //initialization 
            SharedPreferences mPrefs = getSharedPreferences(Constants.SHARED_PREFERENCES, 0);
            int mostLikelyOutcome = Constants.OUTCOME_NONE;
            double mostLikelyOutcomeProb = 1;

            /**
             * Log  raw reading option
             */
            boolean logOn = mPrefs.getBoolean(Constants.LOGGING_ON, false);
            boolean logRawOn = mPrefs.getBoolean(Constants.LOGGING_ACCL_RAW_SWITCH, false);
            if (logOn && logRawOn) {
                String record = CommonUtils.buildALogRecordForNewAccelerometerReadings(event);
                if (record != null)
                    phoneNotStill = true;
                else
                    phoneNotStill = false;
                mLogManager.log(record, Constants.LOG_FILE_TYPE[Constants.LOG_TYPE_ACCEL_RAW]);
            }

            //stop analyzing accelerometer based on environment conditions
            boolean manuallySetToOutdoor = mPrefs.getBoolean(Constants.PREFERENCE_KEY_IS_OUTDOOR, false);
            if (!manuallySetToOutdoor
                    && (lastEnvironment == Constants.ENVIRON_INDOOR && probabilityOfLastEnvironment > 0.8)
            //|| !pastMotionStates.contains((Integer)Constants.STATE_DRIVING)
            ) {
                return;
            }

            //boolean useGoogleActivityInFusion=mPrefs.getBoolean(Constants.PREFERENCE_KEY_USE_GOOGLE_ACTIVITY_IN_FUSION, false);
            double[] mostLikelyOutcomeAndItsProbability;

            /**
             * MST Classifier And Fusion
             */
            AccelerometerFeature accelWindowForMotionState = mClassificationManager.mMSTFeatureExtraction
                    .extractWindowFeature(event);
            if (accelWindowForMotionState != null) {
                String motionStateInstance = accelWindowForMotionState.asStringForMotationState();
                WekaClassifier motionStateClassifier = mClassificationManager.mClassfiers
                        .get(Constants.ACCEL_MOTION_STATE);
                double[] distr = motionStateClassifier.classify(motionStateInstance);
                Log.e(LOG_TAG, "motion state classifier output is : " + Arrays.toString(distr));

                /**
                 * Get the motion state with largest probability
                 */
                int predClassIdx = CommonUtils.idxOfMax(distr);
                if (predClassIdx != -1) {
                    String predClass = Constants.CLASSIFIER_CLASS[1][predClassIdx];
                    //if(!phoneNotStill) predClass="Still";
                    Log.e(LOG_TAG, "cur motion state=" + predClass);
                    //stateTextView.setText(STATE_PREFIX+predClass);
                    mPastClassifiedMotionStates
                            .add(new MotionState(Source.Classifier, MotionState.translate(predClass),
                                    CommonUtils.formatTimestamp(new Date(System.currentTimeMillis()), "HH:mm:ss")));

                }

                //early exit based on state
                if (//!mPastGoogleActivities.containsAtLeastOneWalkingAndOneParking()
                !mPastClassifiedMotionStates.containsAtLeastMOnFootAndAtLeastNInVehicleStates(1, 1))
                    return;

                if (lastClassifiedMotionStateDistr != null) {
                    //build the vector of the MST indicator
                    ArrayList<Double> mstVector = new ArrayList<Double>();
                    mstVector.add(lastClassifiedMotionStateDistr[0]);
                    mstVector.add(lastClassifiedMotionStateDistr[1]);
                    mstVector.add(distr[0]);
                    mstVector.add(distr[1]);
                    Log.e(LOG_TAG, acceleometerSeq + " new mst vector is :" + mstVector.toString());
                    HashMap<Integer, ArrayList<Double>> newPeriodicalVector = new HashMap<Integer, ArrayList<Double>>();
                    newPeriodicalVector.put(Constants.INDICATOR_MST, mstVector);
                    mostLikelyOutcomeAndItsProbability = mFusionManager.fuse(lastVectors, newPeriodicalVector,
                            System.currentTimeMillis(), Constants.HIGH_LEVEL_ACTIVITY_UPARKING, mLogManager);
                    mostLikelyOutcome = (int) mostLikelyOutcomeAndItsProbability[0];
                    mostLikelyOutcomeProb = mostLikelyOutcomeAndItsProbability[1];
                }
                lastClassifiedMotionStateDistr = distr;
                //lastMotionStateDistr=new double[distr.length];
                //for(int ii=0;ii<distr.length;ii++) lastMotionStateDistr[ii]=distr[ii];

            }

            AccelerometerFeature accelWindowForCIV;
            //accelWindowForCIV=accelWindowForMotionState;
            accelWindowForCIV = mClassificationManager.mCIVFeatureExtraction.extractWindowFeature(event);
            if (accelWindowForCIV != null) {
                //get the vector of the Change-In-Variance features 
                String civVector = mClassificationManager.mCIVFeatureExtraction.extractCIVVector(accelWindowForCIV,
                        civVectorsWithinScope);
                if (civVector != null) {
                    Log.e(LOG_TAG, "accelerometerSeq=" + acceleometerSeq + " new civ vector is : " + civVector);

                    /**
                     * calculate the probability of the outcome
                     */
                    boolean classifierForCIVOn = mPrefs.getBoolean(Constants.PREFERENCE_KEY_CIV_CLASSIFIER_ON,
                            false);
                    if (!classifierForCIVOn) {
                        HashMap<Integer, ArrayList<Double>> newPeriodicalVector = new HashMap<Integer, ArrayList<Double>>();
                        newPeriodicalVector.put(Constants.INDICATOR_CIV,
                                CommonUtils.stringToDoubleListRemoved(civVector, ",", new int[] { 0 }));
                        mostLikelyOutcomeAndItsProbability = mFusionManager.fuse(lastVectors, newPeriodicalVector,
                                System.currentTimeMillis(), Constants.HIGH_LEVEL_ACTIVITY_UPARKING, mLogManager);
                        mostLikelyOutcome = (int) mostLikelyOutcomeAndItsProbability[0];
                        mostLikelyOutcomeProb = mostLikelyOutcomeAndItsProbability[1];
                    }
                    /**
                     * classify the vector of the Change-In-Variance vectors
                     */
                    else {
                        WekaClassifier changeInVarianceClassifier = mClassificationManager.mClassfiers
                                .get(Constants.ACCEL_CHANGE_IN_VAR);
                        double[] distr = changeInVarianceClassifier.classify(civVector);

                        int predClassInt = CommonUtils.idxOfMax(distr);
                        String predClass = ",n";

                        switch (predClassInt) {
                        case Constants.CIV_SIGNI_INCREASE:
                        case Constants.CIV_SIGNI_DECREASE:
                            //log the feature
                            if (predClassInt == Constants.CIV_SIGNI_INCREASE) {
                                predClass = ",p";
                                mostLikelyOutcome = Constants.OUTCOME_PARKING;
                            } else {
                                predClass = ",u";
                                mostLikelyOutcome = Constants.OUTCOME_UNPARKING;
                            }
                            break;
                        case Constants.STATE_STILL:
                            // log the feature
                            predClass = ",t";
                            //release the lock
                            mWakeLockManager.unlock(LOCK_TAG);
                            mostLikelyOutcome = Constants.OUTCOME_NONE;
                            break;
                        default:
                            mostLikelyOutcome = Constants.OUTCOME_NONE;
                            break;
                        }
                        Log.e(LOG_TAG, "predClass=" + predClass);
                    }
                }
            }

            //boolean logDetectionOn=mPrefs.getBoolean(Constants.LOGGING_DETECTION_SWITCH, false);

            switch (mostLikelyOutcome) {
            case Constants.OUTCOME_PARKING:
            case Constants.OUTCOME_UNPARKING:
                //long notificationInterval=mPrefs.getInt(Constants.PREFERENCE_KEY_DETECTION_INTERVAL, Constants.DETECTION_INTERVAL_DEFAULT_VALUE);
                //long lastSameTypeDetectionTimestamp;
                //if(outcome==Constants.OUTCOME_PARKING) lastSameTypeDetectionTimestamp=lastParkingTimestamp;
                //else lastSameTypeDetectionTimestamp=lastUnparkingTimestamp;

                //if(System.currentTimeMillis()-lastSameTypeDetectionTimestamp>notificationInterval*Constants.ONE_SECOND_IN_MILLISECOND){
                //if(outcome==Constants.OUTCOME_PARKING) lastParkingTimestamp=System.currentTimeMillis();
                //else lastUnparkingTimestamp=System.currentTimeMillis();

                /*mXPSHandler.getLocation(null
                ,WPSStreetAddressLookup.WPS_FULL_STREET_ADDRESS_LOOKUP
                , new XPSLocationCallback(eventCode) );*/
                Config civExtractionConf = mClassificationManager.mCIVFeatureExtraction.conf;
                int detectionDelayInSecs = civExtractionConf.slidingStep * civExtractionConf.scope / 2;

                mLocationClient.requestLocationUpdates(
                        LocationRequest.create().setNumUpdates(1)
                                .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY),
                        new LocationClientListener(mostLikelyOutcome, mostLikelyOutcomeProb,
                                System.currentTimeMillis(), detectionDelayInSecs));
                //}
                break;
            case Constants.OUTCOME_NONE:
                /*if(logOn){
                   if(logDetectionOn){
                      mLogManager.log("outcome="+outcome+"\n"+mFusionManager.fusionProcessLog.toString()+"\n", Constants.LOG_FILE_TYPE[Constants.LOG_TYPE_DETECTION_REPORT]);
                   }
                }*/
                break;
            default:
                break;
            }

        }

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

    /**
      * A single callback class that will be used to handle
      * all location notifications sent by WPS.
      */
    private class XPSLocationCallback implements WPSLocationCallback {
        private int eventCode;

        public XPSLocationCallback(int eventCode) {
            this.eventCode = eventCode;
        }

        public void done() {
        }

        public WPSContinuation handleError(final WPSReturnCode error) {
            // To retry the location call on error use WPS_CONTINUE,
            // otherwise return WPS_STOP
            Log.e(LOG_TAG, "WPS API return error " + error.toString());
            //return WPSContinuation.WPS_CONTINUE;
            return WPSContinuation.WPS_STOP;
        }

        @Override
        public void handleWPSLocation(WPSLocation location) {
            //actionsOnParkingLocation(eventCode, (Location) location);
        }
    }

    /*
     * Set main UI layout, get a handle to the ListView for logs, and create the broadcast
     * receiver.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        /**
         * Set the views
         */
        // Set the main layout
        setContentView(R.layout.activity_main);

        // get a handle to the console textview
        consoleTextView = (TextView) findViewById(R.id.console_text_id);
        consoleTextView.setMovementMethod(new ScrollingMovementMethod());

        //setup monitoring fields
        environTextView = (TextView) findViewById(R.id.environment);
        environTextView.setText(ENVIRONMENT_PREFIX + CommonUtils.eventCodeToString(lastEnvironment));
        stateTextView = (TextView) findViewById(R.id.state);

        //stateTextView.setText(STATE_PREFIX+"unknown");

        googleStateTextView = (TextView) findViewById(R.id.google_state);
        googleStateTextView.setText(GOOGLE_MOBILITY_STATE_PREFIX + "unknown");

        //indicatorTextView=(TextView) findViewById(R.id.indicator);
        //indicatorTextView.setText(INDICATOR_PREFIX);

        // set up the map view
        setupMapIfNeeded();
        //set up the location client
        setupLocationClientIfNeeded();

        /**
         * set up color coded map
         */
        if (parkingBlocks == null) {
            parkingBlocks = ParkingBlocks.GetParkingBlocks();
            showAvailabilityMap();
        }

        mTextToSpeech = new TextToSpeech(this, this);
        mCalendar = Calendar.getInstance();

        /*
         * Initialize managers
         */
        // Instantiate an adapter to store update data from the log
        /*           mStatusAdapter = new ArrayAdapter<Spanned>(
               this,
               R.layout.item_layout,
               R.id.log_text
                   );*/

        // Set the broadcast receiver intent filer
        mBroadcastManager = LocalBroadcastManager.getInstance(this);

        // Create a new Intent filter for the broadcast receiver
        mBroadcastFilter = new IntentFilter(Constants.ACTION_REFRESH_STATUS_LIST);
        mBroadcastFilter.addCategory(Constants.CATEGORY_LOCATION_SERVICES);
        mBroadcastFilter.addAction(Constants.BLUETOOTH_CONNECTION_UPDATE);
        mBroadcastFilter.addAction(Constants.GOOGLE_ACTIVITY_RECOGNITION_UPDATE);
        mBroadcastManager.registerReceiver(mBroadcastReceiver, mBroadcastFilter);

        // Get the instance of the customized notification manager
        mDetectionNotificationManager = DetectionNotificationManager.getInstance(this);

        //Get the FusionManager object
        mFusionManager = new FusionManager(this);

        // Get the ClassificationManager object
        mClassificationManager = ClassificationManager.getInstance(this);

        //TODO  train classifiers if necessary
        if (Constants.IS_TRAINING_MODE) {
            int[] classifiersToBeTrained = { Constants.ACCEL_MOTION_STATE };
            for (int classifier : classifiersToBeTrained) {
                mClassificationManager.mClassfiers.get(classifier).train();
            }
        }

        //get the sensor service
        mSensorManager = (SensorManager) this.getSystemService(SENSOR_SERVICE);
        //get the accelerometer sensor
        mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        mSensorManager.registerListener(mSensorEventListener, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);

        mAudioRecordManager = AudioRecordManager.getInstance();

        // Get the WakeLockManager object
        mWakeLockManager = WakeLockManager.getInstance(this);
        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

        // Get the LogManager object
        mLogManager = LogManager.getInstance(this);
        mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        mXPSHandler = new XPS(this);
        mXPSHandler.setRegistrationUser(new WPSAuthentication("dbmc", "uic"));
        //mXPSHandler.setTiling("", 0, 0,  null);

        /**
         * Initialize IODetector
         */
        cellTowerChart = new CellTowerChart((TelephonyManager) getSystemService(TELEPHONY_SERVICE), this);
        magnetChart = new MagnetChart(mSensorManager, this);
        lightChart = new LightChart(mSensorManager, this);

        //new AggregatedIODetector().execute("");//Check the detection for the first time

        //This timer handle starts the aggregated calculation for the detection
        //Interval 1 seconds.
        Timer uiTimer = new Timer();
        mIODectorHandler = new Handler();

        /*uiTimer.scheduleAtFixedRate(new TimerTask() {
           private int walked = 0;
           @Override
           public void run() {
        mIODectorHandler.post(new Runnable() {
           @Override
           public void run() {
              if(phoneNotStill){//Check if the user is walking
                 walked++;
              }
              else{
                 walked = 0;
              }
              if(aggregationFinish && walked > 3){//Check if the user has walked for at least 3 second, and the previous calculation has been finish
                 aggregationFinish = false;
                 walked = 0;
                 new AggregatedIODetector().execute("");
              }
            
           }
        });
           }
        }, 0, 1000);*/

        /**
          * Initialize fields other than managers
          */
        lastAccReading = new double[3];

        /**
          * Startup routines
          */
        // catch the force close error
        Thread.setDefaultUncaughtExceptionHandler(new UnCaughtException(MainActivity.this));

        /**
         * Start Google Activity Recognition
         */
        mGoogleActivityDetectionRequester = new GoogleActivityRecognitionClientRequester(this);
        mGoogleActivityDetectionRemover = new GoogleActivityRecognitionClientRemover(this);
        startGoogleActivityRecognitionUpdates(null);

        checkGPSEnabled();

        //test record sample
        //mAudioRecordManager.recordAudioSample("/sdcard/audio.wav");

        //Test extract features from audio files
        //String features=AudioFeatureExtraction.extractFeatures(this, "/sdcard/bus6.wav");
        //mClassificationManager.mClassfiers.get(Constants.SENSOR_MICROPHONE).classify(features);

    }

    /**
     * This class is to handle the Aggregated detection
     */
    private class AggregatedIODetector extends AsyncTask<String, Void, String> {

        private DetectionProfile lightProfile[];
        private DetectionProfile cellProfile[];
        private DetectionProfile magnetProfile[];

        private double[] normalizedProbablities;
        private double[] featureValues;

        @SuppressLint({ "UseSparseArrays", "SimpleDateFormat" })
        @Override
        protected String doInBackground(String... param) {
            cellTowerChart.updateProfile();//get the cell info at time = 0
            for (int i = 0; i < 10; i++) {//get the value for the magnet at the interval of 1s for 10s
                try {
                    magnetChart.updateProfile();
                    Thread.sleep(1000);
                } catch (Exception e) {
                }
            }
            //time = 10s
            lightProfile = lightChart.getProfile();//get the result from the light sensor
            magnetProfile = magnetChart.getProfile();//get the result from the magnet
            cellProfile = cellTowerChart.getProfile();//get the result from the cell tower

            /**
             * Weighted Average to combine different indicators
             */
            /*normalizedProbablities=new double[3];//indoor, semi, outdoor
            Log.i("profile", "light indoor " + lightProfile[0].getConfidence() + " semi " + lightProfile[1].getConfidence() + " outdoor " + lightProfile[2].getConfidence());
            Log.i("profile","magnet indoor " + magnetProfile[0].getConfidence() + " semi " + magnetProfile[1].getConfidence() + " outdoor " + magnetProfile[2].getConfidence());
            Log.i("profile","cell indoor " + cellProfile[0].getConfidence() + " semi " + cellProfile[1].getConfidence() + " outdoor " + cellProfile[2].getConfidence());
                
            for(int i=0;i<normalizedProbablities.length;i++){
               //Aggregate the result
            normalizedProbablities[i] = lightProfile[i].getConfidence()*Constants.IODETECTOR_WEIGHT_LIGHT
                  + magnetProfile[i].getConfidence()*Constants.IODETECTOR_WEIGHT_MAGNET
                  + cellProfile[i].getConfidence()*Constants.IODETECTOR_WEIGHT_CELLULAR;
            }
            double sum=0;
            for(int i=0;i<normalizedProbablities.length;i++) sum+=normalizedProbablities[i];
            for(int i=0;i<normalizedProbablities.length;i++) normalizedProbablities[i]/=sum;*/

            /**
             * Bayesian Data Fusion
             */
            int[] outcomes = { Constants.ENVIRON_INDOOR, Constants.ENVIRON_OUTDOOR };
            HashMap<Integer, ArrayList<Double>> vectorsToBeFused = new HashMap<Integer, ArrayList<Double>>();
            ArrayList<Double> lightVector = new ArrayList<Double>();
            ArrayList<Double> RSSVector = new ArrayList<Double>();
            ArrayList<Double> magneticVector = new ArrayList<Double>();
            Calendar calendar = Calendar.getInstance();

            featureValues = new double[3];
            if (lightChart.getLigthValue() > 0) {//not blocked
                int hour = calendar.get(Calendar.HOUR_OF_DAY);
                if (hour >= 8 && hour <= 17)
                    vectorsToBeFused.put(Constants.INDICATOR_LIGHT_DAY, lightVector);
                else
                    vectorsToBeFused.put(Constants.INDICATOR_LIGHT_NIGHT, lightVector);
                lightVector.add((double) lightChart.getLigthValue());
                featureValues[0] = lightVector.get(0);
            }
            vectorsToBeFused.put(Constants.INDICATOR_RSS, RSSVector);
            RSSVector.add(cellTowerChart.currentASU);
            featureValues[1] = RSSVector.get(0);
            vectorsToBeFused.put(Constants.INDICATOR_MAGNETIC, magneticVector);
            magneticVector.add(magnetChart.magnetVariation);
            featureValues[2] = magneticVector.get(0);

            normalizedProbablities = mFusionManager.BayesianFusion(outcomes, vectorsToBeFused,
                    Constants.HIGH_LEVEL_ACTIVITY_IODOOR, mLogManager);
            Log.d(LOG_TAG, "Baysian fusion Environment: " + Arrays.toString(normalizedProbablities));

            //For logging purposes only        
            SharedPreferences sp = getSharedPreferences(Constants.SHARED_PREFERENCES, 0);
            boolean logEnvironOn = sp.getBoolean(Constants.LOGGING_ENVIRON_SWITCH, false);
            boolean logOn = sp.getBoolean(Constants.LOGGING_ON, false);
            if (logOn && logEnvironOn) {
                mLogManager.log(
                        new SimpleDateFormat("HH:mm:ss").format(new Date(System.currentTimeMillis())) + ","
                                + lightChart.getLigthValue() + "," + magnetChart.magnetVariation + ","
                                + cellTowerChart.currentASU,
                        Constants.LOG_FILE_TYPE[Constants.LOG_TYPE_ENVIRONMENT]);
            }

            return null;
        }

        //After calculation has been done, post the result to the user
        @Override
        protected void onPostExecute(String result2) {
            if (normalizedProbablities[0] > normalizedProbablities[1]
            //   && normalizedProbablities[0] >= normalizedProbablities[1]
            ) {//Indoor
                lastEnvironment = Constants.ENVIRON_INDOOR;//updating the condition for the comparison graph
                probabilityOfLastEnvironment = normalizedProbablities[0];
                //notifyUser(view ,"You are in indoor",R.drawable.indoor_icon, 1);//triggering the notification
                cellTowerChart.setPrevStatus(0);//set the status for the cell tower, to be used for checking previous status when unchanged.
            } else {
                /*if (normalizedProbablities[1] >normalizedProbablities[0] && normalizedProbablities[1] > normalizedProbablities[2]){//Semi outdoor
                   lastEnvironment =Constants.ENVIRON_SEMI_OUTDOOR;
                   probabilityOfLastEnvironment=normalizedProbablities[1];
                   cellTowerChart.setPrevStatus(1);
                }else{//Outdoor
                */ lastEnvironment = Constants.ENVIRON_OUTDOOR;
                probabilityOfLastEnvironment = normalizedProbablities[1];
                cellTowerChart.setPrevStatus(2);
                //}
            }

            if (pastEnvironments.size() == Constants.NO_OF_PAST_STATES_STORED) {
                pastEnvironments.remove(0);
            }
            pastEnvironments.add(lastEnvironment);
            String environText = ENVIRONMENT_PREFIX + CommonUtils.eventCodeToString(lastEnvironment);
            if (Constants.IS_DEBUG) {
                for (int i = 0; i < normalizedProbablities.length; i++) {
                    environText += " " + String.format("%.2f", normalizedProbablities[i]);
                }
            }

            environTextView.setText(environText + "  " + "light:" + String.format("%.1f", featureValues[0])
                    + ", RSS:" + String.format("%.1f", featureValues[1]));
            aggregationFinish = true;//calculation finish
        }
    }

    @Override
    protected void onResume() {
        Log.d(LOG_TAG, LOG_TAG + " activity has resumed");
        super.onResume();

        // restore the mapview        
        setupMapIfNeeded();

        // Register the broadcast receiver
        //mBroadcastManager.registerReceiver(updateListReceiver, mBroadcastFilter);

        // Register the sensor event listencer
        //mSensorManager.registerListener(mSensorEventListener,mAccelerometer, SensorManager.SENSOR_DELAY_GAME);

        // restore the GPS and bluetooth setting
        //checkGPSEnabled();       
    }

    @Override
    protected void onStart() {
        Log.e(LOG_TAG, LOG_TAG + " activity has started");
        super.onStart();
    }

    @Override
    protected void onPause() {
        Log.e(LOG_TAG, LOG_TAG + " activity has paused");
        super.onPause();

        // Stop listening to broadcasts when the Activity isn't visible.
        //mBroadcastManager.unregisterReceiver(updateListReceiver);

        /*  if (mLocationClient != null) {
        mLocationClient.disconnect();
          }*/
    }

    @Override
    protected void onStop() {
        Log.e(LOG_TAG, LOG_TAG + " activity has stopped");
        super.onStop();
    }

    @Override
    protected void onDestroy() {
        /*        if (mTextToSpeech != null) {
                   mTextToSpeech.stop();
                   mTextToSpeech.shutdown();
                }*/
        super.onDestroy();
    }

    /*
     * Create the menu
     */
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.menu, menu);
        return true;
    }

    /**
    * Handle Performance Tuning Click
    */
    private void handleAdvancedSetting() {
        final Dialog dialog = new Dialog(this);
        dialog.setTitle(R.string.menu_item_advanced_settings);
        dialog.setContentView(R.layout.advanced_setting);

        final SharedPreferences mPrefs = getSharedPreferences(Constants.SHARED_PREFERENCES, Context.MODE_PRIVATE);
        final Editor editor = mPrefs.edit();

        final ToggleButton classifierForCIVOnButton = (ToggleButton) dialog.findViewById(R.id.civ_classifier_on);
        classifierForCIVOnButton.setChecked(mPrefs.getBoolean(Constants.PREFERENCE_KEY_CIV_CLASSIFIER_ON, false));

        final ToggleButton isOutdoorButton = (ToggleButton) dialog.findViewById(R.id.is_outdoor);
        isOutdoorButton.setChecked(mPrefs.getBoolean(Constants.PREFERENCE_KEY_IS_OUTDOOR, false));

        final EditText notificationTresholdText = (EditText) dialog.findViewById(R.id.notification_threshold);
        notificationTresholdText
                .setText(String.format("%.2f", mPrefs.getFloat(Constants.PREFERENCE_KEY_NOTIFICATION_THRESHOLD,
                        (float) Constants.DEFAULT_DETECTION_THRESHOLD)));

        //final EditText detectionIntervalText=(EditText)dialog.findViewById(R.id.detection_interval);
        //detectionIntervalText.setText(String.valueOf(mPrefs.getInt(Constants.PREFERENCE_KEY_DETECTION_INTERVAL, Constants.DETECTION_INTERVAL_DEFAULT_VALUE) ));

        final EditText googleActivityUpdateIntervalText = (EditText) dialog
                .findViewById(R.id.google_activity_update_interval);
        googleActivityUpdateIntervalText
                .setText(String.valueOf(mPrefs.getInt(Constants.PREFERENCE_KEY_GOOGLE_ACTIVITY_UPDATE_INTERVAL,
                        Constants.GOOGLE_ACTIVITY_UPDATE_INTERVAL_DEFAULT_VALUE)));

        //final ToggleButton useGoogleActivityInFusion=(ToggleButton)dialog.findViewById(R.id.use_google_for_motion_state_in_fusion);
        //useGoogleActivityInFusion.setChecked(mPrefs.getBoolean(Constants.PREFERENCE_KEY_USE_GOOGLE_ACTIVITY_IN_FUSION, false));

        final ToggleButton logAcclRawButton = (ToggleButton) dialog.findViewById(R.id.log_raw_switch);
        logAcclRawButton.setChecked(mPrefs.getBoolean(Constants.LOGGING_ACCL_RAW_SWITCH, false));

        final ToggleButton logGoogleActUpdatesButton = (ToggleButton) dialog
                .findViewById(R.id.log_google_updates_switch);
        logGoogleActUpdatesButton.setChecked(mPrefs.getBoolean(Constants.LOGGING_GOOGLE_ACTIVITY_UPDATE, false));

        final ToggleButton logDetectionButton = (ToggleButton) dialog.findViewById(R.id.log_report_switch);
        logDetectionButton.setChecked(mPrefs.getBoolean(Constants.LOGGING_DETECTION_SWITCH, false));

        final ToggleButton logErrorButton = (ToggleButton) dialog.findViewById(R.id.log_error_switch);
        logErrorButton.setChecked(mPrefs.getBoolean(Constants.LOGGING_ERROR_SWITCH, true));

        //final EditText deltaForConditionalProb=(EditText)dialog.findViewById(R.id.normal_dist_delta);
        //deltaForConditionalProb.setText(String.valueOf(mPrefs.getFloat(Constants.CIV_DELTA_CONDITIONAL_PROBABILITY, 2)) );      

        final Button applyButton = (Button) dialog.findViewById(R.id.performance_apply_button);
        final Button cancelButton = (Button) dialog.findViewById(R.id.peformance_cancel_button);
        applyButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(final View v) {
                if (classifierForCIVOnButton.isChecked())
                    editor.putBoolean(Constants.PREFERENCE_KEY_CIV_CLASSIFIER_ON, true);
                else
                    editor.putBoolean(Constants.PREFERENCE_KEY_CIV_CLASSIFIER_ON, false);

                if (isOutdoorButton.isChecked())
                    editor.putBoolean(Constants.PREFERENCE_KEY_IS_OUTDOOR, true);
                else
                    editor.putBoolean(Constants.PREFERENCE_KEY_IS_OUTDOOR, false);

                if (logAcclRawButton.isChecked())
                    editor.putBoolean(Constants.LOGGING_ACCL_RAW_SWITCH, true);
                else
                    editor.putBoolean(Constants.LOGGING_ACCL_RAW_SWITCH, false);

                if (logGoogleActUpdatesButton.isChecked())
                    editor.putBoolean(Constants.LOGGING_GOOGLE_ACTIVITY_UPDATE, true);
                else
                    editor.putBoolean(Constants.LOGGING_GOOGLE_ACTIVITY_UPDATE, false);

                if (logDetectionButton.isChecked())
                    editor.putBoolean(Constants.LOGGING_DETECTION_SWITCH, true);
                else
                    editor.putBoolean(Constants.LOGGING_DETECTION_SWITCH, false);

                if (logErrorButton.isChecked())
                    editor.putBoolean(Constants.LOGGING_ERROR_SWITCH, true);
                else
                    editor.putBoolean(Constants.LOGGING_ERROR_SWITCH, false);

                float notificationTreshold;
                try {
                    notificationTreshold = Float.parseFloat(notificationTresholdText.getText().toString());
                } catch (Exception ex) {
                    notificationTreshold = (float) Constants.DEFAULT_DETECTION_THRESHOLD;
                }
                editor.putFloat(Constants.PREFERENCE_KEY_NOTIFICATION_THRESHOLD, notificationTreshold);

                /*int detectionInterval;
                try{
                   detectionInterval=Integer.parseInt(
                 detectionIntervalText.getText().toString());
                }catch(Exception ex){
                   detectionInterval=Constants.DETECTION_INTERVAL_DEFAULT_VALUE;
                }
                editor.putInt(Constants.PREFERENCE_KEY_DETECTION_INTERVAL, detectionInterval);*/

                /*if (useGoogleActivityInFusion.isChecked())
                   editor.putBoolean(Constants.PREFERENCE_KEY_USE_GOOGLE_ACTIVITY_IN_FUSION, true);
                else
                   editor.putBoolean(Constants.PREFERENCE_KEY_USE_GOOGLE_ACTIVITY_IN_FUSION, false);*/

                int googleActivityUpdateInterval;
                try {
                    googleActivityUpdateInterval = Integer
                            .parseInt(googleActivityUpdateIntervalText.getText().toString());
                } catch (Exception ex) {
                    googleActivityUpdateInterval = Constants.GOOGLE_ACTIVITY_UPDATE_INTERVAL_DEFAULT_VALUE;
                }
                editor.putInt(Constants.PREFERENCE_KEY_GOOGLE_ACTIVITY_UPDATE_INTERVAL,
                        googleActivityUpdateInterval);

                /*try{
                   Float delta=Float.parseFloat(deltaForConditionalProb.getText().toString());
                   editor.putFloat(Constants.CIV_DELTA_CONDITIONAL_PROBABILITY, delta);
                }catch(Exception ex){
                   Toast.makeText(getApplicationContext(), "Input must be a float number", Toast.LENGTH_SHORT).show();
                }*/

                editor.commit();
                dialog.cancel();
            }
        });

        cancelButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(final View v) {
                dialog.cancel();
            }
        });
        dialog.show();
    }

    /**
    * Handle Setting click
    */
    private void handleSettings() {
        final Dialog dialog = new Dialog(this);
        dialog.setTitle(R.string.menu_item_settings);
        dialog.setContentView(R.layout.settings);

        final SharedPreferences mPrefs = getSharedPreferences(Constants.SHARED_PREFERENCES, Context.MODE_PRIVATE);
        final Editor editor = mPrefs.edit();

        final ToggleButton logOnButton = (ToggleButton) dialog.findViewById(R.id.log_on);
        logOnButton.setChecked(mPrefs.getBoolean(Constants.LOGGING_ON, false));

        final Button btDeviceSelectButton = (Button) dialog.findViewById(R.id.bt_device_button);
        btDeviceSelectButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(final View v) {
                if (mBluetoothAdapter.isEnabled()) {
                    selectBluetoothDevice();
                } else {
                    Toast.makeText(getApplicationContext(), "Please enable your Bluetooth first.",
                            Toast.LENGTH_SHORT).show();
                }

            }
        });

        final Button applyButton = (Button) dialog.findViewById(R.id.apply_button);
        final Button cancelButton = (Button) dialog.findViewById(R.id.cancel_button);
        applyButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(final View v) {
                if (logOnButton.isChecked())
                    editor.putBoolean(Constants.LOGGING_ON, true);
                else
                    editor.putBoolean(Constants.LOGGING_ON, false);
                editor.commit();
                dialog.cancel();
            }
        });

        cancelButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(final View v) {
                dialog.cancel();
            }
        });
        dialog.show();
    }

    /*
     * Handle selections from the menu
     */
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        // Handle item selection
        switch (item.getItemId()) {
        /*            // Clear the log display and remove the log files
        case R.id.menu_item_clearlog:
            return true;
            
        // Display the update log
        case R.id.menu_item_showlog:                
           // Continue by passing true to the menu handler
            return true;*/
        case R.id.menu_item_settings:
            handleSettings();
            return true;

        case R.id.menu_item_showSensors:
            Intent i = new Intent(MainActivity.this, Sensors.class);
            startActivity(i);
            return true;

        case R.id.menu_item_advanced_settings:
            handleAdvancedSetting();
            return true;

        // For any other choice, pass it to the super()
        default:
            return super.onOptionsItemSelected(item);
        }
    }

    /** Make sure that GPS is enabled */
    public void checkGPSEnabled() {
        if (!mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
            Log.e(LOG_TAG, "GPS not enabled yet");
            /** Ask user to enable GPS */
            final AlertDialog enableGPS = new AlertDialog.Builder(this)
                    .setTitle(Constants.APP_NAME + " needs access to GPS. Please enable GPS.")
                    .setPositiveButton("Press here to enable GPS", new DialogInterface.OnClickListener() {
                        public void onClick(final DialogInterface dialog, final int id) {
                            startActivityForResult(
                                    new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS),
                                    Constants.SENSOR_GPS);
                        }
                    }).setCancelable(false).create();
            /*.setNegativeButton("Skip", new DialogInterface.OnClickListener() {
               public void onClick(@SuppressWarnings("unused") final DialogInterface dialog, @SuppressWarnings("unused") final int id) {
               }
            })*/

            enableGPS.show();
        } else {
            Log.e(LOG_TAG, "GPS already enabled");
            //GPS already enabled
            checkBluetoothEnabled();
        }
    }

    /** Make sure that Bluetooth is enabled */
    public void checkBluetoothEnabled() {
        if (mBluetoothAdapter == null) {
            // Device does not support Bluetooth
            AlertDialog noBluetoothAlert = new AlertDialog.Builder(this).setTitle("Bluetooth not supported.")
                    .setPositiveButton("Exit", new DialogInterface.OnClickListener() {
                        public void onClick(final DialogInterface dialog, final int id) {
                        }
                    }).setCancelable(true).create();
            noBluetoothAlert.show();
            writeToConsole(
                    "This phone does not have Bluetooth capability. Bluetooth connection method will not work.");
        }
        if (!mBluetoothAdapter.isEnabled()) {
            Log.e(LOG_TAG, "bluetooth not enabled yet");
            /** Ask user to enable Bluetooth */
            AlertDialog enableBluetoothDialog = new AlertDialog.Builder(this)
                    .setTitle("Please enable Bluetooth on your phone.").setCancelable(false)
                    .setPositiveButton("Enable Bluetooth", new DialogInterface.OnClickListener() {
                        public void onClick(final DialogInterface dialog, final int id) {
                            startActivityForResult(new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE),
                                    Constants.SENSOR_BLUETOOTH);
                        }
                    }).setNegativeButton("Skip", new DialogInterface.OnClickListener() {
                        public void onClick(final DialogInterface dialog, final int id) {
                        }
                    }).create();
            enableBluetoothDialog.show();
        } else {
            //bluetooth is enabled (directed from onActivityResult())         
            SharedPreferences mPrefs = getSharedPreferences(Constants.SHARED_PREFERENCES, Context.MODE_PRIVATE);
            String carBluetoothDeviceName = null;
            if (mPrefs.contains(Constants.BLUETOOTH_CAR_DEVICE_NAME)) {
                carBluetoothDeviceName = mPrefs.getString(Constants.BLUETOOTH_CAR_DEVICE_NAME, null);
            }
            Log.e(LOG_TAG, "bluetooth enabled " + carBluetoothDeviceName);

            if (carBluetoothDeviceName != null) {
                Intent intent = new Intent(MainActivity.this, BluetoothConnectionService.class);
                startService(intent);
            } else {//ask the user to select a car bluetooth device
                selectBluetoothDevice();
            }
        }
    }

    /*
      * Handle results returned to this Activity by other Activities started with
      * startActivityForResult(). In particular, the method onConnectionFailed() in
      * DetectionRemover and DetectionRequester may call startResolutionForResult() to
      * start an Activity that handles Google Play services problems. The result of this
      * call returns here, to onActivityResult.
      */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
        // Choose what to do based on the request code
        Log.e(LOG_TAG, requestCode + "  " + requestCode);
        switch (requestCode) {
        case Constants.SENSOR_GPS:
            checkBluetoothEnabled();
            break;
        case Constants.SENSOR_BLUETOOTH:
            if (mBluetoothAdapter.isEnabled()) {//only if the user enables the bluetooth
                checkBluetoothEnabled();
            }
            break;
        // If the request code matches the code sent in onConnectionFailed
        case Constants.CONNECTION_FAILURE_RESOLUTION_REQUEST:
            switch (resultCode) {
            // If Google Play services resolved the problem
            case Activity.RESULT_OK:
                // If the request was to start activity recognition updates
                if (Constants.REQUEST_TYPE.ADD == mRequestType) {
                    // Restart the process of requesting activity recognition
                    // updates
                    mGoogleActivityDetectionRequester.requestUpdates();
                    // If the request was to remove activity recognition updates
                } else if (Constants.REQUEST_TYPE.REMOVE == mRequestType) {
                    /*
                     * Restart the removal of all activity recognition updates
                     * for the PendingIntent.
                     */
                    mGoogleActivityDetectionRemover
                            .removeUpdates(mGoogleActivityDetectionRequester.getRequestPendingIntent());

                }
                break;

            // If any other result was returned by Google Play services
            default:
                // Report that Google Play services was unable to resolve the
                // problem.
                Log.d(Constants.APP_NAME, getString(R.string.no_resolution));
            }
            // If any other request code was received
        default:
            // Report that this Activity received an unknown requestCode
            Log.d(Constants.APP_NAME, getString(R.string.unknown_activity_request_code, requestCode));
            break;
        }
    }

    private String selectedBloothDeviceName = null;

    public void selectBluetoothDevice() {
        Set<BluetoothDevice> bluetoothDevices = mBluetoothAdapter.getBondedDevices();
        final CharSequence[] listItems = new CharSequence[bluetoothDevices.size()];
        int i = 0;
        for (BluetoothDevice device : mBluetoothAdapter.getBondedDevices()) {
            String device_name = device.getName();
            listItems[i++] = device_name;
        }

        AlertDialog select = new AlertDialog.Builder(this).setTitle(R.string.set_bluetooth_message)
                .setSingleChoiceItems(listItems, -1, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int whichButton) {
                        Log.e(LOG_TAG, "id=" + whichButton);
                        if (whichButton >= 0)
                            selectedBloothDeviceName = listItems[whichButton].toString();
                    }
                }).setPositiveButton("OK", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int whichButton) {
                        Log.e(LOG_TAG, selectedBloothDeviceName);
                        Toast.makeText(getApplicationContext(),
                                getString(R.string.bluetooth_device_selected, selectedBloothDeviceName),
                                Toast.LENGTH_SHORT).show();

                        final SharedPreferences mPrefs = getSharedPreferences(Constants.SHARED_PREFERENCES,
                                Context.MODE_PRIVATE);
                        Editor editor = mPrefs.edit();
                        editor.putString(Constants.BLUETOOTH_CAR_DEVICE_NAME, selectedBloothDeviceName);
                        editor.commit();
                    }
                }).setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int whichButton) {
                    }
                }).create();
        select.show();
    }

    /** Write a string to output console */
    public void writeToConsole(String str) {
        consoleTextView.append(str);
        final Layout layout = consoleTextView.getLayout();
        if (layout != null) {
            int scrollDelta = layout.getLineBottom(consoleTextView.getLineCount() - 1)
                    - consoleTextView.getScrollY() - consoleTextView.getHeight();
            if (scrollDelta > 0)
                consoleTextView.scrollBy(0, scrollDelta);
        }
    }

    private void setupMapIfNeeded() {
        // Do a null check to confirm that we have not already instantiated the map.
        if (mMap == null) {
            // Try to obtain the map from the SupportMapFragment.
            mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map)).getMap();
            // Check if we were successful in obtaining the map.
            if (mMap != null) {
                mMap.setMyLocationEnabled(true);

                // Setting an info window adapter allows us to change the both the contents and look of the
                // info window.
                mMap.setInfoWindowAdapter(new MarkerInfoWindowAdapter(getLayoutInflater()));
            }
        }
    }

    private void setupLocationClientIfNeeded() {
        if (mLocationClient == null) {
            mLocationClient = new LocationClient(getApplicationContext(), this, // ConnectionCallbacks
                    this); // OnConnectionFailedListener
            if (mLocationClient != null) {
                mLocationClient.connect();

            }
        }
    }

    // actions taken when a parking/unparking event is detected and the location of the event is retrieved
    private void onLocationRetrieved(int eventCode, double detectionConfidence, long detectionTimestamp,
            int detectionTimeDelayInSecs, Location location, String address) {
        boolean logDetection = getSharedPreferences(Constants.SHARED_PREFERENCES, 0)
                .getBoolean(Constants.LOGGING_DETECTION_SWITCH, false);
        if (logDetection) {
            String logMsg = (eventCode == Constants.OUTCOME_PARKING ? Constants.PARKING_NOTIFICATION
                    : Constants.UNPARKING_NOTIFICATION)
                    + CommonUtils.formatTimestamp(new Date(detectionTimestamp), "HH:mm:ss   ")
                    + "\ndetection confidence is " + detectionConfidence + "\nlocatoin retrieval time:"
                    + CommonUtils.formatTimestamp(new Date(location.getTime()), "HH:mm:ss  ") + "\nlocation:"
                    + location.toString() + "\n";

            if (address != null) {
                logMsg += "address retrieved at "
                        + CommonUtils.formatTimestamp(new Date(System.currentTimeMillis()), "HH:mm:ss ") + address
                        + "\n";
            } else
                logMsg += "address is not available. \n";

            logMsg += pastEnvironments.toString() + "\n" + mPastClassifiedMotionStates.toString() + "\n"
                    + mPastGoogleActivities.toString() + "\n" + mPastGoogleActivtiesUnModified.toString() + "\n";
            mLogManager.log(logMsg, Constants.LOG_FILE_TYPE[Constants.LOG_TYPE_DETECTION_REPORT]);
        }

        MotionState.Type toStateType;
        CachedDetectionList cdl;
        CachedDetection.Type cdType;
        if (eventCode == Constants.OUTCOME_PARKING) {//parking
            toStateType = MotionState.Type.ON_FOOT;
            cdl = mCachedParkingDetectionList;
            cdType = CachedDetection.Type.Parking;
        } else {
            toStateType = MotionState.Type.IN_VEHICLE;
            cdl = mCachedUnparkingDetectionList;
            cdType = CachedDetection.Type.Unparking;
        }
        int secsSinceLastFromState;
        secsSinceLastFromState = mPastGoogleActivities.isTransitionTo(toStateType);
        //      secsSinceLastFromState=mPastGoogleActivtiesUnModified.isTransitionTo(toStateType);

        if (detectionConfidence == 1.0 || secsSinceLastFromState != -1) {
            onDetectionConfirmed(eventCode, detectionTimestamp, location, address, detectionTimeDelayInSecs);
        } else {
            CachedDetection cd = new CachedDetection(cdType, location, detectionTimestamp, address,
                    detectionConfidence);
            cdl.add(cd);
        }
    }

    //TODO on detection confirmed
    public void onDetectionConfirmed(int eventCode, long eventDetectionTimestamp, Location eventlocation,
            String eventAddress, long detectionDelayInSecs) {
        MotionState lastOnFootOrInVehicleState = mPastGoogleActivtiesUnModified.lastOnFootOrInVehicleState();
        if (lastOnFootOrInVehicleState != null
                && lastOnFootOrInVehicleState.timeInHMS.equals(timeOfLastGoogleOnFootorInVehicleState)) {
            return;
        }
        timeOfLastGoogleOnFootorInVehicleState = lastOnFootOrInVehicleState.timeInHMS;

        @SuppressWarnings("unused")
        int resID;
        String prefix;
        float markerColor;
        String voiceNotificationMsg;
        if (eventCode == Constants.OUTCOME_PARKING) {
            resID = R.raw.vehicle_parked;
            prefix = Constants.PARKING_NOTIFICATION;
            markerColor = BitmapDescriptorFactory.HUE_AZURE;
            voiceNotificationMsg = "Vehicle Parked ";
        } else {//unparking
            resID = R.raw.vehicle_deparked;
            prefix = Constants.UNPARKING_NOTIFICATION;
            markerColor = BitmapDescriptorFactory.HUE_RED;
            voiceNotificationMsg = "Vehicle Departured ";
        }

        //String curTimeString=CommonUtils.formatTimestamp(new Date(),formatTemplate);
        long curTimestamp = System.currentTimeMillis();
        String curTimeString = CommonUtils.formatTimestamp(new Date(curTimestamp), "HH:mm:ss");
        Log.e(LOG_TAG, curTimeString + " \n" + eventlocation.toString());

        /*
         * actions
         */
        //1. send the text notification
        String notificationMsg = prefix + " " + curTimeString;
        if (eventAddress != null)
            notificationMsg += eventAddress;
        mDetectionNotificationManager.sendTextNotification(notificationMsg);

        //2. play the sound
        //2.1 announce the type
        //mDetectionNotificationManager.playVoiceNotification(resID);
        //2.2 announce the time
        mTextToSpeech.speak(voiceNotificationMsg, TextToSpeech.QUEUE_FLUSH, null);

        //3. log the address of event
        String logMsg = prefix + CommonUtils.formatTimestamp(new Date(eventDetectionTimestamp), "HH:mm:ss ")
                + "\nNotification generation time:" + curTimeString + "\nDetection timestamp: "
                + CommonUtils.formatTimestamp(new Date(eventDetectionTimestamp), "HH:mm:ss ")
                + "\nDetection delay: " + detectionDelayInSecs + " secs" + "\nlocation:" + eventlocation.toString()
                + "\n";
        if (eventAddress != null)
            logMsg += eventAddress + "\n";
        else
            logMsg += "address is not available. \n";
        logMsg += pastEnvironments.toString() + "\n" + mPastClassifiedMotionStates.toString() + "\n"
                + mPastGoogleActivities.toString() + "\n" + mPastGoogleActivtiesUnModified.toString() + "\n"
                + "timeOfLastGoogleOnFootorInVehicleState=" + timeOfLastGoogleOnFootorInVehicleState + "\n";
        boolean logDetection = getSharedPreferences(Constants.SHARED_PREFERENCES, 0)
                .getBoolean(Constants.LOGGING_DETECTION_SWITCH, false);
        if (logDetection)
            mLogManager.log(logMsg, Constants.LOG_FILE_TYPE[Constants.LOG_TYPE_DETECTION_REPORT]);

        //4. show on the map
        mMap.clear();
        mDetectionNotificationManager.addMarkersToMap(mMap, curTimeString, prefix, eventlocation.getLatitude(),
                eventlocation.getLongitude(), eventlocation.getAltitude(), markerColor);
        //center and zoom in the map
        CameraPosition cameraPosition = new CameraPosition.Builder()
                .target(new LatLng(eventlocation.getLatitude(), eventlocation.getLongitude())) // Sets the center of the map to Mountain View
                .zoom(17) // Sets the zoom
                //.bearing(90)                // Sets the orientation of the camera to east
                //.tilt(30)                   // Sets the tilt of the camera to 30 degrees
                .build(); // Creates a CameraPosition from the builder
        mMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
        //add a marker on the map            
        Log.e(LOG_TAG, "operations on map completed");

        //5. update availability display
        //updateAvailabilityDisplay(eventCode, location);
        //add a marker on the map            
        Log.e(LOG_TAG, "operations on map completed");
        updateAvailabilityDisplay(eventCode, eventlocation);
    }

    public void updateAvailabilityDisplay(int eventCode, Location location) {
        //mMap.clear();

        //        for (int i = 0; i < parkingBlocks.size(); i++) {
        //           ParkingBlock parkingBlock = parkingBlocks.elementAt(i);
        //           PolylineOptions line =  
        //                 new PolylineOptions().add(parkingBlock.startLocation, parkingBlock.endLocation)
        //                 .width(20).color(Color.RED);
        //           Polyline polyline = mMap.addPolyline(line);
        //           parkingBlock.display = polyline;
        //        }

        //        for (int i = 0; i < parkingBlocks.size(); i++) {
        //           ParkingBlock parkingBlock = parkingBlocks.elementAt(i);
        //           parkingBlock.display.setColor(Color.RED);;
        //        }

        // find closest street block within 30 meters
        LatLng point = new LatLng(location.getLatitude(), location.getLongitude());
        double minDist = Double.MAX_VALUE;
        ParkingBlock matchedBlock = null;
        for (int i = 0; i < parkingBlocks.size(); i++) {
            ParkingBlock parkingBlock = parkingBlocks.elementAt(i);
            double dist = parkingBlock.distanceToPoint(point);
            if (dist < minDist) {
                minDist = dist;
                matchedBlock = parkingBlock;
            }
        }
        if (matchedBlock != null) {
            //Toast.makeText(getApplicationContext(), "a block matched", 2).show();           
            if (eventCode == Constants.OUTCOME_PARKING) {
                matchedBlock.availability = 0;
            } else {
                matchedBlock.availability = 1;
            }
        }
        showAvailabilityMap();
        //        for (int i = 0; i < parkingBlocks.size(); i++) {
        //             ParkingBlock parkingBlock = parkingBlocks.elementAt(i);
        //           PolylineOptions line =  
        //             new PolylineOptions().add(parkingBlock.startLocation, parkingBlock.endLocation)
        //             .width(20).color(parkingBlock.getColorByAvailability());
        //           Polyline polyline = mMap.addPolyline(line);
        //           parkingBlock.display = polyline;
        //        }        
    }

    public void showAvailabilityMap() {
        //MapFragment mapFragment = (MapFragment)getFragmentManager().findFragmentById(R.id.map);
        //GoogleMap map = mapFragment.getMap();
        //GoogleMap map = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map))
        //.getMap();
        //        VisibleRegion bounds = map.getProjection().getVisibleRegion();
        //        LatLngBounds xyBounds = bounds.latLngBounds;
        //        String msg = "northest = " + xyBounds.northeast;
        //        CameraUpdate center=
        //            CameraUpdateFactory.newLatLng(new LatLng(41.909348,
        //                      -87.777664));
        //            CameraUpdate zoom=CameraUpdateFactory.zoomTo(15);
        //
        //            mMap.moveCamera(center);
        //            mMap.animateCamera(zoom);        

        for (int i = 0; i < parkingBlocks.size(); i++) {
            ParkingBlock parkingBlock = parkingBlocks.elementAt(i);
            PolylineOptions line = new PolylineOptions().add(parkingBlock.startLocation, parkingBlock.endLocation)
                    .width(20).color(parkingBlock.getColorByAvailability());
            Polyline polyline = mMap.addPolyline(line);
            parkingBlock.display = polyline;
            //map.addMarker(new MarkerOptions().position(parkingBlock.meterLocation)
            //.title(parkingBlock.meterAddress));
        }
    }

    @Override
    public void onConnected(Bundle arg0) {
        /***
           * Use periodical GPS to test energy consumption
           */
        /*mLocationClient.requestLocationUpdates( 
           LocationRequest.create()
           .setInterval(1000*60)
           .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY), 
          new LocationListener() {
              @Override
              public void onLocationChanged(Location arg0) {
              }
           });*/
    }

    @Override
    public void onConnectionFailed(ConnectionResult arg0) {
    }

    @Override
    public void onDisconnected() {
    }

    /**
     * Legacy codes
     */

    //private double calibration = 0.0;
    private double currentAcceleration;
    private double appliedAcceleration = 0;
    private Date lastUpdate;

    @SuppressWarnings("unused")
    private double calVelocityIncrease() {
        // Calculate how long this acceleration has been applied.
        Date timeNow = new Date(System.currentTimeMillis());
        double timeDelta = timeNow.getTime() - lastUpdate.getTime();
        lastUpdate.setTime(timeNow.getTime());

        // Calculate the change in velocity
        // current acceleration since the last update. 
        double deltaVelocity = appliedAcceleration * (timeDelta / 1000);
        appliedAcceleration = currentAcceleration;

        // Add the velocity change to the current velocity.
        return deltaVelocity;
    }

    /**
     * Verify that Google Play services is available before making a request.
     *
     * @return true if Google Play services is available, otherwise false
     */
    private boolean isGooglePlayServiceAvailable() {

        // Check that Google Play services is available
        int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);

        // If Google Play services is available
        if (ConnectionResult.SUCCESS == resultCode) {

            // In debug mode, log the status
            Log.d(Constants.APP_NAME, getString(R.string.play_services_available));

            // Continue
            return true;

            // Google Play services was not available for some reason
        } else {

            // Display an error dialog
            GooglePlayServicesUtil.getErrorDialog(resultCode, this, 0).show();
            return false;
        }
    }

    /**
     * Respond to "Start" button by requesting activity recognition
     * updates.
     * @param view The view that triggered this method.
     */
    public void startGoogleActivityRecognitionUpdates(View view) {

        // Check for Google Play services
        if (!isGooglePlayServiceAvailable()) {
            return;
        }

        /*
         * Set the request type. If a connection error occurs, and Google Play services can
         * handle it, then onActivityResult will use the request type to retry the request
         */
        mRequestType = Constants.REQUEST_TYPE.ADD;

        // Pass the update request to the requester object
        mGoogleActivityDetectionRequester.requestUpdates();
    }

    /**
     * Respond to "Stop" button by canceling updates.
     * @param view The view that triggered this method.
     */
    public void stopGoogleActivityRecognitionUpdates(View view) {

        // Check for Google Play services
        if (!isGooglePlayServiceAvailable()) {
            return;
        }

        /*
         * Set the request type. If a connection error occurs, and Google Play services can
         * handle it, then onActivityResult will use the request type to retry the request
         */
        mRequestType = Constants.REQUEST_TYPE.REMOVE;

        // Pass the remove request to the remover object
        mGoogleActivityDetectionRemover.removeUpdates(mGoogleActivityDetectionRequester.getRequestPendingIntent());

        /*
         * Cancel the PendingIntent. Even if the removal request fails, canceling the PendingIntent
         * will stop the updates.
         */
        PendingIntent pIntent = mGoogleActivityDetectionRequester.getRequestPendingIntent();
        if (pIntent != null)
            pIntent.cancel();
    }

    /**
     * Display the activity detection history stored in the
     * log file
     */
    /*private void updateActivityHistory() {
    // Try to load data from the history file
    try {
        // Load log file records into the List
        List<Spanned> activityDetectionHistory =
                mLogManager.loadLogFile();
        
        // Clear the adapter of existing data
        mStatusAdapter.clear();
        
        // Add each element of the history to the adapter
        for (Spanned activity : activityDetectionHistory) {
            mStatusAdapter.add(activity);
        }
        
        // If the number of loaded records is greater than the max log size
        if (mStatusAdapter.getCount() > Constants.MAX_LOG_SIZE) {
               
           AlertDialog.Builder builder = new AlertDialog.Builder(this);
           builder.setMessage("File is too large to be shown.")
                  .setCancelable(true);
           final AlertDialog alert = builder.create();
           alert.show();
               
            // Delete the old log file
            if (!mLogFile.removeLogFiles()) {
        
                // Log an error if unable to delete the log file
                Log.e(Constants.APPTAG, getString(R.string.log_file_deletion_error));
            }
        }
        
        // Trigger the adapter to update the display
        mStatusAdapter.notifyDataSetChanged();
        
    // If an error occurs while reading the history file
    } catch (IOException e) {
        Log.e(Constants.APP_NAME, e.getMessage(), e);
    }
    }*/

    /**
     * Broadcast receiver that receives activity update intents
     * It checks to see if the ListView contains items. If it
     * doesn't, it pulls in history.
     * This receiver is local only. It can't read broadcast Intents from other apps.
     */
    BroadcastReceiver updateListReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) { /*
                                                                * When an Intent is received from the update listener IntentService, update
                                                                * the displayed log.
                                                                */
            //do not execute an update to avoid freezing the app
            //updateActivityHistory();
        }
    };

    @Override
    //of TextToSpeech
    public void onInit(int status) {
        if (status == TextToSpeech.SUCCESS) {
            // set Language
            int result = mTextToSpeech.setLanguage(Locale.US);
            // tts.setPitch(5); // set pitch level
            // tts.setSpeechRate(2); // set speech speed rate
            if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) {
            }
            {
                Log.e(LOG_TAG, "Text-To-Speech Language cannot be setted US. Error code=" + result);
            }
            //mTextToSpeech.speak("test", TextToSpeech.QUEUE_FLUSH, null);
        } else {
            Log.e(LOG_TAG, "Text-To-Speech Initilization Failed");
        }
    }

}

class PastMotionStates {
    public int capacity;
    public Source source;
    public HashMap<MotionState.Type, Integer> map;
    public ArrayList<MotionState> list;

    public long timestampOfLastInVehicleState;
    public long timestampOfLastOnFootState;
    public static final long EXPIRATION_TIME_IN_MILLISEC = Constants.ONE_MINUTE + Constants.ONE_MINUTE / 2;

    public PastMotionStates(Source source, int capacity) {
        this.source = source;
        this.capacity = capacity;
        map = new HashMap<MotionState.Type, Integer>();
        list = new ArrayList<MotionState>();
    }

    public void clear() {
        map.clear();
        list.clear();
    }

    public void add(MotionState state) {
        if (list.size() == capacity) {
            MotionState removedMotionState = list.remove(0);// remove the oldest state
            map.put(removedMotionState.type, map.get(removedMotionState.type) - 1);
        }
        list.add(state);
        Type stateType = state.type;
        if (!map.containsKey(stateType))
            map.put(stateType, 0);
        map.put(stateType, map.get(stateType) + 1);
    }

    public void removeAll(MotionState.Type stateType) {
        ArrayList<MotionState> toBeRemovedStates = new ArrayList<MotionState>();
        for (int i = 0; i < list.size(); i++) {
            MotionState ms = list.get(i);
            if (ms.type == stateType)
                toBeRemovedStates.add(ms);
        }
        for (MotionState ms : toBeRemovedStates) {
            list.remove(ms);
        }
        map.remove(stateType);
    }

    /**
     * 
     * @param toState
     * @return secs elapsed since the state that is right after the last fromState
     */
    public int isTransitionTo(MotionState.Type toState) {
        int secsElapsedSinceFromState = -1;
        if (toState != Type.IN_VEHICLE && toState != Type.ON_FOOT)
            return secsElapsedSinceFromState;

        Type fromState = Type.OTHER;
        int noOfFromStateNeeded = 2, noOfToStateNeeded = 2;
        switch (toState) {
        case ON_FOOT:
            fromState = Type.IN_VEHICLE; //from state is in_vehicle (at least 2)
            //noOfFromStateNeeded++;
            break;
        case IN_VEHICLE:
            fromState = Type.ON_FOOT;
            //noOfFromStateNeeded++;
            break;
        default:
            break;
        }

        int fromStateCnt = 0, toStateCnt = 0;
        int secOfLatestToState = -1, secOfPrevOfLastFromState = 0;
        MotionState prev = null;

        //scan backwards
        for (int i = list.size() - 1; i >= 0; i--) {
            MotionState cur = list.get(i);
            if (cur.type == fromState || cur.type == toState) {//ignore non-walk-drive states

                if (cur.type == fromState) {
                    if (toStateCnt != noOfToStateNeeded)
                        break; //not seen toState yet, not transiting
                    else {
                        fromStateCnt++;
                        if (fromStateCnt == 1) {
                            if (prev != null)
                                secOfPrevOfLastFromState = CommonUtils.HMSToSecs(prev.timeInHMS);
                            else
                                secOfPrevOfLastFromState = CommonUtils.HMSToSecs(cur.timeInHMS);
                        }
                        if (fromStateCnt == noOfFromStateNeeded) {
                            secsElapsedSinceFromState = secOfLatestToState - secOfPrevOfLastFromState;
                            break;
                        }
                    }
                }
                if (cur.type == toState) {
                    toStateCnt++;
                    if (toStateCnt == 1) {
                        secOfLatestToState = CommonUtils.HMSToSecs(cur.timeInHMS);
                    } else {
                        if (toStateCnt > noOfToStateNeeded)
                            break;
                    }
                }

            }
            prev = cur;
        }

        return secsElapsedSinceFromState;
    }

    public MotionState lastOnFootOrInVehicleState() {
        for (int i = list.size() - 1; i >= 0; i--) {
            MotionState cur = list.get(i);
            if (cur.type != Type.ON_FOOT && cur.type != Type.IN_VEHICLE)
                continue;
            return cur;
        }
        return null;
    }

    public boolean containsAtLeastMOnFootAndAtLeastNInVehicleStates(int mOnFoot, int nInVehicle) {
        // return false if the filter fails
        if (!map.containsKey(MotionState.Type.ON_FOOT) || !map.containsKey(MotionState.Type.IN_VEHICLE))
            return false;
        int walkingCnt = map.get(MotionState.Type.ON_FOOT);
        int drivingCnt = map.get(MotionState.Type.IN_VEHICLE);
        // Log.e(LOG_TAG,"#Walk="+walkingCnt+" #Drive="+drivingCnt);
        if (walkingCnt < mOnFoot || drivingCnt < nInVehicle)
            return false;
        return true;
    }

    /**
     * 
     * @param typeOfToState
     * @return elapsed secs since last from state
     */
    public int containsOnlyOneAndIsLaterThanFromState(MotionState.Type typeOfToState) {
        int secsElapsedSinceFromState = -1;
        if (!map.containsKey(typeOfToState) || map.get(typeOfToState) != 1)
            return secsElapsedSinceFromState;

        MotionState toState = null;
        for (int i = list.size() - 1; i >= 0; i--) {//from the last state
            MotionState cur = list.get(i);
            if (cur.type != MotionState.Type.ON_FOOT && cur.type != MotionState.Type.IN_VEHICLE)
                continue;
            if (cur.type == typeOfToState) {
                toState = cur;
            } else {
                if (toState != null) {
                    secsElapsedSinceFromState = CommonUtils.HMSToSecs(toState.timeInHMS)
                            - CommonUtils.HMSToSecs(cur.timeInHMS);
                }
                break;
            }
        }
        return secsElapsedSinceFromState;
    }

    public String toString() {
        String ret = list.toString() + "\n";
        for (Type type : map.keySet())
            ret += type.toString() + ":" + map.get(type) + "  ";
        return ret;
    }
}

class MotionState {

    public enum Source {
        Google, Classifier;
    }

    public enum Type {
        ON_FOOT("On_Foot"), IN_VEHICLE("In_Vehicle"), STILL("Still"), UNKNOWN("Unknown"), ON_BIKE("On_Bike"), OTHER(
                "Other");

        private String typeString;

        private Type(String type) {
            this.typeString = type;
        }

        public String toString() {
            return typeString;
        }
    }

    public Source source;
    public Type type;
    public String timeInHMS;

    public MotionState(Source source, Type type, String timeInHMS) {
        this.source = source;
        this.type = type;
        this.timeInHMS = timeInHMS;
    }

    public static MotionState.Type translate(String predClass) {
        MotionState.Type ret;
        if ("Walking".equals(predClass)) {
            ret = MotionState.Type.ON_FOOT;
        } else {
            if ("Driving".equals(predClass))
                ret = MotionState.Type.IN_VEHICLE;
            else {
                if ("Still".equals(predClass))
                    ret = MotionState.Type.STILL;
                else
                    ret = MotionState.Type.OTHER;
            }
        }
        return ret;
    }

    public static MotionState.Type translate(int activityTypeDefinedByGoogle) {
        MotionState.Type ret;
        switch (activityTypeDefinedByGoogle) {
        case DetectedActivity.ON_FOOT:
            ret = MotionState.Type.ON_FOOT;
            break;
        case DetectedActivity.IN_VEHICLE:
            ret = MotionState.Type.IN_VEHICLE;
            break;
        case DetectedActivity.STILL:
            ret = MotionState.Type.STILL;
            break;
        case DetectedActivity.ON_BICYCLE:
            ret = MotionState.Type.ON_BIKE;
        default:
            ret = MotionState.Type.UNKNOWN;
            break;
        }
        return ret;
    }

    public String toString() {
        return timeInHMS + "-" + type;
    }
}

class CachedDetection {
    public enum Type {
        Parking, Unparking
    }

    public long timestamp;
    public Location location;
    public String address;
    public Type type;
    public double confidence;

    public static final long EXPIRATION_TIME = Constants.ONE_MINUTE;

    public CachedDetection(Type type, Location loc, long locationRequestTime, String address, double confidence) {
        timestamp = locationRequestTime;
        location = loc;
        this.type = type;
        this.address = address;
        this.confidence = confidence;
    }

}

class CachedDetectionList {
    CachedDetection.Type type;
    ArrayList<CachedDetection> list;

    public CachedDetectionList(CachedDetection.Type type) {
        this.type = type;
        list = new ArrayList<CachedDetection>();
    }

    public void removeExpiredCachedDetection() {
        //remove expired cached detections
        long curtime = System.currentTimeMillis();
        int i;
        ArrayList<CachedDetection> newList = new ArrayList<CachedDetection>();
        for (i = 0; i < list.size(); i++) {
            if (curtime - list.get(i).timestamp <= CachedDetection.EXPIRATION_TIME) {
                newList.add(list.get(i));
            }
        }
        list = newList;
    }

    public void add(CachedDetection cd) {
        removeExpiredCachedDetection();
        //add the new one
        list.add(cd);
    }

    public CachedDetection get(int index) {
        removeExpiredCachedDetection();
        if (index < 0 || index >= list.size())
            return null;
        return list.get(index);
    }

    public CachedDetection getWithLargestProbability() {
        removeExpiredCachedDetection();

        if (list.size() <= 0)
            return null;
        if (list.size() == 1)
            return list.get(0);

        //sort 
        ArrayList<CachedDetection> sortedList = new ArrayList<CachedDetection>();
        sortedList.addAll(list);
        Collections.sort(sortedList, new Comparator<CachedDetection>() {
            @Override
            public int compare(CachedDetection lhs, CachedDetection rhs) {
                double diff = lhs.confidence - rhs.confidence;
                if (diff > 0)
                    return 1;
                else if (diff < 0)
                    return -1;
                else
                    return 0;
            }

        });
        return sortedList.get(sortedList.size() - 1);
    }

}