com.wheelphone.remotemini.WheelphoneRemoteMini.java Source code

Java tutorial

Introduction

Here is the source code for com.wheelphone.remotemini.WheelphoneRemoteMini.java

Source

/*
 * Copyright (C) 2011 GUIGUI Simon, fyhertz@gmail.com
 * 
 * This file is part of Spydroid (http://code.google.com/p/spydroid-ipcamera/)
 * 
 * Spydroid is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This source code is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this source code; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package com.wheelphone.remotemini;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Timer;
import java.util.TimerTask;

import org.apache.http.HttpStatus;

import com.qualcomm.QCAR.QCAR;
import com.wheelphone.remotemini.WheelphoneRemoteMini;
import com.wheelphone.wheelphonelibrary.WheelphoneRobot;
import com.wheelphone.wheelphonelibrary.WheelphoneRobot.WheelPhoneRobotListener;

import net.majorkernelpanic.http.CustomHttpServer;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
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.OnSharedPreferenceChangeListener;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.graphics.Point;
import android.media.AudioManager;
import android.media.SoundPool;
import android.media.SoundPool.OnLoadCompleteListener;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.PowerManager;
import android.preference.PreferenceManager;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.WindowManager;
import android.view.View.OnClickListener;
import android.view.ViewGroup.LayoutParams;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

/** 
 * Spydroid launches an RtspServer, clients can then connect to it and receive audio/video streams from the phone
 */
