Java tutorial
// __BEGIN_LICENSE__ // Copyright (C) 2008-2010 United States Government as represented by // the Administrator of the National Aeronautics and Space Administration. // All Rights Reserved. // __END_LICENSE__ package org.ros.android.android_tutorial_teleop.geocam; import java.io.OutputStream; import java.io.IOException; import java.text.NumberFormat; import java.util.Formatter; import java.util.List; import java.util.UUID; import org.json.JSONException; import org.json.JSONObject; import org.ros.android.android_tutorial_teleop.R; import org.ros.android.android_tutorial_teleop.geocam.util.ForegroundTracker; import org.ros.android.android_tutorial_teleop.geocam.util.Reflect; import android.app.Activity; import android.app.Dialog; import android.app.ProgressDialog; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.res.Configuration; import android.database.Cursor; import android.hardware.Camera; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.location.Location; import android.net.Uri; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.provider.MediaStore; import android.util.Log; import android.view.KeyEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.TextView; import android.graphics.PixelFormat; public class CameraActivity extends Activity implements SurfaceHolder.Callback { private static final int DIALOG_SAVE_PROGRESS = 991; private static final int DIALOG_HIDE_KEYBOARD = 992; // UI elements private ProgressDialog mSaveProgressDialog; private Dialog mHideKeyboardDialog; private ImageView mFocusRect; // Camera preview surface private SurfaceView mCameraPreview; SurfaceHolder mHolder; // Camera Camera mCamera; private boolean mLensIsFocused = false; private boolean mPictureTaken = false; private byte mImageBytes[]; private JSONObject mImageData; private Uri mImageUri; // Orientation private SensorManager mSensorManager; private float[] mOrientation = { 0.0f, 0.0f, 0.0f }; private float[] mAccelerometerData = { 0.0f, 0.0f, 0.0f }; private float[] mMagneticFieldData = { 0.0f, 0.0f, 0.0f }; private boolean mAccelerometerGood = false, mMagneticFieldGood = false; private final SensorEventListener mSensorListener = new SensorEventListener() { private NumberFormat mmRPYFormatter = NumberFormat.getInstance(); private TextView mmRollText = null; private TextView mmPitchText = null; private TextView mmYawText = null; public void onAccuracyChanged(Sensor sensor, int accuracy) { } public void onSensorChanged(SensorEvent event) { switch (event.sensor.getType()) { case Sensor.TYPE_ACCELEROMETER: mAccelerometerGood = true; mAccelerometerData = event.values.clone(); break; case Sensor.TYPE_MAGNETIC_FIELD: mMagneticFieldGood = true; mMagneticFieldData = event.values.clone(); break; } float[] rotationMatrix = new float[16]; float[] inclinationMatrix = new float[16]; float[] remappedRotationMatrix = new float[16]; // yaw, pitch, roll mOrientation[0] = mOrientation[1] = mOrientation[2] = 0.0f; if (mAccelerometerGood && mMagneticFieldGood) { boolean rotationGood = SensorManager.getRotationMatrix(rotationMatrix, inclinationMatrix, mAccelerometerData, mMagneticFieldData); rotationGood &= SensorManager.remapCoordinateSystem(rotationMatrix, SensorManager.AXIS_Z, SensorManager.AXIS_MINUS_X, remappedRotationMatrix); if (rotationGood) { SensorManager.getOrientation(remappedRotationMatrix, mOrientation); mOrientation[2] *= 180.0 / Math.PI; mOrientation[1] *= -180.0 / Math.PI; mOrientation[0] *= 180.0 / Math.PI; // map yaw from -180->180 to 0->360 if (mOrientation[0] < 0) { mOrientation[0] += 360.0f; } } } if (mmRollText == null) { mmRollText = (TextView) findViewById(R.id.camera_textview_roll); mmPitchText = (TextView) findViewById(R.id.camera_textview_pitch); mmYawText = (TextView) findViewById(R.id.camera_textview_yaw); mmRPYFormatter.setMaximumFractionDigits(1); } mmRollText.setText("Roll: " + mmRPYFormatter.format(mOrientation[2]) + "\u00b0"); mmPitchText.setText("Pitch: " + mmRPYFormatter.format(mOrientation[1]) + "\u00b0"); mmYawText.setText("Yaw: " + mmRPYFormatter.format(mOrientation[0]) + "\u00b0"); } }; // Location private LocationReceiver mLocationReceiver; private Location mLocation; private NumberFormat mLocationFormatter = NumberFormat.getInstance(); private TextView mLatText; private TextView mLonText; // GeoCam Service private IGeoCamService mService; private boolean mServiceBound = false; private ServiceConnection mServiceConn = new ServiceConnection() { public void onServiceConnected(ComponentName name, IBinder service) { mService = IGeoCamService.Stub.asInterface(service); mServiceBound = true; try { mLocation = mService.getLocation(); if (mLocation != null) { Log.d(GeoCamMobile.DEBUG_ID, "CameraActivity::onServiceConnected"); } else { Log.d(GeoCamMobile.DEBUG_ID, "CameraActivity::onServiceConnected - no location"); } } catch (RemoteException e) { Log.e(GeoCamMobile.DEBUG_ID, "CameraActivity::onServiceConnected - error getting location from service"); } } public void onServiceDisconnected(ComponentName name) { mService = null; mServiceBound = false; } }; // Foreground/background private ForegroundTracker mForeground; // Activity methods @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Window and view properties requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.camera); // Location mLocationReceiver = new LocationReceiver(); mLocationFormatter.setMaximumFractionDigits(6); mLatText = (TextView) findViewById(R.id.camera_textview_lat); mLonText = (TextView) findViewById(R.id.camera_textview_lon); // Orientation mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); // Camera mCameraPreview = (SurfaceView) findViewById(R.id.camera_surfaceview); mHolder = mCameraPreview.getHolder(); mHolder.addCallback(this); mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); // UI elements mFocusRect = (ImageView) findViewById(R.id.camera_focus_imageview); mFocusRect.setImageDrawable(getResources().getDrawable(R.drawable.frame_unfocused_64x48)); final ImageButton shutterButton = (ImageButton) findViewById(R.id.camera_shutter_button); shutterButton.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { if (!mPictureTaken) { mPictureTaken = true; if (!mLensIsFocused) { mFocusRect.setImageDrawable(getResources().getDrawable(R.drawable.frame_unfocused_64x48)); mLensIsFocused = true; CameraActivity.this.focusLens(true); } else { CameraActivity.this.takePicture(); } } } }); mForeground = new ForegroundTracker(this); } @Override public void onDestroy() { super.onDestroy(); } @Override public void onPause() { super.onPause(); mForeground.background(); if (mServiceBound) unbindService(mServiceConn); this.unregisterReceiver(mLocationReceiver); mSensorManager.unregisterListener(mSensorListener); } @Override public void onResume() { super.onResume(); mServiceBound = bindService(new Intent(this, GeoCamService.class), mServiceConn, Context.BIND_AUTO_CREATE); if (!mServiceBound) { Log.e(GeoCamMobile.DEBUG_ID, "CameraActivity::onResume - error binding to service"); } mForeground.foreground(); mSensorManager.registerListener(mSensorListener, mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_GAME); mSensorManager.registerListener(mSensorListener, mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD), SensorManager.SENSOR_DELAY_GAME); mLatText.setText("Lat: unavailable"); mLonText.setText("Lon: unavailable"); IntentFilter filter = new IntentFilter(GeoCamMobile.LOCATION_CHANGED); this.registerReceiver(mLocationReceiver, filter); if (getResources().getConfiguration().hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO) { showDialog(DIALOG_HIDE_KEYBOARD); } // Unset focus and picture status flags when returning from another activity mLensIsFocused = false; mPictureTaken = false; } // Show hide keyboard dialog if keyboard is open in this activity // This overriding this method with the appropriate entry in the manifest // prevents the activity from restarting when the hardware keyboard is opened @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); Log.d(GeoCamMobile.DEBUG_ID, "Keyboard hidden: " + String.valueOf(newConfig.keyboardHidden)); if (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO) { showDialog(DIALOG_HIDE_KEYBOARD); } else if (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) { dismissDialog(DIALOG_HIDE_KEYBOARD); } } public boolean onKeyUp(int keyCode, KeyEvent event) { switch (keyCode) { case KeyEvent.KEYCODE_FOCUS: mFocusRect.setImageDrawable(getResources().getDrawable(R.drawable.frame_unfocused_64x48)); mLensIsFocused = false; break; case KeyEvent.KEYCODE_CAMERA: case KeyEvent.KEYCODE_DPAD_CENTER: mPictureTaken = false; break; } return super.onKeyUp(keyCode, event); } public boolean onKeyDown(int keyCode, KeyEvent event) { switch (keyCode) { case KeyEvent.KEYCODE_FOCUS: if (!mLensIsFocused) { mFocusRect.setImageDrawable(getResources().getDrawable(R.drawable.frame_unfocused_64x48)); mLensIsFocused = true; this.focusLens(false); } break; case KeyEvent.KEYCODE_CAMERA: case KeyEvent.KEYCODE_DPAD_CENTER: if (!mPictureTaken) { mPictureTaken = true; if (!mLensIsFocused) { mFocusRect.setImageDrawable(getResources().getDrawable(R.drawable.frame_unfocused_64x48)); mLensIsFocused = true; this.focusLens(true); } else { this.takePicture(); } } // Return here after catching camera keycode so we don't launch the built-in camera app return true; } return super.onKeyDown(keyCode, event); } @Override protected Dialog onCreateDialog(int id) { switch (id) { case DIALOG_SAVE_PROGRESS: mSaveProgressDialog = new ProgressDialog(this); mSaveProgressDialog.setMessage(getResources().getString(R.string.camera_saving)); mSaveProgressDialog.setIndeterminate(true); mSaveProgressDialog.setCancelable(false); return mSaveProgressDialog; case DIALOG_HIDE_KEYBOARD: mHideKeyboardDialog = new Dialog(this); mHideKeyboardDialog.setTitle(getResources().getString(R.string.camera_hide_keyboard)); return mHideKeyboardDialog; default: break; } return null; } // Camera preview surface methods public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { Camera.Parameters parameters = mCamera.getParameters(); int wDiff = w; int hDiff = h; int realWidth = w, realHeight = h; List<Camera.Size> sizes = Reflect.CameraParameters.getSupportedPreviewSizes(parameters); if (sizes != null) { for (Camera.Size size : sizes) { int tempWDiff = Math.abs(size.width - w); int tempHDiff = Math.abs(size.height - h); if ((tempWDiff + tempHDiff) < (wDiff + hDiff)) { wDiff = tempWDiff; hDiff = tempHDiff; realWidth = size.width; realHeight = size.height; } } } parameters.setPreviewSize(realWidth, realHeight); parameters.setPictureFormat(PixelFormat.JPEG); mCamera.setParameters(parameters); mCamera.startPreview(); } public void surfaceCreated(SurfaceHolder holder) { mCamera = Camera.open(); try { mCamera.setPreviewDisplay(holder); } catch (IOException e) { Log.e(GeoCamMobile.DEBUG_ID, "mCamera.setPreviewDisplay threw an IOException: " + e); } } public void surfaceDestroyed(SurfaceHolder arg0) { mCamera.stopPreview(); mCamera.release(); mCamera = null; } // Camera methods public void focusLens(final boolean takePicture) { mCamera.autoFocus(new Camera.AutoFocusCallback() { public void onAutoFocus(boolean success, Camera camera) { if (success) { mFocusRect.setImageDrawable(getResources().getDrawable(R.drawable.frame_focused_64x48)); } else { mFocusRect.setImageDrawable(getResources().getDrawable(R.drawable.frame_unfocused_64x48)); } if (takePicture) CameraActivity.this.takePicture(); } }); } public void takePicture() { mCamera.takePicture(new Camera.ShutterCallback() { public void onShutter() { return; } }, null, new Camera.PictureCallback() { public void onPictureTaken(byte[] data, Camera camera) { showDialog(DIALOG_SAVE_PROGRESS); mImageBytes = data; Thread t = new Thread() { public void run() { saveImage(); } }; t.start(); } }); if (mServiceBound) { try { mService.increaseLocationUpdateRate(); } catch (RemoteException e) { Log.e(GeoCamMobile.DEBUG_ID, "Error increasing location update rate after takePicture"); } } } private void saveImage() { Log.d(GeoCamMobile.DEBUG_ID, "Saving orientation: " + mOrientation[2] + "," + mOrientation[1] + "," + mOrientation[0]); // Store orientation data in description field of mediastore using JSON encoding JSONObject imageData = new JSONObject(); try { imageData.put("rpy", GeoCamMobile.rpySerialize(mOrientation[2], mOrientation[1], mOrientation[0])); imageData.put("yawRef", GeoCamMobile.YAW_MAGNETIC); imageData.put("uuid", UUID.randomUUID()); Log.d(GeoCamMobile.DEBUG_ID, "Saving image with data: " + imageData.toString()); } catch (JSONException e) { Log.d(GeoCamMobile.DEBUG_ID, "Error while adding JSON data to image"); } mImageData = imageData; double lat, lon; if (mLocation == null) { lat = 0.0; lon = 0.0; } else { lat = mLocation.getLatitude(); lon = mLocation.getLongitude(); } // Add some parameters to the image that will be stored in the Image ContentProvider ContentValues values = new ContentValues(); String name = String.valueOf(System.currentTimeMillis()); values.put(MediaStore.Images.Media.DISPLAY_NAME, name + ".jpg"); values.put(MediaStore.Images.Media.TITLE, name); values.put(MediaStore.Images.Media.DESCRIPTION, mImageData.toString()); values.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis()); values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg"); values.put(MediaStore.Images.Media.SIZE, mImageBytes.length); values.put(MediaStore.Images.Media.LATITUDE, lat); values.put(MediaStore.Images.Media.LONGITUDE, lon); // There appears to be a bug where saveImage() sometimes fails to actually create an entry // in the db so we do one retry int initNumEntries = getMediaStoreNumEntries(); mImageUri = saveImage(values); if (getMediaStoreNumEntries() <= initNumEntries) { Log.d(GeoCamMobile.DEBUG_ID, "Retrying save"); mImageUri = saveImage(values); } mImageBytes = null; Log.d(GeoCamMobile.DEBUG_ID, "Trying to force a GC"); System.gc(); dismissDialog(DIALOG_SAVE_PROGRESS); // Start camera preview activity Intent i = new Intent(Intent.ACTION_VIEW, mImageUri); i.setClass(getApplication(), CameraPreviewActivity.class); i.putExtra("data", mImageData.toString()); startActivity(i); } private Uri saveImage(ContentValues values) { Log.d(GeoCamMobile.DEBUG_ID, "About to insert image into mediastore"); // Inserting the image meta data inside the content provider Uri uri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); Log.d(GeoCamMobile.DEBUG_ID, "Inserted metadata into mediastore"); // Filling the real data returned by the picture callback function into the content provider try { OutputStream outStream = getContentResolver().openOutputStream(uri); outStream.write(mImageBytes); outStream.close(); Log.d(GeoCamMobile.DEBUG_ID, "Saved image data into mediastore"); } catch (Exception e) { Log.d(GeoCamMobile.DEBUG_ID, "Exception while writing image: ", e); } return uri; } private int getMediaStoreNumEntries() { Cursor cur = managedQuery(GeoCamMobile.MEDIA_URI, null, null, null, null); cur.moveToFirst(); Log.d(GeoCamMobile.DEBUG_ID, "Retrieving list of photos"); int count = 0; while (cur.moveToNext()) { count++; } Log.d(GeoCamMobile.DEBUG_ID, "Found " + count + " photos"); return count; } class LocationReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { mLocation = intent.getParcelableExtra(GeoCamMobile.LOCATION_EXTRA); mLatText.setText("Lat: " + mLocationFormatter.format(mLocation.getLatitude()) + "\u00b0"); mLonText.setText("Lon: " + mLocationFormatter.format(mLocation.getLongitude()) + "\u00b0"); Log.d(GeoCamMobile.DEBUG_ID, "CameraActivity::LocationReceiver.onReceive"); } } }