com.cellbots.eyes.EyesActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.cellbots.eyes.EyesActivity.java

Source

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

package com.cellbots.eyes;

import com.cellbots.R;

import org.apache.commons.httpclient.HttpConnection;
import org.apache.commons.httpclient.HttpState;
import org.apache.commons.httpclient.NoHttpResponseException;
import org.apache.commons.httpclient.methods.PutMethod;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.InputStreamBody;
import org.apache.http.impl.client.DefaultHttpClient;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.graphics.Rect;
import android.graphics.YuvImage;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.PreviewCallback;
import android.os.Bundle;
import android.os.Environment;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.webkit.WebView;
import android.widget.FrameLayout;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.UnknownHostException;

/**
 * Remote eyes turns your Android device into a wireless web cam. With Froyo's
 * camera enhancements (ability to manage the preview memory buffer + access to
 * Skia for quickly converting from YUV to JPG), it is possible to capture and
 * upload preview frames fast enough for it to look like video. The behavior is
 * that a new frame will not be captured and processed until the previous frame
 * has been processed and uploaded. This means that if the connection slows
 * down, the video may have brief stutters, but it won't fall behind. See
 * put.php for a simple, working example of what is running server side. Once
 * you have put.php uploaded to your server, be sure to remember to update
 * "putUrl", "server", and "port" in the Remote Eyes code to match your server
 * instead of "myexampleserver.com". See remote_eyes.html for the client HTML
 * page that can be used to see what Remote Eyes is seeing.
 *
 * @author clchen@google.com (Charles L. Chen)
 */

public class EyesActivity extends Activity implements Callback {

    public static final String TAG = "EyesActivity";

    public static final String EYES_COMMAND = "android.intent.action.EYES_COMMAND";

    private SurfaceHolder mHolder;

    private String putUrl = "";

    private String server = "";

    private int port = 80;

    private FrameLayout mFrame;

    private WebView mWebView;

    private SurfaceView mPreview;

    private Camera mCamera;

    private boolean mTorchMode;

    private HttpConnection mConnection;

    private HttpState mHttpState;

    private Rect r;

    private int previewHeight = 0;

    private int previewWidth = 0;

    private int previewFormat = 0;

    private boolean isUploading = false;

    private byte[] mCallbackBuffer;

    private ByteArrayOutputStream out;

    private BroadcastReceiver mReceiver;

    private WakeLock mWakeLock;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        Log.e("remote eyes", "started");
        super.onCreate(savedInstanceState);

        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
        mWakeLock = pm.newWakeLock(
                PowerManager.FULL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE | PowerManager.ACQUIRE_CAUSES_WAKEUP,
                "Cellbot Eyes");
        mWakeLock.acquire();

        mTorchMode = false;

        out = new ByteArrayOutputStream();

        if ((getIntent() != null) && (getIntent().getData() != null)) {
            putUrl = getIntent().getData().toString();
            server = putUrl.replace("http://", "");
            server = server.substring(0, server.indexOf("/"));
            Bundle extras = getIntent().getExtras();
            if ((extras != null) && (extras.getBoolean("TORCH", false))) {
                mTorchMode = true;
            }
        } else {
            SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
            putUrl = prefs.getString("REMOTE_EYES_PUT_URL", "");
            Log.e("prefs", putUrl);
            if (putUrl.length() < 1) {
                Intent i = new Intent();
                i.setClass(this, PrefsActivity.class);
                startActivity(i);
                finish();
                return;
            } else {
                server = putUrl.replace("http://", "");
                server = server.substring(0, server.indexOf("/"));
            }
        }

        resetConnection();
        mHttpState = new HttpState();

        setContentView(R.layout.eyes_main);
        mPreview = (SurfaceView) findViewById(R.id.eyes_preview);
        mHolder = mPreview.getHolder();
        mHolder.addCallback(this);
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

