FileDocCategorySizeDatePackage
Camera.javaAPI DocAndroid 1.5 API57945Wed May 06 22:42:42 BST 2009com.android.camera

Camera

public class Camera extends android.app.Activity implements ShutterButton.OnShutterButtonListener, SurfaceHolder.Callback, View.OnClickListener

Fields Summary
private static final String
TAG
private static final boolean
DEBUG
private static final boolean
DEBUG_TIME_OPERATIONS
private static final int
CROP_MSG
private static final int
RESTART_PREVIEW
private static final int
CLEAR_SCREEN_DELAY
private static final int
SCREEN_DELAY
private static final int
FOCUS_BEEP_VOLUME
public static final int
MENU_SWITCH_TO_VIDEO
public static final int
MENU_SWITCH_TO_CAMERA
public static final int
MENU_FLASH_SETTING
public static final int
MENU_FLASH_AUTO
public static final int
MENU_FLASH_ON
public static final int
MENU_FLASH_OFF
public static final int
MENU_SETTINGS
public static final int
MENU_GALLERY_PHOTOS
public static final int
MENU_GALLERY_VIDEOS
public static final int
MENU_SAVE_SELECT_PHOTOS
public static final int
MENU_SAVE_NEW_PHOTO
public static final int
MENU_SAVE_GALLERY_PHOTO
public static final int
MENU_SAVE_GALLERY_VIDEO_PHOTO
public static final int
MENU_SAVE_CAMERA_DONE
public static final int
MENU_SAVE_CAMERA_VIDEO_DONE
private android.view.OrientationEventListener
mOrientationListener
private int
mLastOrientation
private android.content.SharedPreferences
mPreferences
private static final int
IDLE
private static final int
SNAPSHOT_IN_PROGRESS
private static final int
SNAPSHOT_COMPLETED
private int
mStatus
private static final String
sTempCropFilename
private android.hardware.Camera
mCameraDevice
private android.hardware.Camera.Parameters
mParameters
private VideoPreview
mSurfaceView
private android.view.SurfaceHolder
mSurfaceHolder
private int
mOriginalViewFinderWidth
private int
mOriginalViewFinderHeight
private int
mViewFinderWidth
private int
mViewFinderHeight
private boolean
mPreviewing
private Capturer
mCaptureObject
private ImageCapture
mImageCapture
private boolean
mPausing
private static final int
FOCUS_NOT_STARTED
private static final int
FOCUSING
private static final int
FOCUSING_SNAP_ON_FINISH
private static final int
FOCUS_SUCCESS
private static final int
FOCUS_FAIL
private int
mFocusState
private static android.content.ContentResolver
mContentResolver
private boolean
mDidRegister
private ArrayList
mGalleryItems
private android.location.LocationManager
mLocationManager
private ShutterButton
mShutterButton
private android.view.animation.Animation
mFocusBlinkAnimation
private android.view.View
mFocusIndicator
private android.widget.ImageView
mGpsIndicator
private android.media.ToneGenerator
mFocusToneGenerator
private ShutterCallback
mShutterCallback
private RawPictureCallback
mRawPictureCallback
private AutoFocusCallback
mAutoFocusCallback
private long
mFocusStartTime
private long
mFocusCallbackTime
private long
mCaptureStartTime
private long
mShutterCallbackTime
private long
mRawPictureCallbackTime
private int
mPicturesRemaining
private boolean
mRecordLocation
private boolean
mKeepAndRestartPreview
private boolean
mIsImageCaptureIntent
private android.view.View
mPostCaptureAlert
private android.widget.ImageView
mLastPictureButton
private ThumbnailController
mThumbController
private android.os.Handler
mHandler
LocationListener[]
mLocationListeners
private final android.content.BroadcastReceiver
mReceiver
private boolean
mImageSavingItem
private OnScreenHint
mStorageHint
SelectedImageGetter
mSelectedImageGetter
Constructors Summary
Methods Summary
public static android.graphics.MatrixGetDisplayMatrix(android.graphics.Bitmap b, android.widget.ImageView v)

        Matrix m = new Matrix();
        float bw = (float)b.getWidth();
        float bh = (float)b.getHeight();
        float vw = (float)v.getWidth();
        float vh = (float)v.getHeight();
        float scale, x, y;
        if (bw*vh > vw*bh) {
            scale = vh / bh;
            x = (vw - scale*bw)*0.5F;
            y = 0;
        } else {
            scale = vw / bw;
            x = 0;
            y = (vh - scale*bh)*0.5F;
        }
        m.setScale(scale, scale, 0.5F, 0.5F);
        m.postTranslate(x, y);
        return m;
    