public class WheelphoneRemoteMini extends Activity
        implements OnSharedPreferenceChangeListener, WheelPhoneRobotListener {

    // Various
    private static String TAG = WheelphoneRemoteMini.class.getName();
    Timer timerImg = new Timer();
    boolean getFirmwareFlag = true;
    private int firmwareVersion = 0;
    private SoundPool soundPool = new SoundPool(4, AudioManager.STREAM_MUSIC, 0);
    private Field[] raws = R.raw.class.getFields();
    public static final int IMG_NORMAL = 0;
    public static final int IMG_DRIVE = 1;
    public static final int IMG_BORED = 2;
    public static final int IMG_BORED_1 = 3;
    public static final int IMG_BORED_2 = 4;
    public static final int IMG_BORED_3 = 5;
    public static final int IMG_SLEEP = 6;
    public static final int IMG_ANGRY = 7;
    public int imgState = IMG_NORMAL;
    public Intent intent;
    boolean frontImageActivityStarted = false;
    private String logString;
    private boolean debugUsbComm = false;

    // UI
    Button btnStart;

    // Robot state
    WheelphoneRobot wheelphone;

    // target detection
    public int targetX = 0, targetY = 0;
    public int targetTimeout = 0;
    public int isFollowingTarget = 0;

    //motion
    private int lSpeed = 0, rSpeed = 0;
    public static final int MIN_SPEED = -350;
    public static final int MAX_SPEED = 350;
    public static final int SPEED_STEP = 20; //mm/s
    public static final int ROTATION_STEP = 10;
    public static final int MAX_SPEED_INDEX = MAX_SPEED / SPEED_STEP;
    public int currentSpeed = 0;
    public int currentSpeedIndex = 0;
    public int currentRotation = 0;
    public int currentRotationIndex = 0;
    public static final int NO_ROTATION = 0;
    public static final int ROTATE_LEFT = 1;
    public static final int ROTATE_RIGHT = 2;
    public int pivotType = NO_ROTATION;
    public int pivotCounter = 0;

    // http server
    private CustomHttpServer httpServer = null;
    private PowerManager.WakeLock wl;
    private SurfaceHolder holder;
    private SurfaceView camera;
    private TextView line1, line2, version, signWifi, signStreaming;
    private LinearLayout signInformation;
    private Context context;
    private Animation pulseAnimation;

    // AR
    // Menu item string constants:
    private static final String MENU_ITEM_ACTIVATE_CONT_AUTO_FOCUS = "Activate Cont. Auto focus";
    private static final String MENU_ITEM_DEACTIVATE_CONT_AUTO_FOCUS = "Deactivate Cont. Auto focus";
    // Focus mode constants:
    private static final int FOCUS_MODE_NORMAL = 0;
    private static final int FOCUS_MODE_CONTINUOUS_AUTO = 1;
    // Application status constants:
    private static final int APPSTATUS_UNINITED = -1;
    private static final int APPSTATUS_INIT_APP = 0;
    private static final int APPSTATUS_INIT_QCAR = 1;
    private static final int APPSTATUS_INIT_APP_AR = 2;
    private static final int APPSTATUS_INIT_TRACKER = 3;
    private static final int APPSTATUS_INITED = 4;
    private static final int APPSTATUS_CAMERA_STOPPED = 5;
    private static final int APPSTATUS_CAMERA_RUNNING = 6;
    // Name of the native dynamic libraries to load:
    private static final String NATIVE_LIB_SAMPLE = "WheelphoneRemoteMini";
    private static final String NATIVE_LIB_QCAR = "QCAR";
    // Display size of the device:
    private int mScreenWidth = 0;
    private int mScreenHeight = 0;
    // The current application status:
    private int mAppStatus = APPSTATUS_UNINITED;
    // The async task to initialize the QCAR SDK:
    private InitQCARTask mInitQCARTask;
    // An object used for synchronizing QCAR initialization, dataset loading and
    // the Android onDestroy() life cycle event. If the application is destroyed
    // while a data set is still being loaded, then we wait for the loading:
    // operation to finish before shutting down QCAR.
    private Object mShutdownLock = new Object();
    // QCAR initialization flags:
    private int mQCARFlags = 0;
    // The current focus mode selected:
    private int mFocusMode;
    /** Static initializer block to load native libraries on start-up. */
    static {
        loadLibrary(NATIVE_LIB_QCAR);
        loadLibrary(NATIVE_LIB_SAMPLE);
    }

    /** An async task to initialize QCAR asynchronously. */
    private class InitQCARTask extends AsyncTask<Void, Integer, Boolean> {
        // Initialize with invalid value
        private int mProgressValue = -1;

        protected Boolean doInBackground(Void... params) {
            // Prevent the onDestroy() method to overlap with initialization:
            synchronized (mShutdownLock) {
                QCAR.setInitParameters(WheelphoneRemoteMini.this, mQCARFlags);

                do {
                    // QCAR.init() blocks until an initialization step is
                    // complete, then it proceeds to the next step and reports
                    // progress in percents (0 ... 100%)
                    // If QCAR.init() returns -1, it indicates an error.
                    // Initialization is done when progress has reached 100%.
                    mProgressValue = QCAR.init();

                    // Publish the progress value:
                    publishProgress(mProgressValue);

                    // We check whether the task has been canceled in the
                    // meantime (by calling AsyncTask.cancel(true))
                    // and bail out if it has, thus stopping this thread.
                    // This is necessary as the AsyncTask will run to completion
                    // regardless of the status of the component that
                    // started is.
                } while (!isCancelled() && mProgressValue >= 0 && mProgressValue < 100);

                return (mProgressValue > 0);
            }
        }

        protected void onProgressUpdate(Integer... values) {
            // Do something with the progress value "values[0]", e.g. update
            // splash screen, progress bar, etc.
        }

        protected void onPostExecute(Boolean result) {
            // Done initializing QCAR, proceed to next application
            // initialization status:
            if (result) {
                Log.d(TAG, "InitQCARTask::onPostExecute: QCAR " + "initialization successful");

                updateApplicationStatus(APPSTATUS_INIT_TRACKER);
            } else {
                // Create dialog box for display error:
                AlertDialog dialogError = new AlertDialog.Builder(WheelphoneRemoteMini.this).create();

                dialogError.setButton(DialogInterface.BUTTON_POSITIVE, "Close",
                        new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int which) {
                                // Exiting application:
                                System.exit(1);
                            }
                        });

                String logMessage;

                // NOTE: Check if initialization failed because the device is
                // not supported. At this point the user should be informed
                // with a message.
                if (mProgressValue == QCAR.INIT_DEVICE_NOT_SUPPORTED) {
                    logMessage = "Failed to initialize QCAR because this " + "device is not supported.";
                } else {
                    logMessage = "Failed to initialize QCAR.";
                }

                // Log error:
                Log.e(TAG, "InitQCARTask::onPostExecute: " + logMessage + " Exiting.");

                // Show dialog box with error message:
                dialogError.setMessage(logMessage);
                dialogError.show();
            }
        }
    }

    //tells activity to run on ui thread    
    class frontImageStateTask extends TimerTask {
        @Override
        public void run() {
            WheelphoneRemoteMini.this.runOnUiThread(new Runnable() {
                //@Override                 
                public void run() {
                    switch (imgState) {
                    case IMG_DRIVE:
                        FrontImageActivity.setFrontImage("smiley_bored2");
                        imgState = IMG_BORED;
                        timerImg.schedule(new frontImageStateTask(), 15000); // after 15 seconds
                        break;

                    case IMG_BORED:
                        FrontImageActivity.setFrontImage("smiley_annoyed");
                        imgState = IMG_BORED_1;
                        timerImg.schedule(new frontImageStateTask(), 15000); // after 15 seconds                     
                        break;

                    case IMG_BORED_1:
                        FrontImageActivity.setFrontImage("smiley_annoyed3");
                        imgState = IMG_BORED_2;
                        timerImg.schedule(new frontImageStateTask(), 15000); // after 15 seconds                     
                        break;

                    case IMG_BORED_2:
                        FrontImageActivity.setFrontImage("smiley_bored");
                        imgState = IMG_BORED_3;
                        timerImg.schedule(new frontImageStateTask(), 15000); // after 15 seconds                     
                        break;

                    case IMG_BORED_3:
                        FrontImageActivity.setFrontImage("smiley_yawn1");
                        imgState = IMG_SLEEP;
                        timerImg.schedule(new frontImageStateTask(), 15000); // after 15 seconds                     
                        break;

                    case IMG_SLEEP:
                        FrontImageActivity.setFrontImage("smiley_asleep");
                        break;

                    case IMG_ANGRY:
                        FrontImageActivity.setFrontImage("smiley01_drive_car");
                        imgState = IMG_DRIVE;
                        timerImg.schedule(new frontImageStateTask(), 15000); // after 15 seconds                                          
                        break;

                    }
                }
            });
        }
    };

    /** A helper for loading native libraries stored in "libs/armeabi*". */
    public static boolean loadLibrary(String nLibName) {
        try {
            System.loadLibrary(nLibName);
            Log.i(TAG, "Native library lib" + nLibName + ".so loaded");
            return true;
        } catch (UnsatisfiedLinkError ulee) {
            Log.e(TAG, "The library lib" + nLibName + ".so could not be loaded");
        } catch (SecurityException se) {
            Log.e(TAG, "The library lib" + nLibName + ".so was not allowed to be loaded");
        }

        return false;
    }

    private void storeScreenDimensions() {
        // Query display dimensions:
        DisplayMetrics metrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(metrics);
        mScreenWidth = metrics.widthPixels;
        mScreenHeight = metrics.heightPixels;
    }

    /** Configure QCAR with the desired version of OpenGL ES. */
    private int getInitializationFlags() {
        return QCAR.GL_20;
    }

    /** Native tracker initialization and deinitialization. */
    public native int initTracker();

    public native void deinitTracker();

    /** Native methods for starting and stopping the camera. */
    private native void startCamera();

    private native void stopCamera();

    /** Native method for setting / updating the projection matrix for
     * AR content rendering */
    private native void setProjectionMatrix();

    public void onConfigurationChanged(Configuration config) {
        Log.d(TAG, "FrameMarkers::onConfigurationChanged");
        super.onConfigurationChanged(config);

        storeScreenDimensions();

        // Set projection matrix:
        if (QCAR.isInitialized() && (mAppStatus == APPSTATUS_CAMERA_RUNNING))
            setProjectionMatrix();
    }

    /** Native function to deinitialize the application.*/
    private native void deinitApplicationNative();

    /** Native function to update the renderer. */
    public native void updateRendering(int width, int height);

    /** NOTE: this method is synchronized because of a potential concurrent
     * access by FrameMarkers::onResume() and InitQCARTask::onPostExecute(). */
    private synchronized void updateApplicationStatus(int appStatus) {
        // Exit if there is no change in status:
        if (mAppStatus == appStatus)
            return;

        // Store new status value
        mAppStatus = appStatus;

        // Execute application state-specific actions:
        switch (mAppStatus) {
        case APPSTATUS_INIT_APP:
            // Initialize application elements that do not rely on QCAR
            // initialization:
            initApplication();

            // Proceed to next application initialization status:
            updateApplicationStatus(APPSTATUS_INIT_QCAR);
            break;

        case APPSTATUS_INIT_QCAR:
            // Initialize QCAR SDK asynchronously to avoid blocking the
            // main (UI) thread.
            // This task instance must be created and invoked on the UI
            // thread and it can be executed only once!
            try {
                mInitQCARTask = new InitQCARTask();
                mInitQCARTask.execute();
            } catch (Exception e) {
                Log.e(TAG, "Initializing QCAR SDK failed");
            }
            break;

        case APPSTATUS_INIT_TRACKER:

            // Initialize the marker tracker and create markers:
            if (initTracker() >= 0) {
                Log.d(TAG, "Tracker initialized!\n");
                // Proceed to next application initialization status:
                updateApplicationStatus(APPSTATUS_INIT_APP_AR);
            }
            break;

        case APPSTATUS_INIT_APP_AR:
            // Initialize Augmented Reality-specific application elements
            // that may rely on the fact that the QCAR SDK has been
            // already initialized:
            initApplicationAR();

            // Proceed to next application initialization status:
            updateApplicationStatus(APPSTATUS_INITED);
            break;

        case APPSTATUS_INITED:
            // Hint to the virtual machine that it would be a good time to
            // run the garbage collector:
            //
            // NOTE: This is only a hint. There is no guarantee that the
            // garbage collector will actually be run.
            System.gc();

            //                // The elapsed time since the splash screen was visible:
            //                long splashScreenTime = System.currentTimeMillis() -
            //                                            mSplashScreenStartTime;
            //                long newSplashScreenTime = 0;
            //                if (splashScreenTime < MIN_SPLASH_SCREEN_TIME)
            //                {
            //                    newSplashScreenTime = MIN_SPLASH_SCREEN_TIME -
            //                                            splashScreenTime;
            //                }
            //
            //                // Request a callback function after a given timeout to dismiss
            //                // the splash screen:
            //                mSplashScreenHandler = new Handler();
            //                mSplashScreenRunnable =
            //                    new Runnable() {
            //                        public void run()
            //                        {
            //                            // Hide the splash screen:
            //                            mSplashScreenView.setVisibility(View.INVISIBLE);
            //
            //                            // Activate the renderer:
            //                            mRenderer.mIsActive = true;
            //
            //                            // Now add the GL surface view. It is important
            //                            // that the OpenGL ES surface view gets added
            //                            // BEFORE the camera is started and video
            //                            // background is configured.
            //                            addContentView(mGlView, new LayoutParams(
            //                                            LayoutParams.MATCH_PARENT,
            //                                            LayoutParams.MATCH_PARENT));
            //
            //                            // Start the camera:
            updateApplicationStatus(APPSTATUS_CAMERA_RUNNING);
            //                        }
            //                };
            //
            //                mSplashScreenHandler.postDelayed(mSplashScreenRunnable,
            //                                                    newSplashScreenTime);
            break;

        case APPSTATUS_CAMERA_STOPPED:
            // Call the native function to stop the camera:
            stopCamera();
            break;

        case APPSTATUS_CAMERA_RUNNING:
            // Call the native function to start the camera:
            startCamera();
            setProjectionMatrix();

            // Set continuous auto-focus if supported by the device,
            // otherwise default back to regular auto-focus mode.
            // This will be activated by a tap to the screen in this
            // application.
            mFocusMode = FOCUS_MODE_CONTINUOUS_AUTO;
            if (!setFocusMode(mFocusMode)) {
                mFocusMode = FOCUS_MODE_NORMAL;
                setFocusMode(mFocusMode);
            }

            break;

        default:
            throw new RuntimeException("Invalid application state");
        }
    }

    /** Tells native code whether we are in portait or landscape mode */
    private native void setActivityPortraitMode(boolean isPortrait);

    /** Initialize application GUI elements that are not related to AR. */
    private void initApplication() {
        // Set the screen orientation:
        int screenOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;

        // Apply screen orientation:
        setRequestedOrientation(screenOrientation);

        // Pass on screen orientation info to native code:
        setActivityPortraitMode(screenOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

        storeScreenDimensions();

        // As long as this window is visible to the user, keep the device's
        // screen turned on and bright.
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
                WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

    }

    /** Native function to initialize the application. */
    private native void initApplicationNative(int width, int height);

    /** Initializes AR application components. */
    private void initApplicationAR() {
        // Do application initialization in native code (e.g. registering
        // callbacks, etc.)
        initApplicationNative(mScreenWidth, mScreenHeight);

    }

    /** Invoked every time before the options menu gets displayed to give
     *  the Activity a chance to populate its Menu with menu items. */
    public boolean onPrepareOptionsMenu(Menu menu) {
        super.onPrepareOptionsMenu(menu);

        //        menu.clear();
        //
        //        if(mFocusMode == FOCUS_MODE_CONTINUOUS_AUTO)
        //            menu.add(MENU_ITEM_DEACTIVATE_CONT_AUTO_FOCUS);
        //        else
        //            menu.add(MENU_ITEM_ACTIVATE_CONT_AUTO_FOCUS);

        return true;
    }

    private native boolean autofocus();

    private native boolean setFocusMode(int mode);

    /** The native render function. */
    public native void getTrackInfo();

    public void displayMessageInt2(int i1, int i2) {
        // We use a handler because this thread cannot change the UI
        Message message = new Message();
        message.obj = String.valueOf(i1) + ", " + String.valueOf(i2);
        Log.d(TAG, "x=" + i1 + ", y=" + i2 + "\n");
        targetX = i1;
        targetY = i2;
        FrontImageActivity
                .setTextTarget("Target detected at (" + String.valueOf(i1) + ", " + String.valueOf(i2) + ")");
        //mainActivityHandler.sendMessage(message);
        targetTimeout = 0;
        isFollowingTarget = 1;
    }

    public void onCreate(Bundle savedInstanceState) {
        if (debugUsbComm) {
            logString = TAG + ": onCreate";
            Log.d(TAG, logString);
            appendLog("debugUsbComm.txt", logString, false);
        }

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

        camera = (SurfaceView) findViewById(R.id.smallcameraview);
        context = this.getApplicationContext();
        line1 = (TextView) findViewById(R.id.line1);
        line2 = (TextView) findViewById(R.id.line2);
        version = (TextView) findViewById(R.id.version);
        signWifi = (TextView) findViewById(R.id.advice);
        signStreaming = (TextView) findViewById(R.id.streaming);
        signInformation = (LinearLayout) findViewById(R.id.information);
        pulseAnimation = AnimationUtils.loadAnimation(this, R.anim.pulse);

        SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);

        settings.registerOnSharedPreferenceChangeListener(this);

        camera.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        holder = camera.getHolder();

        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
        wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "com.wheelphone.remotemini.wakelock");

        httpServer = new CustomHttpServer(8080, this.getApplicationContext(), handler);

        soundPool.setOnLoadCompleteListener(new OnLoadCompleteListener() {
            public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
                soundPool.play(sampleId, 0.99f, 0.99f, 1, 0, 1);
            }
        });

        timerImg = new Timer();

        intent = new Intent(context, FrontImageActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        //startActivity(intent);        

        // Query the QCAR initialization flags:
        mQCARFlags = getInitializationFlags();

        // Update the application status to start initializing application
        updateApplicationStatus(APPSTATUS_INIT_APP);

        Display display = getWindowManager().getDefaultDisplay();
        Point size = new Point();
        display.getSize(size);
        updateRendering(size.x, size.y);

        btnStart = (Button) findViewById(R.id.btnStart);

        //Make sure that the app stays open:
        getWindow().addFlags(
                WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
                        | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
                        | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);

        wheelphone = new WheelphoneRobot(getApplicationContext(), getIntent());
        wheelphone.enableSpeedControl();
        wheelphone.setWheelPhoneRobotListener(this);

    }

    public void onStart() {
        if (debugUsbComm) {
            logString = TAG + ": onStart";
            Log.d(TAG, logString);
            appendLog("debugUsbComm.txt", logString, false);
        }
        super.onStart();

        // Lock screen
        wl.acquire();

        Intent notificationIntent = new Intent(this, WheelphoneRemoteMini.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent,
                PendingIntent.FLAG_CANCEL_CURRENT);

        Notification.Builder builder = new Notification.Builder(this);
        builder.setContentIntent(pendingIntent).setWhen(System.currentTimeMillis())
                .setTicker(getText(R.string.notification_title))
                .setSmallIcon(R.drawable.wheelphone_logo_remote_mini_small)
                .setContentTitle(getText(R.string.notification_title))
                .setContentText(getText(R.string.notification_content));
        Notification notification = builder.getNotification();
        notification.flags |= Notification.FLAG_ONGOING_EVENT;
        ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).notify(0, notification);

    }

    public void onStop() {
        if (debugUsbComm) {
            logString = TAG + ": onStop";
            Log.d(TAG, logString);
            appendLog("debugUsbComm.txt", logString, false);
        }
        super.onStop();
        wl.release();
    }

    public void onResume() {
        if (debugUsbComm) {
            logString = TAG + ": onResume";
            Log.d(TAG, logString);
            appendLog("debugUsbComm.txt", logString, false);
        }
        super.onResume();

        // when FrontImageActivity is started and the back button is pressed then this "onResume"
        // is called again and we don't want to restart twice the communication. Basically only at the 
        // first call to "onStart" we start the USB communication.       
        if (!frontImageActivityStarted) {
            wheelphone.startUSBCommunication();
        }

        // Determines if user is connected to a wireless network & displays ip 
        if (!streaming)
            displayIpAddress();
        startServers();
        registerReceiver(wifiStateReceiver, new IntentFilter(WifiManager.NETWORK_STATE_CHANGED_ACTION));

        // QCAR-specific resume operation:
        QCAR.onResume();

        // We may start the camera only if the QCAR SDK has already been
        // initialized:
        if (mAppStatus == APPSTATUS_CAMERA_STOPPED) {
            updateApplicationStatus(APPSTATUS_CAMERA_RUNNING);
        }

    }

    public void onPause() {
        if (debugUsbComm) {
            logString = TAG + ": onPause";
            Log.d(TAG, logString);
            appendLog("debugUsbComm.txt", logString, false);
        }
        super.onPause();

        // do not close the communication here because otherwise when "FrontImageActivity" is 
        // started then the communication doens't work anymore!
        //wheelphone.closeUSBCommunication();  

        CustomHttpServer.setScreenState(false);
        unregisterReceiver(wifiStateReceiver);

        //        if (mAppStatus == APPSTATUS_CAMERA_RUNNING)
        //        {
        //            updateApplicationStatus(APPSTATUS_CAMERA_STOPPED);
        //        }
        //
        //        // QCAR-specific pause operation:
        //        QCAR.onPause();

    }

    public void onDestroy() {
        super.onDestroy();
        // Remove notification
        ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).cancel(0);
        if (httpServer != null)
            httpServer.stop();

        // Cancel potentially running tasks:
        if (mInitQCARTask != null && mInitQCARTask.getStatus() != InitQCARTask.Status.FINISHED) {
            mInitQCARTask.cancel(true);
            mInitQCARTask = null;
        }

        // Ensure that the asynchronous operations to initialize QCAR does
        // not overlap:
        synchronized (mShutdownLock) {

            // Do application deinitialization in native code:
            deinitApplicationNative();

            // Deinit the tracker:
            deinitTracker();

            // Deinitialize QCAR SDK:
            QCAR.deinit();
        }

        System.gc();

    }

    public void onBackPressed() {
        Intent setIntent = new Intent(Intent.ACTION_MAIN);
        setIntent.addCategory(Intent.CATEGORY_HOME);
        setIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(setIntent);
    }

    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.menu, menu);
        return true;
    }

    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.quit:
            // Quits Spydroid i.e. stops the HTTP server
            if (httpServer != null)
                httpServer.stop();
            wheelphone.closeUSBCommunication();
            finish();
            return true;
        default:
            return super.onOptionsItemSelected(item);
        }
    }

    private void startServers() {
        if (httpServer != null) {
            CustomHttpServer.setScreenState(true);
            try {
                httpServer.start();
            } catch (IOException e) {
                log("HttpServer could not be started : "
                        + (e.getMessage() != null ? e.getMessage() : "Unknown error"));
            }
        }
    }

    // BroadcastReceiver that detects wifi state changements
    private final BroadcastReceiver wifiStateReceiver = new BroadcastReceiver() {
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            // This intent is also received when app resumes even if wifi state hasn't changed :/
            if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
                if (!streaming)
                    displayIpAddress();
            }
        }
    };

    private boolean streaming = false;

    // The Handler that gets information back from the RtspServer and Session
    private final Handler handler = new Handler() {

        public void handleMessage(Message msg) {

            switch (msg.what) {
            case CustomHttpServer.MOVE_FORWARD:
                if (frontImageActivityStarted) {
                    FrontImageActivity.setFrontImage("smiley01_drive_car");
                    imgState = IMG_DRIVE;
                    timerImg.cancel();
                    timerImg = new Timer();
                    timerImg.schedule(new frontImageStateTask(), 15000);
                }
                try {
                    if (currentSpeedIndex == 0) {
                        soundPool.load(context, raws[2].getInt(null), 0);
                    }
                } catch (Exception e) {
                    Log.e(TAG, "Error !");
                    e.printStackTrace();
                }
                if (currentRotationIndex == 0) {
                    if (currentSpeedIndex < MAX_SPEED_INDEX) {
                        currentSpeedIndex++;
                    } else {
                        currentSpeedIndex = MAX_SPEED_INDEX;
                    }
                } else {
                    currentRotationIndex = 0;
                }
                pivotCounter = 0;
                pivotType = NO_ROTATION;
                break;
            case CustomHttpServer.MOVE_BACKWARD:
                if (frontImageActivityStarted) {
                    FrontImageActivity.setFrontImage("smiley01_drive_car");
                    imgState = IMG_DRIVE;
                    timerImg.cancel();
                    timerImg = new Timer();
                    timerImg.schedule(new frontImageStateTask(), 15000);
                }
                try {
                    if (currentSpeedIndex == 0) {
                        soundPool.load(context, raws[5].getInt(null), 0);
                    }
                } catch (Exception e) {
                    Log.e(TAG, "Error !");
                    e.printStackTrace();
                }
                if (currentRotationIndex == 0) {
                    if (currentSpeedIndex > -MAX_SPEED_INDEX) {
                        currentSpeedIndex--;
                    } else {
                        currentSpeedIndex = -MAX_SPEED_INDEX;
                    }
                } else {
                    currentRotationIndex = 0;
                }
                pivotCounter = 0;
                pivotType = NO_ROTATION;
                break;
            case CustomHttpServer.MOVE_LEFT:
                if (frontImageActivityStarted) {
                    FrontImageActivity.setFrontImage("smiley01_drive_car");
                    imgState = IMG_DRIVE;
                    timerImg.cancel();
                    timerImg = new Timer();
                    timerImg.schedule(new frontImageStateTask(), 15000);
                }
                try {
                    if (currentSpeedIndex >= 3 && Math.abs(currentRotationIndex) >= 3) {
                        soundPool.load(context, raws[4].getInt(null), 0);
                    }
                } catch (Exception e) {
                    Log.e(TAG, "Error !");
                    e.printStackTrace();
                }
                if (currentSpeedIndex == 0) {
                    pivotType = ROTATE_LEFT;
                    pivotCounter += 9; // (should be 7 theoretically) 45 degrees CCW with speed l=-50, r=15           
                } else {
                    if (Math.abs(currentSpeedIndex) < 3) {
                        if (currentRotationIndex < MAX_SPEED_INDEX) {
                            currentRotationIndex++;
                        } else {
                            currentRotationIndex = MAX_SPEED_INDEX;
                        }
                    } else {
                        if (currentSpeedIndex >= 0) {
                            if (currentRotationIndex < (MAX_SPEED_INDEX - (currentSpeedIndex / 3))) {
                                currentRotationIndex += currentSpeedIndex / 3;
                            } else {
                                currentRotationIndex = MAX_SPEED_INDEX;
                            }
                        } else {
                            if (currentRotationIndex < (MAX_SPEED_INDEX + (currentSpeedIndex / 3))) {
                                currentRotationIndex -= currentSpeedIndex / 3;
                            } else {
                                currentRotationIndex = MAX_SPEED_INDEX;
                            }
                        }
                    }
                }
                break;
            case CustomHttpServer.MOVE_RIGHT:
                if (frontImageActivityStarted) {
                    FrontImageActivity.setFrontImage("smiley01_drive_car");
                    imgState = IMG_DRIVE;
                    timerImg.cancel();
                    timerImg = new Timer();
                    timerImg.schedule(new frontImageStateTask(), 15000);
                }
                try {
                    if (currentSpeedIndex >= 3 && Math.abs(currentRotationIndex) >= 3) {
                        soundPool.load(context, raws[4].getInt(null), 0);
                    }
                } catch (Exception e) {
                    Log.e(TAG, "Error !");
                    e.printStackTrace();
                }
                if (currentSpeedIndex == 0) {
                    pivotType = ROTATE_RIGHT;
                    pivotCounter -= 9; // 45 degrees CW with speed l=15, r=-50                                              
                } else {
                    if (Math.abs(currentSpeedIndex) < 3) {
                        if (currentRotationIndex > -MAX_SPEED_INDEX) {
                            currentRotationIndex--;
                        } else {
                            currentRotationIndex = -MAX_SPEED_INDEX;
                        }
                    } else {
                        if (currentSpeedIndex >= 0) {
                            if (currentRotationIndex > (-MAX_SPEED_INDEX + (currentSpeedIndex / 3))) {
                                currentRotationIndex -= currentSpeedIndex / 3;
                            } else {
                                currentRotationIndex = -MAX_SPEED_INDEX;
                            }
                        } else {
                            if (currentRotationIndex > (-MAX_SPEED_INDEX - (currentSpeedIndex / 3))) {
                                currentRotationIndex += currentSpeedIndex / 3;
                            } else {
                                currentRotationIndex = -MAX_SPEED_INDEX;
                            }
                        }
                    }
                }
                break;
            case CustomHttpServer.STOP:
                if (frontImageActivityStarted) {
                    FrontImageActivity.setFrontImage("smiley01_drive_car");
                    imgState = IMG_DRIVE;
                    timerImg.cancel();
                    timerImg = new Timer();
                    timerImg.schedule(new frontImageStateTask(), 15000);
                }
                try {
                    if (currentSpeedIndex >= 0) {
                        soundPool.load(context, raws[1].getInt(null), 0);
                    }
                } catch (Exception e) {
                    Log.e(TAG, "Error !");
                    e.printStackTrace();
                }
                currentSpeedIndex = 0;
                currentRotationIndex = 0;
                pivotCounter = 0;
                pivotType = NO_ROTATION;
                break;
            case CustomHttpServer.ENABLE_OBSTACLE_AVOIDANCE:
                wheelphone.enableObstacleAvoidance();
                break;
            case CustomHttpServer.DISABLE_OBSTACLE_AVOIDANCE:
                wheelphone.disableObstacleAvoidance();
                break;
            case CustomHttpServer.ENABLE_CLIFF_AVOIDANCE:
                wheelphone.enableCliffAvoidance();
                break;
            case CustomHttpServer.DISABLE_CLIFF_AVOIDANCE:
                wheelphone.disableCliffAvoidance();
                break;

            default:
                break;

            }//switch

            if (currentSpeedIndex >= 0) { // forward direction
                rSpeed = currentSpeedIndex + currentRotationIndex;
                lSpeed = currentSpeedIndex - currentRotationIndex;
            } else { // backward direction
                rSpeed = currentSpeedIndex - currentRotationIndex;
                lSpeed = currentSpeedIndex + currentRotationIndex;
            }

            // handle speed measure in different ways based on positive velocity (speed controller) 
            // and negative velocity (no speed controller)
            //         if(rSpeed >= 0) {
            rSpeed *= SPEED_STEP;
            //         } else {            
            //            if(rSpeed == -1) {
            //               rSpeed = -30;
            //            } else if(rSpeed == -2) {
            //               rSpeed = -50;
            //            } else if(rSpeed == -3) {
            //               rSpeed = -65;
            //            } else if(rSpeed == -4) {
            //               rSpeed = -70;
            //            } else if(rSpeed == -5) {
            //               rSpeed = -80;
            //            } else if(rSpeed == -6) {
            //               rSpeed = -90;
            //            } else if(rSpeed == -7) {
            //               rSpeed = -100;
            //            } else if(rSpeed == -8) {
            //               rSpeed = -110;
            //            } else if(rSpeed == -9) {
            //               rSpeed = -120;
            //            } else {
            //               rSpeed = -MAX_SPEED;
            //            }
            //         }
            //         if(lSpeed >= 0) {
            lSpeed *= SPEED_STEP;
            //         } else {                        
            //            if(lSpeed == -1) {
            //               lSpeed = -30;
            //            } else if(lSpeed == -2) {
            //               lSpeed = -50;
            //            } else if(lSpeed == -3) {
            //               lSpeed = -65;
            //            } else if(lSpeed == -4) {
            //               lSpeed = -70;
            //            } else if(lSpeed == -5) {
            //               lSpeed = -80;
            //            } else if(lSpeed == -6) {
            //               lSpeed = -90;
            //            } else if(lSpeed == -7) {
            //               lSpeed = -100;
            //            } else if(lSpeed == -8) {
            //               lSpeed = -110;
            //            } else if(lSpeed == -9) {
            //               lSpeed = -120;
            //            } else {
            //               lSpeed = -MAX_SPEED;
            //            }
            //         }

            if (rSpeed > MAX_SPEED) {
                rSpeed = MAX_SPEED;
            }
            if (rSpeed < -MAX_SPEED) {
                rSpeed = -MAX_SPEED;
            }
            if (lSpeed > MAX_SPEED) {
                lSpeed = MAX_SPEED;
            }
            if (lSpeed < -MAX_SPEED) {
                lSpeed = -MAX_SPEED;
            }

            wheelphone.setLeftSpeed(lSpeed);
            wheelphone.setRightSpeed(rSpeed);

        } //handle_message

    };

    public void startAnimation(View view) {
        startActivity(intent);
        frontImageActivityStarted = true;
    }

    private void displayIpAddress() {
        WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
        WifiInfo info = wifiManager.getConnectionInfo();
        if (info != null && info.getNetworkId() > -1) {
            int i = info.getIpAddress();
            String ip = String.format("%d.%d.%d.%d", i & 0xff, i >> 8 & 0xff, i >> 16 & 0xff, i >> 24 & 0xff);
            line1.setText("HTTP://");
            line1.append(ip);
            line1.append(":8080");
            line2.setText("RTSP://");
            line2.append(ip);
            line2.append(":8086");
            streamingState(0);
        } else {
            line1.setText("HTTP://xxx.xxx.xxx.xxx:8080");
            line2.setText("RTSP://xxx.xxx.xxx.xxx:8086");
            streamingState(2);
        }
    }

    public void log(String s) {
        Toast.makeText(context, s, Toast.LENGTH_SHORT).show();
    }

    private void streamingState(int state) {
        // Not streaming
        if (state == 0) {
            signStreaming.clearAnimation();
            signWifi.clearAnimation();
            signStreaming.setVisibility(View.GONE);
            signInformation.setVisibility(View.VISIBLE);
            signWifi.setVisibility(View.GONE);
        } else if (state == 1) {
            // Streaming
            signWifi.clearAnimation();
            signStreaming.setVisibility(View.VISIBLE);
            signStreaming.startAnimation(pulseAnimation);
            signInformation.setVisibility(View.INVISIBLE);
            signWifi.setVisibility(View.GONE);
        } else if (state == 2) {
            // No wifi !
            signStreaming.clearAnimation();
            signStreaming.setVisibility(View.GONE);
            signInformation.setVisibility(View.INVISIBLE);
            signWifi.setVisibility(View.VISIBLE);
            signWifi.startAnimation(pulseAnimation);
        }
    }

    @Override
    public void onSharedPreferenceChanged(SharedPreferences arg0, String arg1) {
        // TODO Auto-generated method stub

    }

    public void onWheelphoneUpdate() {

        //Toast.makeText(WheelphoneRemoteMini.this, "target Task is running", Toast.LENGTH_SHORT).show();

        if (getFirmwareFlag) {
            firmwareVersion = wheelphone.getFirmwareVersion();
            if (firmwareVersion > 0) { // wait for the first USB transaction to be accomplished
                getFirmwareFlag = false;
                if (firmwareVersion >= 3) {
                    Toast.makeText(WheelphoneRemoteMini.this,
                            "Firmware version " + firmwareVersion + ".0, fully compatible.", Toast.LENGTH_SHORT)
                            .show();
                    //msgbox("Firmware version "+firmwareVersion+".0", "Firmware is fully compatible.");
                } else {
                    //Toast.makeText(WheelphoneActivity.this, "Firmware version "+firmwareVersion+".0, NOT fully compatible. Update robot firmware.", Toast.LENGTH_LONG).show();
                    msgbox("Firmware version " + firmwareVersion + ".0",
                            "Firmware is NOT fully compatible. Update robot firmware.");
                }
            }
        }

        getTrackInfo();
        targetTimeout++;
        if (targetTimeout >= 2) {
            if (frontImageActivityStarted) {
                FrontImageActivity.setTextTarget("");
            }
            isFollowingTarget = 0;
            if (targetTimeout == 2) { // stop rotating when target lost
                lSpeed = 0;
                rSpeed = 0;
                pivotType = NO_ROTATION;
            }
        }

        if (isFollowingTarget == 1) {
            if (frontImageActivityStarted) {
                FrontImageActivity.setFrontImage("smiley01_drive_car");
                imgState = IMG_DRIVE;
                timerImg.cancel();
                timerImg = new Timer();
                timerImg.schedule(new frontImageStateTask(), 15000);
            }
            if (targetX > (mScreenWidth / 2) + 20) {
                lSpeed = 10;
                rSpeed = -10;
            } else if (targetX < (mScreenWidth / 2) - 20) {
                lSpeed = -10;
                rSpeed = 10;
            } else {
                lSpeed = 0;
                rSpeed = 0;
            }
        }

        if (pivotType != NO_ROTATION) {

            if (pivotCounter == 0) {

                lSpeed = 0;
                rSpeed = 0;
                pivotType = NO_ROTATION;

            } else {

                if (pivotCounter > 0) {
                    pivotCounter--;
                    lSpeed = -25;
                    rSpeed = 25;
                } else {
                    pivotCounter++;
                    lSpeed = 25;
                    rSpeed = -25;
                }

            }

        }

        wheelphone.setLeftSpeed(lSpeed);
        wheelphone.setRightSpeed(rSpeed);

        try {
            if (wheelphone.getFrontProx(0) >= 180 || wheelphone.getFrontProx(1) >= 180
                    || wheelphone.getFrontProx(2) >= 180 || wheelphone.getFrontProx(3) >= 180) {
                soundPool.load(context, raws[0].getInt(null), 0);
                if (frontImageActivityStarted) {
                    FrontImageActivity.setFrontImage("smiley_angry3");
                    imgState = IMG_ANGRY;
                    timerImg.cancel();
                    timerImg = new Timer();
                    timerImg.schedule(new frontImageStateTask(), 5000);
                }
            }
        } catch (Exception e) {
            Log.e(TAG, "Error !");
            e.printStackTrace();
        }
        if (wheelphone.getGroundProx(0) < 50 && wheelphone.getGroundProx(1) < 50 && wheelphone.getGroundProx(2) < 50
                && wheelphone.getGroundProx(3) < 50) {
            if (frontImageActivityStarted) {
                FrontImageActivity.setFrontImage("smiley_afraid3");
            }
        } else {
            //         switch(imgState) {
            //            case IMG_NORMAL:
            //               FrontImageActivity.setFrontImage("smiley00_normal");
            //               break;
            //            case IMG_DRIVE:
            //               FrontImageActivity.setFrontImage("smiley01_drive_car");
            //               break;
            //            case IMG_BORED:
            //               FrontImageActivity.setFrontImage("smiley_bored2");
            //               break;
            //            case IMG_BORED_1:
            //               FrontImageActivity.setFrontImage("smiley_annoyed");
            //               break;
            //            case IMG_BORED_2:
            //               FrontImageActivity.setFrontImage("smiley_annoyed3");
            //               break;
            //            case IMG_BORED_3:
            //               FrontImageActivity.setFrontImage("smiley_bored");
            //               break;
            //            case IMG_SLEEP:
            //               FrontImageActivity.setFrontImage("smiley_sleep");
            //               break;
            //            case IMG_ANGRY:
            //               FrontImageActivity.setFrontImage("smiley_angry3");
            //               break;                              
            //         }                           
        }

    }

    public void msgbox(String title, String msg) {
        AlertDialog.Builder dlgAlert = new AlertDialog.Builder(this);
        dlgAlert.setTitle(title);
        dlgAlert.setMessage(msg);
        dlgAlert.setPositiveButton("OK", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int whichButton) {
                //finish(); 
            }
        });
        dlgAlert.setCancelable(true);
        dlgAlert.create().show();
    }

    void appendLog(String fileName, String text, boolean clearFile) {
        File logFile = new File("sdcard/" + fileName);
        if (!logFile.exists()) {
            try {
                logFile.createNewFile();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        } else {
            if (clearFile) {
                logFile.delete();
                try {
                    logFile.createNewFile();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
        try {
            //BufferedWriter for performance, true to set append to file flag
            BufferedWriter buf = new BufferedWriter(new FileWriter(logFile, true));
            buf.append(text);
            buf.newLine();
            buf.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}