com.TwentyCodes.android.IOIOTruck.NavigationActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.TwentyCodes.android.IOIOTruck.NavigationActivity.java

Source

/**
 * NavigationActivity.java
 * @date Jan 7, 2012
 * @author ricky barrette
 * @author Twenty Codes, LLC
 * 
 * 
 * Copyright 2012 Richard Barrette 
 * 
 * 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.TwentyCodes.android.IOIOTruck;

import java.util.ArrayList;

import android.content.Context;
import android.graphics.Color;
import android.os.Bundle;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.ProgressBar;
import android.widget.ScrollView;
import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;

import com.TwentyCodes.android.IOIOTruck.IOIOTruckConnectionManager.IOIOTruckThreadListener;
import com.TwentyCodes.android.location.CompassSensor.CompassListener;
import com.TwentyCodes.android.location.GeoPointLocationListener;
import com.TwentyCodes.android.location.GeoUtils;
import com.TwentyCodes.android.location.OnLocationSelectedListener;
import com.TwentyCodes.android.overlays.DirectionsOverlay;
import com.TwentyCodes.android.overlays.DirectionsOverlay.OnDirectionsCompleteListener;
import com.TwentyCodes.android.overlays.PathOverlay;
import com.google.android.maps.GeoPoint;

/**
 * This activity will be used to interact with the IOIO and setup autonomous routines
 * 
 * The end goal of this activity is to:
 *  + have the user select a point on the map
 *  + have the IOIO drive the rc truck to the point on the map when the user starts the autonomous routine
 *  
 * TODO
 *  + drive the truck forward or reverse to best navigate to the selected point
 * @author ricky barrette
 */