private voidaddBaseMenuItems(android.view.Menu menu)

        MenuHelper.addSwitchModeMenuItem(menu, this, true);
        {
            MenuItem gallery = menu.add(MenuHelper.IMAGE_MODE_ITEM, MENU_GALLERY_PHOTOS, 0, R.string.camera_gallery_photos_text).setOnMenuItemClickListener(new OnMenuItemClickListener() {
                public boolean onMenuItemClick(MenuItem item) {
                    gotoGallery();
                    return true;
                }
            });
            gallery.setIcon(android.R.drawable.ic_menu_gallery);
            mGalleryItems.add(gallery);
        }
        {
            MenuItem gallery = menu.add(MenuHelper.VIDEO_MODE_ITEM, MENU_GALLERY_VIDEOS, 0, R.string.camera_gallery_photos_text).setOnMenuItemClickListener(new OnMenuItemClickListener() {
                public boolean onMenuItemClick(MenuItem item) {
                    gotoGallery();
                    return true;
                }
            });
            gallery.setIcon(android.R.drawable.ic_menu_gallery);
            mGalleryItems.add(gallery);
        }

        MenuItem item = menu.add(MenuHelper.GENERIC_ITEM, MENU_SETTINGS, 0, R.string.settings).setOnMenuItemClickListener(new OnMenuItemClickListener() {
            public boolean onMenuItemClick(MenuItem item) {
                Intent intent = new Intent();
                intent.setClass(Camera.this, CameraSettings.class);
                startActivity(intent);
                return true;
            }
        });
        item.setIcon(android.R.drawable.ic_menu_preferences);
    
private voidautoFocus()

        updateFocusIndicator();
        if (mFocusState != FOCUSING && mFocusState != FOCUSING_SNAP_ON_FINISH) {
            if (mCameraDevice != null) {
                if (DEBUG_TIME_OPERATIONS) {
                    mFocusStartTime = System.currentTimeMillis();
                }
                mFocusState = FOCUSING;
                mCameraDevice.autoFocus(mAutoFocusCallback);
            }
        }
    
private intcalculatePicturesRemaining()


       
        mPicturesRemaining = MenuHelper.calculatePicturesRemaining();
        return mPicturesRemaining;
    
private voidcancelAutomaticPreviewRestart()

        mKeepAndRestartPreview = false;
        mHandler.removeMessages(RESTART_PREVIEW);
    
private voidclearFocusState()

        mFocusState = FOCUS_NOT_STARTED;
    
private voidcloseCamera()

        if (mCameraDevice != null) {
            mCameraDevice.release();
            mCameraDevice = null;
            mPreviewing = false;
        }
    
private static java.lang.StringcreateName(long dateTaken)

        return DateFormat.format("yyyy-MM-dd kk.mm.ss", dateTaken).toString();
    
private static ImageManager.DataLocationdataLocation()

        return ImageManager.DataLocation.EXTERNAL;
    
