/*
* Copyright (C) 2009 The Android Open Source Project
*
* 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.
*/
/*
* Modified by Nilesh Patel
*/
package com.lightbox.android.camera.device;
import static com.lightbox.android.camera.Util.Assert;
import android.hardware.Camera.Parameters;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import com.lightbox.android.camera.CameraHardwareException;
import com.lightbox.android.camera.device.CameraHolder;
import com.lightbox.android.camera.device.GingerbreadCameraHolder;
import com.lightbox.android.camera.device.HTCFrontFacingFixGingerbreadCameraHolder;
import com.lightbox.android.camera.device.LGOptimus2XCameraHolder;
import com.lightbox.android.camera.device.PreGingerbreadCameraHolder;
/**
* The class is used to hold an {@code android.hardware.Camera} instance.
*
* <p>The {@code open()} and {@code release()} calls are similar to the ones
* in {@code android.hardware.Camera}. The difference is if {@code keep()} is
* called before {@code release()}, CameraHolder will try to hold the {@code
* android.hardware.Camera} instance for a while, so if {@code open()} is
* called soon after, we can avoid the cost of {@code open()} in {@code
* android.hardware.Camera}.
*
* <p>This is used in switching between {@code Camera} and {@code VideoCamera}
* activities.
*/
public abstract class CameraHolder {
protected static final String TAG = "CameraHolder";
protected android.hardware.Camera mCameraDevice;
protected long mKeepBeforeTime = 0; // Keep the Camera before this time.
protected final Handler mHandler;
protected int mUsers = 0; // number of open() - number of release()
protected int mNumberOfCameras;
protected int mCameraId = -1;
// We store the camera parameters when we actually open the device,
// so we can restore them in the subsequent open() requests by the user.
// This prevents the parameters set by the Camera activity used by
// the VideoCamera activity inadvertently.
protected Parameters mParameters;
// Use a singleton.
private static CameraHolder sHolder;
public static synchronized CameraHolder instance() {
if (sHolder == null) {
if (Build.VERSION.SDK_INT >= 0x00000009 /*Build.VERSION_CODES.GINGERBREAD*/) {
if (Build.MODEL.equals("PC36100") || Build.MODEL.equals("HTC Desire S")) { //TODO add HTC Sensation 4G
sHolder = new HTCFrontFacingFixGingerbreadCameraHolder();
} else if (Build.MODEL.equals("SAMSUNG-SGH-I897")) {
sHolder = new SamsungCaptivateGingerbreadCameraHolder();
} else {
sHolder = new GingerbreadCameraHolder();
}
} else {
if (Build.DEVICE.equals("p990") || Build.DEVICE.equals("p999")) {
sHolder = new LGOptimus2XCameraHolder();
} else {
sHolder = new PreGingerbreadCameraHolder();
}
}
}
return sHolder;
}
protected CameraHolder() {
HandlerThread ht = new HandlerThread("CameraHolder");
ht.start();
mHandler = new MyHandler(ht.getLooper());
}
protected static final int RELEASE_CAMERA = 1;
protected class MyHandler extends Handler {
MyHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch(msg.what) {
case RELEASE_CAMERA:
synchronized (CameraHolder.this) {
// In 'CameraHolder.open', the 'RELEASE_CAMERA' message
// will be removed if it is found in the queue. However,
// there is a chance that this message has been handled
// before being removed. So, we need to add a check
// here:
if (CameraHolder.this.mUsers == 0) releaseCamera();
}
break;
}
}
}
public abstract int getNumberOfCameras();
public abstract int getFrontFacingCameraId();
public abstract int getRearFacingCameraId();
public abstract boolean isFrontFacing(int cameraId);
public abstract int getCameraOrientation(int cameraId, int orientationSensorValue);
public abstract android.hardware.Camera open(int cameraId) throws CameraHardwareException;
public abstract double getAspectRatio();
/**
* Tries to open the hardware camera. If the camera is being used or
* unavailable then return {@code null}.
*/
public synchronized android.hardware.Camera tryOpen(int cameraId) {
try {
return mUsers == 0 ? open(cameraId) : null;
} catch (CameraHardwareException e) {
// In eng build, we throw the exception so that test tool
// can detect it and report it
if ("eng".equals(Build.TYPE)) {
throw new RuntimeException(e);
}
return null;
}
}
public synchronized void release() {
Assert(mUsers == 1);
--mUsers;
mCameraDevice.stopPreview();
releaseCamera();
}
protected synchronized void releaseCamera() {
Assert(mUsers == 0);
Assert(mCameraDevice != null);
long now = System.currentTimeMillis();
if (now < mKeepBeforeTime) {
mHandler.sendEmptyMessageDelayed(RELEASE_CAMERA,
mKeepBeforeTime - now);
return;
}
mCameraDevice.release();
mCameraDevice = null;
mCameraId = -1;
}
public synchronized void keep() {
// We allow (mUsers == 0) for the convenience of the calling activity.
// The activity may not have a chance to call open() before the user
// choose the menu item to switch to another activity.
Assert(mUsers == 1 || mUsers == 0);
// Keep the camera instance for 3 seconds.
mKeepBeforeTime = System.currentTimeMillis() + 3000;
}
}