public class NavigationActivity extends FragmentActivity
        implements CompassListener, GeoPointLocationListener, OnLocationSelectedListener, OnClickListener,
        OnCheckedChangeListener, IOIOTruckThreadListener, OnDirectionsCompleteListener {

    /**
     * This thread will be used to update all the informational displays
     * @author ricky barrette
     */
    class LogUpdater extends Thread {

        private boolean isAborted;

        /**
         * aborts the thread 
         * @author ricky barrette
         */
        public synchronized void abort() {
            isAborted = true;
        }

        @Override
        public void run() {
            while (!isAborted) {
                updateLog("\nDistance: " + mDistance + getString(R.string.m) + "\nDrive: "
                        + mIOIOManager.getDriveValue() + "\nSteering: " + mIOIOManager.getSteerValue()
                        + "\nBearing: " + mBearing + "\nisRunning: " + isRunning);
                if (mPoints != null)
                    updateLog("Point = " + mIndex + " of " + mPoints.size());
                updateLastUpdateTextView();
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        /**
         * updates the last update textview 
         * thread safe
         * @author ricky barrette
         */
        private void updateLastUpdateTextView() {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    mLastUpdateTextView.setText((System.currentTimeMillis() - mLast) + getString(R.string.ms));
                }
            });

        }

    }

    private static final String TAG = "NavigationActivity";
    private IOIOTruckConnectionManager mIOIOManager;
    private MapFragment mMap;
    private TextView mLog;
    private ProgressBar mProgress;
    private boolean isRunning = false;
    private Button mGoButton;
    private float mBearing;
    private ScrollView mScrollView;
    private boolean isScrollingEnabled = true;
    private int mDistance;
    private LogUpdater mLoggerThread;
    private TextView mAccuracyTextView;
    private TextView mLastUpdateTextView;
    private long mLast;
    private WakeLock mWakeLock;
    private int mCount = 0;
    private ArrayList<GeoPoint> mPoints;
    private int mIndex = 0;
    private GeoPoint mDestPoint;
    private ArrayList<PathOverlay> mWayPoints;
    private GeoPoint mLastReportedLocation;
    private float mHeading = 0;
    private long mLastReportedTime;

    /**
     * Called when the scrolling switch is checked
     * (non-Javadoc)
     * @see android.widget.CompoundButton.OnCheckedChangeListener#onCheckedChanged(android.widget.CompoundButton, boolean)
     */
    @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        isScrollingEnabled = isChecked;
    }

    /**
     * Called when a button is clicked
     * (non-Javadoc)
     * @see android.view.View.OnClickListener#onClick(android.view.View)
     */
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.go_button:
            updateGoButton();
            break;

        case R.id.map_button:
            mMap.changeMapMode();
            break;

        case R.id.mark_my_lcoation_button:
            final GeoPoint point = mMap.getUserLocation();

            if (point != null) {
                mMap.onLocationSelected(point);
            } else
                Toast.makeText(this, R.string.no_gps_signal, Toast.LENGTH_SHORT).show();
            break;

        case R.id.my_location_button:
            final GeoPoint user = mMap.getUserLocation();

            if (user != null) {
                mMap.setMapCenter(user);
            } else

                break;

        }
    }

    /**
     * Called when there is an update from the compass
     * (non-Javadoc)
     * @see com.TwentyCodes.android.location.CompassListener#onCompassUpdate(float)
     */
    @Override
    public void onCompassUpdate(float bearing) {
        mBearing = bearing;

        if (Debug.DEBUG)
            Log.v(TAG, "Bearing =" + bearing);

        /*
         * used calculated bearing if the last location was updated less than a x ago
         */
        if ((System.currentTimeMillis() - mLastReportedTime) < 500)
            mHeading = bearing;

        if (Debug.DEBUG)
            Log.v(TAG, "Heading =" + mHeading);

        bearing = GeoUtils.calculateBearing(mMap.getUserLocation(), mMap.getDestination(),
                bearing < 0 ? bearing + 360 : bearing);

        if (bearing > 355 || bearing < 5)
            mIOIOManager.setSteerValue(IOIOTruckValues.STEER_STRAIGHT);

        if (bearing < 355 && bearing > 180)
            mIOIOManager.setSteerValue(IOIOTruckValues.STEER_RIGHT);

        if (bearing < 180 && bearing > 5)
            mIOIOManager.setSteerValue(IOIOTruckValues.STEER_LEFT);
    }

    /**
     * (non-Javadoc)
     * @see android.support.v4.app.FragmentActivity#onCreate(android.os.Bundle)
     */
    @Override
    protected void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        setContentView(R.layout.nav_activity);

        /*
         * init UI
         */
        mLog = (TextView) findViewById(R.id.log_textView);
        mGoButton = (Button) findViewById(R.id.go_button);
        mProgress = (ProgressBar) findViewById(R.id.progressBar);
        mScrollView = (ScrollView) findViewById(R.id.scrollview);
        Switch scrollSwitch = (Switch) findViewById(R.id.scrolling_switch);
        mAccuracyTextView = (TextView) findViewById(R.id.accuracy_textview);
        mLastUpdateTextView = (TextView) findViewById(R.id.time_textview);

        /*
         * init listeners
         */
        scrollSwitch.setOnCheckedChangeListener(this);
        mGoButton.setOnClickListener(this);
        findViewById(R.id.mark_my_lcoation_button).setOnClickListener(this);
        findViewById(R.id.my_location_button).setOnClickListener(this);
        findViewById(R.id.map_button).setOnClickListener(this);

        mIOIOManager = new IOIOTruckConnectionManager(this, this);
        mIOIOManager.getIOIOAndroidApplicationHelper().create();
    }

    /**
     * (non-Javadoc)
     * @see android.support.v4.app.FragmentActivity#onDestroy()
     */
    @Override
    protected void onDestroy() {
        mIOIOManager.getIOIOAndroidApplicationHelper().destroy();
        super.onDestroy();
    }

    /**
     * called when the directions overlay is generated
     * (non-Javadoc)
     * @see com.TwentyCodes.android.overlays.DirectionsOverlay.OnDirectionsCompleteListener#onDirectionsComplete(com.TwentyCodes.android.overlays.DirectionsOverlay)
     */
    @Override
    public void onDirectionsComplete(DirectionsOverlay directionsOverlay) {
        ArrayList<PathOverlay> path = directionsOverlay.getPath();

        if (path.size() > 0) {
            mWayPoints = new ArrayList<PathOverlay>();
            ArrayList<GeoPoint> points = new ArrayList<GeoPoint>();
            points.add(path.get(0).getStartPoint());
            for (PathOverlay item : path)
                if (item.getEndPoint() != null) {
                    points.add(item.getEndPoint());
                    mWayPoints.add(new PathOverlay(item.getEndPoint(), 5, Color.GRAY));
                }

            mPoints = points;
            mMap.setDestination(points.get(0));
            mWayPoints.add(new PathOverlay(points.get(0), 5, Color.MAGENTA));
            mMap.getMap().getOverlays().addAll(mWayPoints);
            mWayPoints.addAll(path);
        }
    }

    @Override
    public void onFirstFix(boolean isFirstFix) {
        mMap.disableGPSProgess();
    }

    /**
     * Called when android's location services have an update
     * (non-Javadoc)
     * @see com.TwentyCodes.android.location.GeoPointLocationListener#onLocationChanged(com.google.android.maps.GeoPoint, int)
     */
    @Override
    public synchronized void onLocationChanged(final GeoPoint point, final int accuracy) {
        mLast = System.currentTimeMillis();
        mAccuracyTextView.setText(accuracy + getString(R.string.m));
        mDistance = updateProgress(point);
        final GeoPoint currentDest = mMap.getDestination();

        /*
         * if we have a destination, check to see if we are there yet
         * if we are then increment mCount
         */
        if (point != null)

            if (mLastReportedLocation != null) {
                if (!point.equals(mLastReportedLocation)) {
                    mLastReportedTime = mLast;
                    float heading = new Float(GeoUtils.bearing(mLastReportedLocation, point));
                    mHeading = heading > 180 ? heading - 360 : heading;
                    if (Debug.DEBUG)
                        updateLog("Heading = " + mHeading);
                }
            }
        mLastReportedLocation = point;
        if (currentDest != null)

            /*
             * are we closer than 30 feet?
             */
            //            if (GeoUtils.distanceKm(point, currentDest) < Debug.FUDGE_FACTOR) {
            if (GeoUtils.isIntersecting(point, (float) (accuracy / 1E3), currentDest, Debug.RADIUS,
                    Debug.FUDGE_FACTOR)) {
                updateLog("Count = " + (++mCount));
                /*
                 * if we get 6 positives, we are problay at our waypoint/dest
                 */
                if (mCount == 6) {

                    mCount = 0;

                    /*
                     * if the points list is null, or there are no more waypoints
                     */
                    if (mPoints == null || mIndex == mPoints.size()) {
                        mIOIOManager.setDriveValue(IOIOTruckValues.DRIVE_STOP);
                        updateGoButton(true);
                        updateLog(R.string.dest_reached);
                        mMap.setDestination(null);
                    } else {
                        updateLog("Index = " + (++mIndex));

                        /*
                         * if there are more waypoints, then move on to the next
                         * otherwise move on to the dest
                         */
                        if (mIndex < mPoints.size()) {
                            updateLog("Waypoint reached, moving to next");
                            mMap.setDestination(mPoints.get(mIndex));
                        } else {
                            updateLog("last Waypoint reached, moving to dest");
                            mMap.setDestination(mDestPoint);
                        }

                        updateLog("New dest = " + mMap.getDestination().toString());
                    }
                }

            } else {
                Log.v(TAG, "Driving Forward");
                mCount = 0;
                mIOIOManager.setDriveValue(IOIOTruckValues.DRIVE_FORWARD);
            }
        else {
            updateLog("Lost GPS signal (point was null), stopping");
            mIOIOManager.setDriveValue(IOIOTruckValues.DRIVE_STOP);
        }

    }

    /**
     * Called when the user selects a point for the truck to drive to
     * (non-Javadoc)
     * @see com.TwentyCodes.android.location.LocationSelectedListener#onLocationSelected(com.google.android.maps.GeoPoint)
     */
    @Override
    public void onLocationSelected(GeoPoint point) {
        if (mWayPoints != null)
            mMap.getMap().getOverlays().removeAll(mWayPoints);
        mDestPoint = point;
        mDistance = updateProgress(mMap.getUserLocation());
        updateLog(getString(R.string.point_selected) + point.toString());
        mIndex = 0;
        mCount = 0;
    }

    /**
     * Called when the IOIOTruckThread has a log it wants to display
     * (non-Javadoc)
     * @see com.TwentyCodes.android.IOIOTruck.IOIOTruckConnectionManager.IOIOTruckThreadListener#onLogUpdate(java.lang.String)
     */
    @Override
    public void onLogUpdate(String log) {
        updateLog(log);
    }

    /**
     * Called when the application is paused. We want to disconnect with the
     * IOIO at this point, as the user is no longer interacting with our
     * application.
     * @author ricky barrette
     */
    @Override
    protected void onPause() {
        if (mLoggerThread != null)
            mLoggerThread.abort();

        if (mWakeLock.isHeld())
            mWakeLock.release();
        super.onPause();
    }

    /**
     * (non-Javadoc)
     * @see android.app.Activity#onRestart()
     */
    @Override
    protected void onRestart() {
        mIOIOManager.getIOIOAndroidApplicationHelper().restart();
        super.onRestart();
    }

    /**
     * (non-Javadoc)
     * @see android.support.v4.app.FragmentActivity#onResume()
     */
    @Override
    protected void onResume() {
        super.onResume();
        mMap = (MapFragment) this.getSupportFragmentManager().findFragmentById(R.id.map_fragment);
        mMap.setCompassListener(this);
        mMap.setGeoPointLocationListener(this);
        mMap.setLocationSelectedListener(this);
        mMap.setDirectionsCompleteListener(this);
        mMap.setRadius((int) (Debug.RADIUS * 1E3));
        mMap.enableGPSProgess();

        PowerManager pm = (PowerManager) this.getSystemService(Context.POWER_SERVICE);
        mWakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, TAG);
        mWakeLock.acquire();
    }

    /**
     * (non-Javadoc)
     * @see android.support.v4.app.FragmentActivity#onStart()
     */
    @Override
    protected void onStart() {
        mIOIOManager.getIOIOAndroidApplicationHelper().start();
        super.onStart();
    }

    /**
     * (non-Javadoc)
     * @see android.support.v4.app.FragmentActivity#onStop()
     */
    @Override
    protected void onStop() {
        mIOIOManager.getIOIOAndroidApplicationHelper().stop();
        super.onStop();
    }

    /**
     * updates the go/stop button based on isRunning
     * thread safe 
     * @author ricky barrette
     */
    private void updateGoButton() {
        updateGoButton(isRunning);
    }

    /**
     * Sets the go/stop button to the provided value
     * thread safe
     * @param isRunnuing true = stop, false = go
     * @author ricky barrette
     */
    private void updateGoButton(final boolean isRun) {
        mIOIOManager.setStatLedEnabled(!isRun);
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                if (isRun) {
                    mCount = 0;
                    mGoButton.setText(R.string.go);
                    isRunning = false;
                    updateLog(R.string.stop);
                    if (mLoggerThread != null)
                        mLoggerThread.abort();
                } else {
                    mGoButton.setText(R.string.stop);
                    isRunning = true;
                    updateLog(R.string.go);
                    mLoggerThread = new LogUpdater();
                    mLoggerThread.start();
                }
            }
        });
    }

    /**
     * updates the log with the provided string res
     * thread safe
     * @param resId
     */
    private void updateLog(final int resId) {
        updateLog("\n" + getString(resId));
    }

    /**
     * updates the log with the provided string
     * thread safe 
     * @param id The string ID of the message to present.
     */
    private void updateLog(final String log) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {

                mLog.append("\n" + log);

                /*
                 * Scroll the scroll view down
                 */
                if (isScrollingEnabled)
                    mScrollView.scrollTo(0, mLog.getHeight());
            }
        });

    }

    /**
     * updates the progress bar
     * it will roughly show the progress of the truck
     * 
     * less = closer
     * more = farther 
     * @param point dest
     * @return distance meters
     * @author ricky barrette
     */
    private int updateProgress(GeoPoint point) {
        int distance = (int) (GeoUtils.distanceKm(point, mMap.getDestination()) * 1000);
        if (distance > mProgress.getMax())
            mProgress.setMax(distance);
        mProgress.setProgress(distance);
        return distance;
    }
}