private voiddoAttach()

        if (mPausing) {
            return;
        }
        Bitmap bitmap = mImageCapture.getLastBitmap();

        String cropValue = null;
        Uri saveUri = null;

        Bundle myExtras = getIntent().getExtras();
        if (myExtras != null) {
            saveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
            cropValue = myExtras.getString("crop");
        }


        if (cropValue == null) {
            /*
             * First handle the no crop case -- just return the value.  If the caller
             * specifies a "save uri" then write the data to it's stream.  Otherwise,
             * pass back a scaled down version of the bitmap directly in the extras.
             */
            if (saveUri != null) {
                OutputStream outputStream = null;
                try {
                    outputStream = mContentResolver.openOutputStream(saveUri);
                    bitmap.compress(Bitmap.CompressFormat.JPEG, 75, outputStream);
                    outputStream.close();

                    setResult(RESULT_OK);
                    finish();
                } catch (IOException ex) {
                    //
                } finally {
                    if (outputStream != null) {
                        try {
                            outputStream.close();
                        } catch (IOException ex) {

                        }
                    }
                }
            } else {
                float scale = .5F;
                Matrix m = new Matrix();
                m.setScale(scale, scale);

                bitmap = Bitmap.createBitmap(bitmap, 0, 0,
                        bitmap.getWidth(),
                        bitmap.getHeight(),
                        m, true);

                setResult(RESULT_OK, new Intent("inline-data").putExtra("data", bitmap));
                finish();
            }
        }
        else {
            /*
             * Save the image to a temp file and invoke the cropper
             */
            Uri tempUri = null;
            FileOutputStream tempStream = null;
            try {
                File path = getFileStreamPath(sTempCropFilename);
                path.delete();
                tempStream = openFileOutput(sTempCropFilename, 0);
                bitmap.compress(Bitmap.CompressFormat.JPEG, 75, tempStream);
                tempStream.close();
                tempUri = Uri.fromFile(path);
            } catch (FileNotFoundException ex) {
                setResult(Activity.RESULT_CANCELED);
                finish();
                return;
            } catch (IOException ex) {
                setResult(Activity.RESULT_CANCELED);
                finish();
                return;
            } finally {
                if (tempStream != null) {
                    try {
                        tempStream.close();
                    } catch (IOException ex) {

                    }
                }
            }

            Bundle newExtras = new Bundle();
            if (cropValue.equals("circle"))
                newExtras.putString("circleCrop", "true");
            if (saveUri != null)
                newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, saveUri);
            else
                newExtras.putBoolean("return-data", true);

            Intent cropIntent = new Intent();
            cropIntent.setClass(Camera.this, CropImage.class);
            cropIntent.setData(tempUri);
            cropIntent.putExtras(newExtras);

            startActivityForResult(cropIntent, CROP_MSG);
        }
    
private voiddoCancel()

        setResult(RESULT_CANCELED, new Intent());
        finish();
    
private voiddoFocus(boolean pressed)

        if (pressed) {  // Focus key down.
            if (mPreviewing) {
                autoFocus();
            } else if (mCaptureObject != null) {
                // Save and restart preview
                mCaptureObject.onSnap();
            }
        } else {  // Focus key up.
            if (mFocusState != FOCUSING_SNAP_ON_FINISH) {
                // User releases half-pressed focus key.
                clearFocusState();
                updateFocusIndicator();
            }
        }
    
private voiddoSnap()

        // If the user has half-pressed the shutter and focus is completed, we
        // can take the photo right away.
        if ((mFocusState == FOCUS_SUCCESS || mFocusState == FOCUS_FAIL)
                || !mPreviewing) {
            // doesn't get set until the idler runs
            if (mCaptureObject != null) {
                mCaptureObject.onSnap();
            }
            clearFocusState();
            updateFocusIndicator();
        } else if (mFocusState == FOCUSING) {
            // Half pressing the shutter (i.e. the focus button event) will
            // already have requested AF for us, so just request capture on
            // focus here.
            mFocusState = FOCUSING_SNAP_ON_FINISH;
        } else if (mFocusState == FOCUS_NOT_STARTED) {
            // Focus key down event is dropped for some reasons. Just ignore.
        }
    
private booleanensureCameraDevice()

        if (mCameraDevice == null) {
            mCameraDevice = android.hardware.Camera.open();
        }
        return mCameraDevice != null;
    
private android.location.LocationgetCurrentLocation()

        // go in best to worst order
        for (int i = 0; i < mLocationListeners.length; i++) {
            Location l = mLocationListeners[i].current();
            if (l != null) return l;
        }
        return null;
    
private ImageManager.IImagegetImageForURI(android.net.Uri uri)

        ImageManager.IImageList list = ImageManager.instance().allImages(
                this,
                mContentResolver,
                dataLocation(),
                ImageManager.INCLUDE_IMAGES,
                ImageManager.SORT_ASCENDING);
        ImageManager.IImage image = list.getImageForUri(uri);
        list.deactivate();
        return image;
    
voidgotoGallery()

        MenuHelper.gotoCameraImageGallery(this);
    
private voidhidePostCaptureAlert()

        if (mIsImageCaptureIntent) {
            mPostCaptureAlert.setVisibility(View.INVISIBLE);
        }
    