        mPreview.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                setTorchMode(!mTorchMode);
            }
        });

        mReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                boolean useTorch = intent.getBooleanExtra("TORCH", false);
                boolean shouldTakePicture = intent.getBooleanExtra("PICTURE", false);
                setTorchMode(useTorch);
                setTakePicture(shouldTakePicture);
            }
        };

        this.registerReceiver(mReceiver, new IntentFilter(EyesActivity.EYES_COMMAND));

        mFrame = (FrameLayout) findViewById(R.id.eyes_frame);
        mWebView = new WebView(this);
        mWebView.getSettings().setJavaScriptEnabled(true);
        // Use this if you want to load content locally
        // mWebView.loadUrl("content://com.cellbot.localpersonas/default/index.html");
        mWebView.loadUrl("http://personabots.appspot.com/expressions/tuby");

        mFrame.addView(mWebView);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (mReceiver != null) {
            this.unregisterReceiver(mReceiver);
        }
        if (mWakeLock != null) {
            mWakeLock.release();
        }
    }

    private void resetConnection() {
        //Log.e("server", server);
        try {
            String ip = new URL(putUrl).getHost();
            int port = new URL(putUrl).getPort();
            mConnection = new HttpConnection(ip, port == -1 ? 80 : port);
            mConnection.open();
        } catch (MalformedURLException e1) {
            e1.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void surfaceCreated(SurfaceHolder holder) {
        mCamera = Camera.open();
        try {
            mCamera.setPreviewDisplay(holder);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        mCamera.stopPreview();
        mCamera.release();
        mCamera = null;

        try {
            PutMethod put = new PutMethod(putUrl);
            put.setRequestBody(new ByteArrayInputStream(new byte[0]));
            put.execute(mHttpState, mConnection);
        } catch (NoHttpResponseException e) {
            // Silently ignore this.
        } catch (IOException e) {
            e.printStackTrace();
            resetConnection();
        }
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        mHolder.setFixedSize(w, h);
        // Start the preview
        Parameters params = mCamera.getParameters();
        previewHeight = params.getPreviewSize().height;
        previewWidth = params.getPreviewSize().width;
        previewFormat = params.getPreviewFormat();

        // Crop the edges of the picture to reduce the image size
        r = new Rect(80, 20, previewWidth - 80, previewHeight - 20);

        mCallbackBuffer = new byte[460800];

        mCamera.setParameters(params);
        mCamera.setPreviewCallbackWithBuffer(new PreviewCallback() {
            public void onPreviewFrame(byte[] imageData, Camera arg1) {
                final byte[] data = imageData;
                if (!isUploading) {
                    if (needToTakePicture) {
                        takePicture(imageData);
                    } else {
                        isUploading = true;
                        new Thread(new Runnable() {
                            @Override
                            public void run() {
                                uploadImage(data);
                            }
                        }).start();
                        // appEngineUploadImage(imageData);
                    }
                }
            }
        });
        mCamera.addCallbackBuffer(mCallbackBuffer);
        mCamera.startPreview();
        setTorchMode(mTorchMode);
    }

    private void uploadImage(byte[] imageData) {
        try {
            YuvImage yuvImage = new YuvImage(imageData, previewFormat, previewWidth, previewHeight, null);
            yuvImage.compressToJpeg(r, 20, out); // Tweak the quality here - 20
            // seems pretty decent for quality + size.
            if (putUrl.contains("127.0.0.1") || putUrl.contains("localhost")) {
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                resetConnection();
            }
            PutMethod put = new PutMethod(putUrl);
            put.setRequestBody(new ByteArrayInputStream(out.toByteArray()));
            int result = put.execute(mHttpState, mConnection);
            //Log.e("result", result + "");
        } catch (UnsupportedEncodingException e) {
            Log.e(TAG, "UnsupportedEncodingException: Error uploading image: " + e.getMessage());
        } catch (IllegalStateException e) {
            Log.e(TAG, "IllegalStateException: Error uploading image: " + e.getMessage());
            resetConnection();
        } catch (ClientProtocolException e) {
            Log.e(TAG, "ClientProtocolException: Error uploading image: " + e.getMessage());
            resetConnection();
        } catch (UnknownHostException e) {
            Log.e(TAG, "UnknownHostException: Error uploading image: " + e.getMessage());
            resetConnection();
        } catch (NoHttpResponseException e) {
            // Silently ignore this.
        } catch (IOException e) {
            Log.e(TAG, "IOException: Error uploading image: " + e.getMessage());
            resetConnection();
        } finally {
            out.reset();
            if (mCamera != null) {
                mCamera.addCallbackBuffer(mCallbackBuffer);
            }
            isUploading = false;
        }

    }

    HttpClient httpclient = new DefaultHttpClient();

    String postUrl = "http://botczar.appspot.com/abc123/image";

    private void appEngineUploadImage(byte[] imageData) {
        Log.e("app engine remote eyes", "called");
        try {
            YuvImage yuvImage = new YuvImage(imageData, previewFormat, previewWidth, previewHeight, null);
            yuvImage.compressToJpeg(r, 20, out); // Tweak the quality here - 20
            // seems pretty decent for
            // quality + size.
            Log.e("app engine remote eyes", "upload starting");
            HttpPost httpPost = new HttpPost(postUrl);
            Log.e("app engine perf", "0");
            MultipartEntity entity = new MultipartEntity();
            Log.e("app engine perf", "1");
            entity.addPart("img", new InputStreamBody(new ByteArrayInputStream(out.toByteArray()), "video.jpg"));
            Log.e("app engine perf", "2");
            httpPost.setEntity(entity);
            Log.e("app engine perf", "3");
            HttpResponse response = httpclient.execute(httpPost);
            Log.e("app engine remote eyes", "result: " + response.getStatusLine());
            Log.e("app engine remote eyes", "upload complete");
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalStateException e) {
            e.printStackTrace();
            resetAppEngineConnection();
        } catch (ClientProtocolException e) {
            e.printStackTrace();
            resetAppEngineConnection();
        } catch (IOException e) {
            e.printStackTrace();
            resetAppEngineConnection();
        } finally {
            out.reset();
            if (mCamera != null) {
                mCamera.addCallbackBuffer(mCallbackBuffer);
            }
            isUploading = false;
            Log.e("app engine remote eyes", "finished");
        }
    }

    private void resetAppEngineConnection() {
        mConnection = new HttpConnection("http://botczar.appspot.com/", port);
        try {
            mConnection.open();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    private void setTorchMode(boolean on) {
        if (mCamera != null) {
            Parameters params = mCamera.getParameters();
            if (on) {
                params.setFlashMode(Parameters.FLASH_MODE_TORCH);
            } else {
                params.setFlashMode(Parameters.FLASH_MODE_AUTO);
            }
            mTorchMode = on;
            mCamera.setParameters(params);
        }
    }

    private void setTakePicture(boolean shouldTake) {
        needToTakePicture = shouldTake;
    }

    boolean needToTakePicture = false;

    private void takePicture(byte[] imageData) {
        YuvImage yuvImage = new YuvImage(imageData, previewFormat, previewWidth, previewHeight, null);
        yuvImage.compressToJpeg(r, 100, out);
        File dir = new File(Environment.getExternalStorageDirectory() + "/cellbot/pictures");
        dir.mkdirs();
        FileOutputStream outStream;
        try {
            String picName = dir.toString() + "/" + System.currentTimeMillis() + ".jpg";
            outStream = new FileOutputStream(picName);
            outStream.write(out.toByteArray());
            outStream.flush();
            outStream.close();
            Log.e("Picture saved:", picName);
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            needToTakePicture = false;
            out.reset();
            if (mCamera != null) {
                mCamera.addCallbackBuffer(mCallbackBuffer);
            }
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        menu.add(0, R.string.settings, 0, R.string.settings).setIcon(android.R.drawable.ic_menu_preferences);
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        Intent intent;
        switch (item.getItemId()) {
        case R.string.settings:
            intent = new Intent(this, PrefsActivity.class);
            startActivity(intent);
            finish();
            break;
        }
        return super.onOptionsItemSelected(item);
    }

}