private booleanisImageCaptureIntent()

        String action = getIntent().getAction();
        return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action));
    
voidkeep()

        if (mCaptureObject != null) {
            mCaptureObject.dismissFreezeFrame();
        }
    
protected voidonActivityResult(int requestCode, int resultCode, android.content.Intent data)

        switch (requestCode) {
            case CROP_MSG: {
                Intent intent = new Intent();
                if (data != null) {
                    Bundle extras = data.getExtras();
                    if (extras != null) {
                        intent.putExtras(extras);
                    }
                }
                setResult(resultCode, intent);
                finish();

                File path = getFileStreamPath(sTempCropFilename);
                path.delete();

                break;
            }
        }
    
public voidonClick(android.view.View v)

        switch (v.getId()) {
        case R.id.last_picture_button:
            if (mStatus == IDLE && mFocusState == FOCUS_NOT_STARTED) {
                viewLastImage();
            }
            break;
        case R.id.attach:
            doAttach();
            break;
        case R.id.cancel:
            doCancel();
        }
    
public voidonCreate(android.os.Bundle icicle)
Called with the activity is first created.

        super.onCreate(icicle);

        // To reduce startup time, we open camera device in another thread.
        // We make sure the camera is opened at the end of onCreate.
        Thread openCameraThread = new Thread(new Runnable() {
            public void run() {
                mCameraDevice = android.hardware.Camera.open();
            }
        });
        openCameraThread.start();

        // To reduce startup time, we run some service creation code in another thread.
        // We make sure the services are loaded at the end of onCreate().
        Thread loadServiceThread = new Thread(new Runnable() {
            public void run() {
                mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
                mOrientationListener = new OrientationEventListener(Camera.this) {
                    public void onOrientationChanged(int orientation) {
                        // We keep the last known orientation. So if the user
                        // first orient the camera then point the camera to
                        // floor/sky, we still have the correct orientation.
                        if (orientation != ORIENTATION_UNKNOWN)
                            mLastOrientation = orientation;
                    }
                };
            }
        });
        loadServiceThread.start();

        mPreferences = PreferenceManager.getDefaultSharedPreferences(this);
        mContentResolver = getContentResolver();

        Window win = getWindow();
        win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        setContentView(R.layout.camera);

        mSurfaceView = (VideoPreview) findViewById(R.id.camera_preview);
        mGpsIndicator = (ImageView) findViewById(R.id.gps_indicator);

        // don't set mSurfaceHolder here. We have it set ONLY within
        // surfaceCreated / surfaceDestroyed, other parts of the code
        // assume that when it is set, the surface is also set.
        SurfaceHolder holder = mSurfaceView.getHolder();
        holder.addCallback(this);
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

        mIsImageCaptureIntent = isImageCaptureIntent();

        if (!mIsImageCaptureIntent)  {
            mLastPictureButton = (ImageView) findViewById(R.id.last_picture_button);
            mLastPictureButton.setOnClickListener(this);
            Drawable frame = getResources().getDrawable(R.drawable.frame_thumbnail);
            mThumbController = new ThumbnailController(mLastPictureButton,
                    frame, mContentResolver);
            mThumbController.loadData(ImageManager.getLastImageThumbPath());
        }

        mShutterButton = (ShutterButton) findViewById(R.id.shutter_button);
        mShutterButton.setOnShutterButtonListener(this);

        mFocusIndicator = findViewById(R.id.focus_indicator);
        mFocusBlinkAnimation = AnimationUtils.loadAnimation(this, R.anim.auto_focus_blink);
        mFocusBlinkAnimation.setRepeatCount(Animation.INFINITE);
        mFocusBlinkAnimation.setRepeatMode(Animation.REVERSE);

        // We load the post_picture_panel layout only if it is needed.
        if (mIsImageCaptureIntent) {
            ViewGroup cameraView = (ViewGroup)findViewById(R.id.camera);
            getLayoutInflater().inflate(R.layout.post_picture_panel,
                                        cameraView);
            mPostCaptureAlert = findViewById(R.id.post_picture_panel);
        }

        // Make sure the services are loaded.
        try {
            openCameraThread.join();
            loadServiceThread.join();
        } catch (InterruptedException ex) {
        }

        ImageManager.ensureOSXCompatibleFolder();
    
public booleanonCreateOptionsMenu(android.view.Menu menu)

        super.onCreateOptionsMenu(menu);

        if (mIsImageCaptureIntent) {
            // No options menu for attach mode.
            return false;
        } else {
            addBaseMenuItems(menu);
        }
        return true;
    
public booleanonKeyDown(int keyCode, android.view.KeyEvent event)

        mHandler.sendEmptyMessageDelayed(CLEAR_SCREEN_DELAY, SCREEN_DELAY);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

        switch (keyCode) {
            case KeyEvent.KEYCODE_BACK:
                if (mStatus == SNAPSHOT_IN_PROGRESS) {
                    // ignore backs while we're taking a picture
                    return true;
                }
                break;
            case KeyEvent.KEYCODE_FOCUS:
                if (event.getRepeatCount() == 0) {
                    doFocus(true);
                }
                return true;
            case KeyEvent.KEYCODE_CAMERA:
                if (event.getRepeatCount() == 0) {
                    doSnap();
                }
                return true;
            case KeyEvent.KEYCODE_DPAD_CENTER:
                // If we get a dpad center event without any focused view, move the
                // focus to the shutter button and press it.
                if (event.getRepeatCount() == 0) {
                    // Start auto-focus immediately to reduce shutter lag. After the shutter button
                    // gets the focus, doFocus() will be called again but it is fine.
                    doFocus(true);
                    if (mShutterButton.isInTouchMode()) {
                        mShutterButton.requestFocusFromTouch();
                    } else {
                        mShutterButton.requestFocus();
                    }
                    mShutterButton.setPressed(true);
                }
                return true;
        }

        return super.onKeyDown(keyCode, event);
    
public booleanonKeyUp(int keyCode, android.view.KeyEvent event)

        switch (keyCode) {
            case KeyEvent.KEYCODE_FOCUS:
                doFocus(false);
                return true;
        }
        return super.onKeyUp(keyCode, event);
    
public booleanonMenuOpened(int featureId, android.view.Menu menu)

        if (featureId == Window.FEATURE_OPTIONS_PANEL) {
            if (mStatus == SNAPSHOT_IN_PROGRESS) {
                cancelAutomaticPreviewRestart();
            }
        }
        return super.onMenuOpened(featureId, menu);
    
public voidonOptionsMenuClosed(android.view.Menu menu)

        super.onOptionsMenuClosed(menu);
        if (mImageSavingItem) {
            // save the image if we presented the "advanced" menu
            // which happens if "menu" is pressed while in
            // SNAPSHOT_IN_PROGRESS  or SNAPSHOT_COMPLETED modes
            keep();
            mHandler.sendEmptyMessage(RESTART_PREVIEW);
        }
    
protected voidonPause()

        keep();

        mPausing = true;
        mOrientationListener.disable();

        stopPreview();

        if (!mImageCapture.mCapturing) {
            closeCamera();
        }
        if (mDidRegister) {
            unregisterReceiver(mReceiver);
            mDidRegister = false;
        }
        stopReceivingLocationUpdates();

        if (mFocusToneGenerator != null) {
            mFocusToneGenerator.release();
            mFocusToneGenerator = null;
        }

        if (!mIsImageCaptureIntent) {
            mThumbController.storeData(ImageManager.getLastImageThumbPath());
        }

        if (mStorageHint != null) {
            mStorageHint.cancel();
            mStorageHint = null;
        }

        // If we are in an image capture intent and has taken
        // a picture, we just clear it in onPause.
        mImageCapture.clearLastBitmap();
        mImageCapture = null;
        hidePostCaptureAlert();

        super.onPause();
    
public booleanonPrepareOptionsMenu(android.view.Menu menu)

        super.onPrepareOptionsMenu(menu);

        for (int i = 1; i <= MenuHelper.MENU_ITEM_MAX; i++) {
            if (i != MenuHelper.GENERIC_ITEM) {
                menu.setGroupVisible(i, false);
            }
        }

        if (mStatus == SNAPSHOT_IN_PROGRESS || mStatus == SNAPSHOT_COMPLETED) {
            menu.setGroupVisible(MenuHelper.IMAGE_SAVING_ITEM, true);
            mImageSavingItem = true;
        } else {
            menu.setGroupVisible(MenuHelper.IMAGE_MODE_ITEM, true);
            mImageSavingItem = false;
        }

        return true;
    
public voidonResume()

        super.onResume();
        mHandler.sendEmptyMessageDelayed(CLEAR_SCREEN_DELAY, SCREEN_DELAY);

        mPausing = false;
        mOrientationListener.enable();
        mRecordLocation = mPreferences.getBoolean(
                "pref_camera_recordlocation_key", false);
        mGpsIndicator.setVisibility(View.INVISIBLE);

        // install an intent filter to receive SD card related events.
        IntentFilter intentFilter = new IntentFilter(Intent.ACTION_MEDIA_MOUNTED);
        intentFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
        intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_STARTED);
        intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
        intentFilter.addAction(Intent.ACTION_MEDIA_CHECKING);
        intentFilter.addDataScheme("file");
        registerReceiver(mReceiver, intentFilter);
        mDidRegister = true;

        mImageCapture = new ImageCapture();

        restartPreview();

        if (mRecordLocation) startReceivingLocationUpdates();

        updateFocusIndicator();

        try {
            mFocusToneGenerator = new ToneGenerator(AudioManager.STREAM_SYSTEM, FOCUS_BEEP_VOLUME);
        } catch (RuntimeException e) {
            Log.w(TAG, "Exception caught while creating local tone generator: " + e);
            mFocusToneGenerator = null;
        }
    
public voidonShutterButtonClick(ShutterButton button)

        if (mPausing) {
            return;
        }
        switch (button.getId()) {
            case R.id.shutter_button:
                doSnap();
                break;
        }
    
public voidonShutterButtonFocus(ShutterButton button, boolean pressed)

        if (mPausing) {
            return;
        }
        switch (button.getId()) {
            case R.id.shutter_button:
                doFocus(pressed);
                break;
        }
    
public voidonStart()

        super.onStart();

        Thread t = new Thread(new Runnable() {
            public void run() {
                final boolean storageOK = calculatePicturesRemaining() > 0;

                if (!storageOK) {
                    mHandler.post(new Runnable() {
                        public void run() {
                            updateStorageHint(mPicturesRemaining);
                        }
                    });
                }
            }
        });
        t.start();
    
public voidonStop()

        keep();
        stopPreview();
        closeCamera();
        mHandler.removeMessages(CLEAR_SCREEN_DELAY);
        super.onStop();
    
private voidrestartPreview()

        VideoPreview surfaceView = mSurfaceView;

        // make sure the surfaceview fills the whole screen when previewing
        surfaceView.setAspectRatio(VideoPreview.DONT_CARE);
        setViewFinder(mOriginalViewFinderWidth, mOriginalViewFinderHeight, true);
        mStatus = IDLE;

        // Calculate this in advance of each shot so we don't add to shutter latency. It's true that
        // someone else could write to the SD card in the mean time and fill it, but that could have
        // happened between the shutter press and saving the JPEG too.
        // TODO: The best longterm solution is to write a reserve file of maximum JPEG size, always
        // let the user take a picture, and delete that file if needed to save the new photo.
        calculatePicturesRemaining();

        if (!mIsImageCaptureIntent && !mThumbController.isUriValid()) {
            updateLastImage();
        }

        if (!mIsImageCaptureIntent) {
            mThumbController.updateDisplayIfNeeded();
        }
    
private voidsetLastPictureThumb(byte[] data, android.net.Uri uri)

        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize = 16;
        Bitmap lastPictureThumb = BitmapFactory.decodeByteArray(data, 0, data.length, options);
        mThumbController.setData(uri, lastPictureThumb);
    
private voidsetViewFinder(int w, int h, boolean startPreview)

        if (mPausing)
            return;

        if (mPreviewing &&
                w == mViewFinderWidth &&
                h == mViewFinderHeight) {
            return;
        }

        if (!ensureCameraDevice())
            return;

        if (mSurfaceHolder == null)
            return;

        if (isFinishing())
            return;

        if (mPausing)
            return;

        // remember view finder size
        mViewFinderWidth = w;
        mViewFinderHeight = h;
        if (mOriginalViewFinderHeight == 0) {
            mOriginalViewFinderWidth = w;
            mOriginalViewFinderHeight = h;
        }

        if (startPreview == false)
            return;

        /*
         * start the preview if we're asked to...
         */

        // we want to start the preview and we're previewing already,
        // stop the preview first (this will blank the screen).
        if (mPreviewing)
            stopPreview();

        // this blanks the screen if the surface changed, no-op otherwise
        try {
            mCameraDevice.setPreviewDisplay(mSurfaceHolder);
        } catch (IOException exception) {
            mCameraDevice.release();
            mCameraDevice = null;
            // TODO: add more exception handling logic here
            return;
        }

        // request the preview size, the hardware may not honor it,
        // if we depended on it we would have to query the size again
        mParameters = mCameraDevice.getParameters();
        mParameters.setPreviewSize(w, h);
        try {
            mCameraDevice.setParameters(mParameters);
        } catch (IllegalArgumentException e) {
            // Ignore this error, it happens in the simulator.
        }


        final long wallTimeStart = SystemClock.elapsedRealtime();
        final long threadTimeStart = Debug.threadCpuTimeNanos();

        final Object watchDogSync = new Object();
        Thread watchDog = new Thread(new Runnable() {
            public void run() {
                int next_warning = 1;
                while (true) {
                    try {
                        synchronized (watchDogSync) {
                            watchDogSync.wait(1000);
                        }
                    } catch (InterruptedException ex) {
                        //
                    }
                    if (mPreviewing) break;

                    int delay = (int) (SystemClock.elapsedRealtime() - wallTimeStart) / 1000;
                    if (delay >= next_warning) {
                        if (delay < 120) {
                            Log.e(TAG, "preview hasn't started yet in " + delay + " seconds");
                        } else {
                            Log.e(TAG, "preview hasn't started yet in " + (delay / 60) + " minutes");
                        }
                        if (next_warning < 60) {
                            next_warning <<= 1;
                            if (next_warning == 16) {
                                next_warning = 15;
                            }
                        } else {
                            next_warning += 60;
                        }
                    }
                }
            }
        });

        watchDog.start();

        if (Config.LOGV)
            Log.v(TAG, "calling mCameraDevice.startPreview");
        try {
            mCameraDevice.startPreview();
        } catch (Throwable e) {
            // TODO: change Throwable to IOException once android.hardware.Camera.startPreview
            // properly declares that it throws IOException.
        }
        mPreviewing = true;

        synchronized (watchDogSync) {
            watchDogSync.notify();
        }

        long threadTimeEnd = Debug.threadCpuTimeNanos();
        long wallTimeEnd = SystemClock.elapsedRealtime();
        if ((wallTimeEnd - wallTimeStart) > 3000) {
            Log.w(TAG, "startPreview() to " + (wallTimeEnd - wallTimeStart) + " ms. Thread time was"
                    + (threadTimeEnd - threadTimeStart) / 1000000 + " ms.");
        }
    
private voidshowPostCaptureAlert()

        if (mIsImageCaptureIntent) {
            mPostCaptureAlert.setVisibility(View.VISIBLE);
            int[] pickIds = {R.id.attach, R.id.cancel};
            for(int id : pickIds) {
                View view = mPostCaptureAlert.findViewById(id);
                view.setOnClickListener(this);
                Animation animation = new AlphaAnimation(0F, 1F);
                animation.setDuration(500);
                view.setAnimation(animation);
            }
        }
    
private voidstartReceivingLocationUpdates()

        if (mLocationManager != null) {
            try {
                mLocationManager.requestLocationUpdates(
                        LocationManager.NETWORK_PROVIDER,
                        1000,
                        0F,
                        mLocationListeners[1]);
            } catch (java.lang.SecurityException ex) {
                // ok
            } catch (IllegalArgumentException ex) {
                if (Config.LOGD) {
                    Log.d(TAG, "provider does not exist " + ex.getMessage());
                }
            }
            try {
                mLocationManager.requestLocationUpdates(
                        LocationManager.GPS_PROVIDER,
                        1000,
                        0F,
                        mLocationListeners[0]);
            } catch (java.lang.SecurityException ex) {
                // ok
            } catch (IllegalArgumentException ex) {
                if (Config.LOGD) {
                    Log.d(TAG, "provider does not exist " + ex.getMessage());
                }
            }
        }
    
private voidstopPreview()

        if (mCameraDevice != null && mPreviewing) {
            mCameraDevice.stopPreview();
        }
        mPreviewing = false;
        // If auto focus was in progress, it would have been canceled.
        clearFocusState();
    
private voidstopReceivingLocationUpdates()

        if (mLocationManager != null) {
            for (int i = 0; i < mLocationListeners.length; i++) {
                try {
                    mLocationManager.removeUpdates(mLocationListeners[i]);
                } catch (Exception ex) {
                    // ok
                }
            }
        }
    
public voidsurfaceChanged(android.view.SurfaceHolder holder, int format, int w, int h)

        mSurfaceView.setVisibility(View.VISIBLE);
        // if we're creating the surface, start the preview as well.
        boolean preview = holder.isCreating();
        setViewFinder(w, h, preview);
        mCaptureObject = mImageCapture;
    
public voidsurfaceCreated(android.view.SurfaceHolder holder)

        mSurfaceHolder = holder;
    
public voidsurfaceDestroyed(android.view.SurfaceHolder holder)

        stopPreview();
        mSurfaceHolder = null;
    
private voidupdateFocusIndicator()

        mHandler.post(new Runnable() {
            public void run() {
                if (mFocusState == FOCUS_SUCCESS) {
                    mFocusIndicator.setVisibility(View.VISIBLE);
                    mFocusIndicator.clearAnimation();
                } else if (mFocusState == FOCUS_FAIL) {
                    mFocusIndicator.setVisibility(View.VISIBLE);
                    mFocusIndicator.startAnimation(mFocusBlinkAnimation);
                } else {
                    mFocusIndicator.setVisibility(View.GONE);
                    mFocusIndicator.clearAnimation();
                }
            }
        });
    
private voidupdateLastImage()

        ImageManager.IImageList list = ImageManager.instance().allImages(
            this,
            mContentResolver,
            dataLocation(),
            ImageManager.INCLUDE_IMAGES,
            ImageManager.SORT_ASCENDING,
            ImageManager.CAMERA_IMAGE_BUCKET_ID);
        int count = list.getCount();
        if (count > 0) {
            ImageManager.IImage image = list.getImageAt(count-1);
            Uri uri = image.fullSizeImageUri();
            mThumbController.setData(uri, image.miniThumbBitmap());
        } else {
            mThumbController.setData(null, null);
        }
        list.deactivate();
    
private voidupdateStorageHint()

      updateStorageHint(MenuHelper.calculatePicturesRemaining());
    
private voidupdateStorageHint(int remaining)

        String noStorageText = null;

        if (remaining == MenuHelper.NO_STORAGE_ERROR) {
            String state = Environment.getExternalStorageState();
            if (state == Environment.MEDIA_CHECKING) {
                noStorageText = getString(R.string.preparing_sd);
            } else {
                noStorageText = getString(R.string.no_storage);
            }
        } else if (remaining < 1) {
            noStorageText = getString(R.string.not_enough_space);
        }

        if (noStorageText != null) {
            if (mStorageHint == null) {
                mStorageHint = OnScreenHint.makeText(this, noStorageText);
            } else {
                mStorageHint.setText(noStorageText);
            }
            mStorageHint.show();
        } else if (mStorageHint != null) {
            mStorageHint.cancel();
            mStorageHint = null;
        }
    
private voidviewLastImage()

        if (mThumbController.isUriValid()) {
            Uri targetUri = mThumbController.getUri();
            targetUri = targetUri.buildUpon().
                appendQueryParameter("bucketId", ImageManager.CAMERA_IMAGE_BUCKET_ID).build();
            Intent intent = new Intent(Intent.ACTION_VIEW, targetUri);
            intent.putExtra(MediaStore.EXTRA_SCREEN_ORIENTATION,
                    ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
            intent.putExtra(MediaStore.EXTRA_FULL_SCREEN, true);
            intent.putExtra(MediaStore.EXTRA_SHOW_ACTION_ICONS, true);
            intent.putExtra("com.android.camera.ReviewMode", true);

            try {
                startActivity(intent);
            } catch (android.content.ActivityNotFoundException ex) {
                // ignore.
            }
        } else {
            Log.e(TAG, "Can't view last image.");
        }