I have a Glass application where I am trying to use a Live Card to start a Camera Preview activity from the menu options. The application seems to freeze when I move away from the Live Card (say, to the weather) and back, and then try to bring up the camera through the menu options. Sorry for all of the code, here are the following files:
Service.java
. . .
#Override
public IBinder onBind(Intent intent) { return mBinder; }
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (mLiveCard == null) {
mLiveCard = new LiveCard(this, LIVE_CARD_TAG);
mRemoteViews = new RemoteViews(getPackageName(), R.layout.service_layout);
mLiveCard.setViews(mRemoteViews);
// Display the options menu when the live card is tapped.
Intent menuIntent = new Intent(this, MenuActivity.class);
menuIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_CLEAR_TASK);
mLiveCard.setAction(PendingIntent.getActivity(this, 0, menuIntent, 0));
mLiveCard.attach(this);
mLiveCard.publish(PublishMode.REVEAL);
} else {
mLiveCard.navigate();
}
return START_STICKY;
}
#Override
public void onDestroy() {
if (mLiveCard != null && mLiveCard.isPublished()) {
mLiveCard.unpublish();
mLiveCard = null;
}
super.onDestroy();
}
}
MenuActivity.java
public class MenuActivity extends Activity {
#Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
// Open the options menu right away.
openOptionsMenu();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.layout, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_stop:
// Stop the service which will unpublish the live card.
stopService(new Intent(MenuActivity.this, Service.class));
return true;
case R.id.action_camera:
startActivity(new Intent(MenuActivity.this, Camera.class));
return true;
default:
return super.onOptionsItemSelected(item);
}
}
#Override
public void onOptionsMenuClosed(Menu menu) { finish(); }
}
Camera.java
public class Camera extends Activity {
private SurfaceView mPreview = null;
private SurfaceHolder mPreviewHolder = null;
private Camera mCamera = null;
private boolean inPreview = false;
private boolean cameraConfigured = false;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.camera);
mPreview = (SurfaceView) findViewById(R.id.camera_preview);
mPreviewHolder = mPreview.getHolder();
mPreviewHolder.addCallback(surfaceCallback);
}
#Override
public void onResume() {
super.onResume();
mCamera = Camera.open();
startPreview();
}
#Override
public void onPause() {
super.onPause();
if (inPreview) {
mCamera.stopPreview();
}
mCamera.release();
mCamera = null;
inPreview = false;
}
private Camera.Size getBestPreviewSize(int width, int height,
Camera.Parameters parameters) {
Camera.Size result = null;
for (Camera.Size size : parameters.getSupportedPreviewSizes()) {
if (size.width <= width && size.height <= height) {
if (result == null) {
result = size;
} else {
int resultArea = result.width * result.height;
int newArea = size.width * size.height;
if (newArea > resultArea) {
result = size;
}
}
}
}
return (result);
}
private void initPreview(int width, int height) {
if (mCamera != null && mPreviewHolder.getSurface() != null) {
try {
mCamera.setPreviewDisplay(mPreviewHolder);
} catch (Throwable t) {
Log.e("PreviewDemo-surfaceCallback",
"Exception in setPreviewDisplay()", t);
}
if (!cameraConfigured) {
Camera.Parameters parameters = mCamera.getParameters();
Camera.Size size = getBestPreviewSize(width, height,
parameters);
if (size != null) {
parameters.setPreviewSize(size.width, size.height);
mCamera.setParameters(parameters);
cameraConfigured = true;
}
}
}
}
private void startPreview() {
if (cameraConfigured && mCamera != null) {
mCamera.startPreview();
inPreview = true;
}
}
I can't seem to figure out why the camera preview works initially but doesn't work when navigating away. I feel like there is something simple that I am just missing here. Any help would be greatly appreciated!
I'm not sure if this would fix your problem, but I would start and stop the camera this way:
private Camera getCameraInstance() {
Camera mCamera = null;
try {
/* attempt to get a Camera instance */
mCamera = Camera.open();
} catch (Exception e) {
/* Camera is not available (in use or does not exist) */
Log.e(TAG, "Error getting camera: " + e.getMessage());
// process null error here
// finish();
}
/* returns null if camera is unavailable */
return mCamera;
}
#Override
public void onResume() {
super.onResume();
if (mCamera == null) {
mCamera = getCameraInstance();
if (mCamera != null) {
startPreview();
} else {
// process null error here
}
}
}
#Override
public void onPause() {
super.onPause();
if (mCamera != null) {
/* stop preview */
if (inPreview) {
try {
mCamera.stopPreview();
} catch (Exception e) {
/* ignore: tried to stop a non-existent preview */
}
}
/* release the camera */
mCamera.release();
mCamera = null;
inPreview = false;
}
}
I would also check if mCamera or getSurface is null in initPreview.
private void initPreview(int width, int height) {
if (mCamera != null && mPreviewHolder.getSurface() != null) {
try {
mCamera.setPreviewDisplay(mPreviewHolder);
} catch (Throwable t) {
Log.e("PreviewDemo-surfaceCallback",
"Exception in setPreviewDisplay()", t);
}
if (!cameraConfigured) {
Camera.Parameters parameters = mCamera.getParameters();
Camera.Size size = getBestPreviewSize(width, height,
parameters);
if (size != null) {
parameters.setPreviewSize(size.width, size.height);
mCamera.setParameters(parameters);
cameraConfigured = true;
}
}
} else {
Log.e(TAG, "Camera or getSurface is null");
}
}
Related
I'm making a very basic camera app using Google's camera API.
Initially launching the app is fine, but upon leaving and reentering, it crashes. I have found similar answers but I couldn't apply them to my specific code/situation. I have also tried messing around with onPause(), onResume, etc. but to no avail. Why is this happening and how can I fix this?
Below are the relevant methods and error log.
SurfaceView class:
public class ImageSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
private Camera camera;
private SurfaceHolder surfaceHolder;
private String TAG = ImageSurfaceView.class.getSimpleName();
public ImageSurfaceView(Context context, Camera camera) {
super(context);
this.camera = camera;
surfaceHolder = getHolder();
surfaceHolder.addCallback(this);
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
try {
this.camera.setPreviewDisplay(holder);
this.camera.startPreview();
this.camera.setDisplayOrientation(90);
} catch (IOException e) {
Log.d(TAG, "Error setting camera preview: " + e.getMessage());
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if (holder.getSurface() == null){
// preview surface does not exist
return;
}
try {
camera.stopPreview();
} catch (Exception e){
// ignore: tried to stop a non-existent preview
}
try {
Camera.Parameters params = camera.getParameters();
List<Camera.Size> supportedPreviewSizes = params.getSupportedPreviewSizes();
Camera.Size camPreviewSize = getOptimalPreviewSize(supportedPreviewSizes, width , height);
params.setPreviewSize(camPreviewSize.width ,camPreviewSize.height);
camera.setParameters(params);
} catch (RuntimeException e) {
Log.d(TAG, "Error getting camera parameters: " + e.getMessage());
}
try {
camera.setPreviewDisplay(surfaceHolder);
camera.startPreview();
} catch (Exception e){
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
}
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
camera.release();
camera = null;
}
private void stopPreviewAndFreeCamera() {
if (camera != null) {
// Call stopPreview() to stop updating the preview surface.
camera.stopPreview();
// Important: Call release() to release the camera for use by other
// applications. Applications should release the camera immediately
// during onPause() and re-open() it during onResume()).
camera.release();
camera = null;
}
}
private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {
final double ASPECT_TOLERANCE = 0.1;
double targetRatio=(double)h / w;
if (sizes == null) return null;
Camera.Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
for (Camera.Size size : sizes) {
double ratio = (double) size.width / size.height;
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Camera.Size size : sizes) {
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}
return optimalSize;
}
}
onPause/onResume
protected void onPause() {
Log.d(TAG, " -> onPause");
if(camera != null){
releaseCamera();
}
super.onPause();
Log.d(TAG, " <- onPause");
}
private void releaseCamera(){
if (camera != null){
camera.release();
camera = null;
}
}
#Override
public void onResume() {
super.onResume();
Log.d(TAG, " -> OnResume");
if (camera == null) {
camera = checkDeviceCamera();
}
Log.d(TAG, " <- OnResume");
}
Camera methods:
private Camera checkDeviceCamera(){
Camera mCamera = null;
try {
mCamera = Camera.open();
} catch (Exception e) {
e.printStackTrace();
}
return mCamera;
}
private void startCamera() {
camera = checkDeviceCamera();
mImageSurfaceView = new ImageSurfaceView(MainActivity.this, camera);
cameraPreviewLayout = (FrameLayout) findViewById(R.id.camera_preview); //the framelayout
cameraPreviewLayout.addView(mImageSurfaceView); //adding surfaceview to framelayout
ImageButton captureButton = (ImageButton)findViewById(R.id.imageButton);
captureButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
camera.takePicture(null, null, pictureCallback);
}
});
}
Camera.PictureCallback pictureCallback = new Camera.PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera cam) {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
if (camera != null) {
camera.startPreview();
}
}
}, 0);
}
};
Error:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: daniel816.com.danger, PID: 10594
java.lang.RuntimeException: Camera is being used after Camera.release() was called
at android.hardware.Camera.setPreviewSurface(Native Method)
at android.hardware.Camera.setPreviewDisplay(Camera.java:738)
at danielwei816.com.danger.ImageSurfaceView.surfaceCreated(ImageSurfaceView.java:33)
at android.view.SurfaceView.updateWindow(SurfaceView.java:634)
at android.view.SurfaceView.onWindowVisibilityChanged(SurfaceView.java:256)
at android.view.View.dispatchWindowVisibilityChanged(View.java:10293)
at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:1289)
at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:1289)
at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:1289)
at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:1289)
at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:1289)
at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:1289)
at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:1289)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1599)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1299)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6558)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:871)
at android.view.Choreographer.doCallbacks(Choreographer.java:683)
at android.view.Choreographer.doFrame(Choreographer.java:619)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:857)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6316)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:872)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:762)
You have lifecycle of your activity (onResume, onPause etc.) and lifecycle of your SurfaceView (surfaceChanged, surfaceDestroyed etc.).
You release your camera in onPause, but you stop it in surfaceDestroyed (which is called later). I think this is the problem.
I have an application that uses a barcode scanner to pass data to the next activity. I have managed to pass the data over to the next activity but when I return to the barcode scanner the app crashes. Im presuming it is because it is trying to open the Surfaceview again.
How would I continue with the surfaceview when going back to the barcode scanner
****Barcode Scanner ****
public class BarcodeScannerNew extends AppCompatActivity {
private Camera mCamera;
private CameraPreview mPreview;
private Handler autoFocusHandler;
private Button scanButton;
private ImageScanner scanner;
private boolean barcodeScanned = false;
private boolean previewing = true;
static {
System.loadLibrary("iconv");
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.new_barcode_window);
initControls();
}
private void initControls() {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
autoFocusHandler = new Handler();
mCamera = getCameraInstance();
// Instance barcode scanner
scanner = new ImageScanner();
scanner.setConfig(0, Config.X_DENSITY, 10);
scanner.setConfig(0, Config.Y_DENSITY, 10);
mPreview = new CameraPreview(BarcodeScannerNew.this, mCamera, previewCb,
autoFocusCB);
FrameLayout preview = (FrameLayout) findViewById(R.id.cameraPreview);
preview.addView(mPreview);
scanButton = (Button) findViewById(R.id.ScanButton);
scanButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
if (barcodeScanned) {
barcodeScanned = false;
mCamera.setPreviewCallback(previewCb);
mCamera.startPreview();
previewing = true;
mCamera.autoFocus(autoFocusCB);
}
}
});
}
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
releaseCamera();
}
return super.onKeyDown(keyCode, event);
}
/**
* A safe way to get an instance of the Camera object.
*/
public static Camera getCameraInstance() {
Camera c = null;
try {
c = Camera.open();
} catch (Exception e) {
}
return c;
}
private void releaseCamera() {
if (mCamera != null) {
previewing = false;
mCamera.setPreviewCallback(null);
mCamera.release();
mCamera = null;
}
}
private Runnable doAutoFocus = new Runnable() {
public void run() {
if (previewing)
mCamera.autoFocus(autoFocusCB);
}
};
Camera.PreviewCallback previewCb = new Camera.PreviewCallback() {
public void onPreviewFrame(byte[] data, Camera camera) {
Camera.Parameters parameters = camera.getParameters();
Camera.Size size = parameters.getPreviewSize();
Image barcode = new Image(size.width, size.height, "Y800");
barcode.setData(data);
int result = scanner.scanImage(barcode);
if (result != 0) {
previewing = false;
mCamera.stopPreview();
mCamera.setPreviewCallback(null);
TextView labelScanResult = (TextView)findViewById(R.id.labelScanResult);
SymbolSet syms = scanner.getResults();
for (Symbol sym : syms) {
Log.i("<<<<<<Asset Code>>>>> ",
"<<<<Bar Code>>> " + sym.getData());
String scanResult = sym.getData().trim();
// shows dialog showAlertDialog(scanResult);
labelScanResult.setText(scanResult);
/* Toast.makeText(BarcodeScanner.this, scanResult,
Toast.LENGTH_SHORT).show();*/
Intent i = new Intent(BarcodeScannerNew.this, BarcodeScanner.class);
i.putExtra("" , labelScanResult.getText().toString());
startActivity(i);
finish();
barcodeScanned = true;
break;
}
}
}
};
// Mimic continuous auto-focusing
Camera.AutoFocusCallback autoFocusCB = new Camera.AutoFocusCallback() {
public void onAutoFocus(boolean success, Camera camera) {
autoFocusHandler.postDelayed(doAutoFocus, 1000);
}
};
private void showAlertDialog(String message) {
new AlertDialog.Builder(this)
.setCancelable(false)
.setMessage(message)
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
}
})
.show();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_home, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
if (id == R.id.homebutton){
startActivity(new Intent(this,MainActivity.class));
}
return super.onOptionsItemSelected(item);
}
}
Camera activity
package com.example.dsouchon.myapplication;
import android.content.Context;
import android.hardware.Camera;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import java.io.IOException;
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private Camera mCamera;
private Camera.PreviewCallback previewCallback;
private Camera.AutoFocusCallback autoFocusCallback;
public CameraPreview(Context context, Camera camera,
Camera.PreviewCallback previewCb,
Camera.AutoFocusCallback autoFocusCb) {
super(context);
mCamera = camera;
previewCallback = previewCb;
autoFocusCallback = autoFocusCb;
/*
* Set camera to continuous focus if supported, otherwise use
* software auto-focus. Only works for API level >=9.
*/
/*
Camera.Parameters parameters = camera.getParameters();
for (String f : parameters.getSupportedFocusModes()) {
if (f == Parameters.FOCUS_MODE_CONTINUOUS_PICTURE) {
mCamera.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
autoFocusCallback = null;
break;
}
}
*/
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = getHolder();
mHolder.addCallback(this);
// deprecated setting, but required on Android versions prior to 3.0
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
try {
mCamera.setPreviewDisplay(holder);
}
catch (IOException e) {
Log.d("DBG", "Error setting camera preview: " + e.getMessage());
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
/*
* If your preview can change or rotate, take care of those events here.
* Make sure to stop the preview before resizing or reformatting it.
*/
if (mHolder.getSurface() == null) {
// preview surface does not exist
return;
}
// stop preview before making changes
try {
mCamera.stopPreview();
} catch (Exception e) {
// ignore: tried to stop a non-existent preview
}
try {
// Hard code camera surface rotation 90 degs to match Activity view in portrait
mCamera.setDisplayOrientation(90);
mCamera.setPreviewDisplay(mHolder);
mCamera.setPreviewCallback(previewCallback);
mCamera.startPreview();
mCamera.autoFocus(autoFocusCallback);
} catch (Exception e) {
Log.d("DBG", "Error starting camera preview: " + e.getMessage());
}
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
}
Logs
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.hardware.Camera.setPreviewDisplay(android.view.SurfaceHolder)' on a null object reference
at com.example.dsouchon.myapplication.CameraPreview.surfaceCreated(CameraPreview.java:56)
at android.view.SurfaceView.updateWindow(SurfaceView.java:607)
at android.view.SurfaceView$3.onPreDraw(SurfaceView.java:184)
at android.view.ViewTreeObserver.dispatchOnPreDraw(ViewTreeObserver.java:921)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2164)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1191)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6642)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:777)
at android.view.Choreographer.doCallbacks(Choreographer.java:590)
at android.view.Choreographer.doFrame(Choreographer.java:560)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:763)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:145)
at android.app.ActivityThread.main(ActivityThread.java:5951)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1400)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1195)
I know this question has been asked many times but i have no other option than asking this question again. I am using camera2 api to display both front and rear cameras on the same screen. I have created two texture views each with two camera instances. Even then I'm getting an exception.
This is my code for the MainActivity.java:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "AndroidCameraApi";
private TextureView textureView,textureView1;
private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
static {
ORIENTATIONS.append(Surface.ROTATION_0, 90);
ORIENTATIONS.append(Surface.ROTATION_90, 0);
ORIENTATIONS.append(Surface.ROTATION_180, 270);
ORIENTATIONS.append(Surface.ROTATION_270, 180);
}
private String cameraId,cameraId1;
protected CameraDevice cameraDevice,cameraDevice1;
protected CameraCaptureSession cameraCaptureSessions,cameraCaptureSessions1;
protected CaptureRequest.Builder captureRequestBuilder,captureRequestBuilder1;
private Size imageDimension,imageDimension1;
private static final int REQUEST_CAMERA_PERMISSION = 200;
private Handler mBackgroundHandler,mBackgroundHandler1;
private HandlerThread mBackgroundThread,mBackgroundThread1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_android_camera_api);
textureView = (TextureView) findViewById(R.id.texture);
textureView1 = (TextureView) findViewById(texture1);
assert textureView != null;
assert textureView1 != null;
textureView.setSurfaceTextureListener(textureListener);
textureView1.setSurfaceTextureListener(textureListener1);
}
TextureView.SurfaceTextureListener textureListener = new TextureView.SurfaceTextureListener() {
#Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
//open your camera here
openCamera();
}
#Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
// Transform you image captured size according to the surface width and height
}
#Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
return false;
}
#Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
};
TextureView.SurfaceTextureListener textureListener1 = new TextureView.SurfaceTextureListener() {
#Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
//open your camera here
openBackCamera();
}
#Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
// Transform you image captured size according to the surface width and height
}
#Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
return false;
}
#Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
};
private final CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {
#Override
public void onOpened(CameraDevice camera) {
//This is called when the camera is open
Log.e(TAG, "onOpened");
cameraDevice = camera;
createCameraPreview();
}
#Override
public void onDisconnected(CameraDevice camera) {
cameraDevice.close();
}
#Override
public void onError(CameraDevice camera, int error) {
cameraDevice.close();
cameraDevice = null;
}
};
private final CameraDevice.StateCallback stateCallback1 = new CameraDevice.StateCallback() {
#Override
public void onOpened(CameraDevice camera) {
//This is called when the camera is open
Log.e(TAG, "onOpened");
cameraDevice1 = camera;
createCameraPreview1();
}
#Override
public void onDisconnected(CameraDevice camera) {
cameraDevice1.close();
}
#Override
public void onError(CameraDevice camera, int error) {
cameraDevice1.close();
cameraDevice1 = null;
}
};
protected void startBackgroundThread() {
mBackgroundThread = new HandlerThread("Camera Background");
mBackgroundThread.start();
mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
}
protected void stopBackgroundThread() {
mBackgroundThread.quitSafely();
try {
mBackgroundThread.join();
mBackgroundThread = null;
mBackgroundHandler = null;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
protected void startBackgroundThread1() {
mBackgroundThread1 = new HandlerThread("Camera Background");
mBackgroundThread1.start();
mBackgroundHandler1 = new Handler(mBackgroundThread1.getLooper());
}
protected void stopBackgroundThread1() {
mBackgroundThread1.quitSafely();
try {
mBackgroundThread1.join();
mBackgroundThread1 = null;
mBackgroundHandler1 = null;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
protected void createCameraPreview() {
try {
SurfaceTexture texture = textureView.getSurfaceTexture();
assert texture != null;
texture.setDefaultBufferSize(imageDimension.getWidth(), imageDimension.getHeight());
Surface surface = new Surface(texture);
captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
captureRequestBuilder.addTarget(surface);
cameraDevice.createCaptureSession(Arrays.asList(surface), new CameraCaptureSession.StateCallback(){
#Override
public void onConfigured(#NonNull CameraCaptureSession cameraCaptureSession) {
//The camera is already closed
if (null == cameraDevice) {
return;
}
// When the session is ready, we start displaying the preview.
cameraCaptureSessions = cameraCaptureSession;
updatePreview();
}
#Override
public void onConfigureFailed(#NonNull CameraCaptureSession cameraCaptureSession) {
Toast.makeText(MainActivity.this, "Configuration change", Toast.LENGTH_SHORT).show();
}
}, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
protected void createCameraPreview1() {
try {
SurfaceTexture texture1 = textureView1.getSurfaceTexture();
assert texture1 != null;
texture1.setDefaultBufferSize(imageDimension1.getWidth(), imageDimension1.getHeight());
Surface surface = new Surface(texture1);
captureRequestBuilder1 = cameraDevice1.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
captureRequestBuilder1.addTarget(surface);
cameraDevice1.createCaptureSession(Arrays.asList(surface), new CameraCaptureSession.StateCallback(){
#Override
public void onConfigured(#NonNull CameraCaptureSession cameraCaptureSession) {
//The camera is already closed
if (null == cameraDevice1) {
return;
}
// When the session is ready, we start displaying the preview.
cameraCaptureSessions1 = cameraCaptureSession;
updatePreview1();
}
#Override
public void onConfigureFailed(#NonNull CameraCaptureSession cameraCaptureSession) {
Toast.makeText(MainActivity.this, "Configuration change", Toast.LENGTH_SHORT).show();
}
}, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private void openCamera() {
CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
Log.e(TAG, "is camera open");
try {
cameraId = manager.getCameraIdList()[0];
CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
assert map != null;
imageDimension = map.getOutputSizes(SurfaceTexture.class)[0];
// Add permission for camera and let user grant the permission
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CAMERA_PERMISSION);
return;
}
manager.openCamera(cameraId, stateCallback, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
Log.e(TAG, "openCamera X");
}
private void openBackCamera() {
CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
Log.e(TAG, "is camera open");
try {
cameraId1 = manager.getCameraIdList()[1];
CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId1);
StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
assert map != null;
imageDimension1 = map.getOutputSizes(SurfaceTexture.class)[0];
// Add permission for camera and let user grant the permission
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CAMERA_PERMISSION);
return;
}
manager.openCamera(cameraId1, stateCallback1, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
Log.e(TAG, "openCamera X");
}
protected void updatePreview() {
if(null == cameraDevice) {
Log.e(TAG, "updatePreview error, return");
}
captureRequestBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
try {
cameraCaptureSessions.setRepeatingRequest(captureRequestBuilder.build(), null, mBackgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
protected void updatePreview1() {
if(null == cameraDevice1) {
Log.e(TAG, "updatePreview error, return");
}
captureRequestBuilder1.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
try {
cameraCaptureSessions1.setRepeatingRequest(captureRequestBuilder1.build(), null, mBackgroundHandler1);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
if (requestCode == REQUEST_CAMERA_PERMISSION) {
if (grantResults[0] == PackageManager.PERMISSION_DENIED) {
// close the app
Toast.makeText(MainActivity.this, "Sorry!!!, you can't use this app without granting permission", Toast.LENGTH_LONG).show();
finish();
}
}
}
#Override
protected void onResume() {
super.onResume();
Log.e(TAG, "onResume");
startBackgroundThread();
startBackgroundThread1();
if (textureView.isAvailable()) {
openCamera();
} else {
textureView.setSurfaceTextureListener(textureListener);
}
if (textureView1.isAvailable()) {
openBackCamera();
} else {
textureView1.setSurfaceTextureListener(textureListener1);
}
}
#Override
protected void onPause() {
Log.e(TAG, "onPause");
//closeCamera();
stopBackgroundThread();
stopBackgroundThread1();
super.onPause();
}
}
Code for layout file:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<TextureView
android:id="#+id/texture"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_marginBottom="10dp"/>
<TextureView
android:id="#+id/texture1"
android:layout_width="match_parent"
android:layout_height="200dp"
/>
</LinearLayout>
Exception i am getting is as below:
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.hardware.camera2.CameraDevice.close()' on a null object reference
I am getting exception on this line:
cameraDevice1.close();
Which of the cameraDevice1.close() calls fails? Did you ever get an onOpened call for that camera?
There's no guarantee that a given Android device can have multiple cameras open at once; the only way to know is to try, and if it can't be done, the second camera will not fire onOpened, it'll fire onError with error code TOO_MANY_CAMERAS_IN_USE.
So if that's happening here, then you'll get onError without onOpened, and your cameraDevice1 will never have been set.
I need to show camera preview in live wallpaper.
I've already made for android < M versions. But can't understand how it working with new API. Oldest Camera is deprecated now
In google example they put it in xml and in TextureView but how I can change that for my needs, I can't understand.
Anyway Thanks!
Google example
https://github.com/googlesamples/android-Camera2Basic
Below my code.
Start wallpaper
Intent intent = new Intent(WallpaperManager.ACTION_CHANGE_LIVE_WALLPAPER);
intent.putExtra(WallpaperManager.EXTRA_LIVE_WALLPAPER_COMPONENT, new ComponentName(this, TransparentWallpaperService.class));
startActivity(intent);
TransparentWallpaperService
public class TransparentWallpaperService extends WallpaperService {
#Override
public Engine onCreateEngine() {
return new GlowEngine();
}
private class GlowEngine extends Engine {
private final Handler handler = new Handler();
private final Runnable viewRunner = new Runnable() {
#Override
public void run() {
drawView();
}
};
private boolean visible = true;
private CameraView view;
public GlowEngine() {
super();
view = new CameraView(getBaseContext(), getSurfaceHolder());
handler.post(viewRunner);
}
#Override
public void onVisibilityChanged(boolean visible) {
this.visible = visible;
if (visible) {
handler.post(viewRunner);
}
else {
handler.removeCallbacks(viewRunner);
}
}
#Override
public void onSurfaceDestroyed(SurfaceHolder holder) {
super.onSurfaceDestroyed(holder);
this.visible = false;
handler.removeCallbacks(viewRunner);
}
private void drawView() {
view.surfaceChanged(getSurfaceHolder(), OPAQUE, view.getWidth(), view.getHeight());
handler.removeCallbacks(viewRunner);
if (visible) {
handler.postDelayed(viewRunner, 4000);
}
}
}
}
CameraView class
public class CameraView extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder holder;
private Camera camera;
public CameraView(Context context) {
super(context);
camera = getCameraInstance();
}
public CameraView(Context context, SurfaceHolder holder) {
this(context);
this.holder = holder;
holder.addCallback(this);
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) {
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
}
#Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
if(camera == null) {
camera = getCameraInstance();
}
if(camera != null) {
// The Surface has been created, now tell the camera where to draw the preview.
try {
camera.setPreviewDisplay(surfaceHolder);
camera.startPreview();
}
catch (IOException e) {
Log.e("CameraView", "Error setting camera preview: " + e.getMessage());
}
}
}
#Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {
boolean portrait = true;
if (width > 0 && height >0 && width < height){
portrait =true;
} else if (width > 0 && height >0 && width > height){
portrait = false;
}
Camera.Parameters parameters;
if (camera == null) {
camera = getCameraInstance();
}
if (camera != null){
parameters = camera.getParameters();
Camera.Size size = parameters.getPictureSize();
size = parameters.getPreviewSize();
parameters.setPreviewSize(size.width, size.height);
if (portrait) {
camera.setDisplayOrientation(90);
} else {
camera.setDisplayOrientation(180);
}
try {
camera.setParameters(parameters);
camera.setPreviewDisplay(holder);
camera.startPreview();
} catch (IOException e) {
e.printStackTrace();
}
}
}
#Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
if(camera != null) {
try {
camera.stopPreview();
camera.release();
}
catch (Exception e) {
Log.e("CameraView", "Error stopping camera preview: " + e.getMessage());
}
}
}
private Camera getCameraInstance() {
Context context = getContext();
Camera camera = null;
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
try {
camera = Camera.open();
}
catch (Exception e) {
Log.e("CameraView", "Error getting camera instance: " + e.getMessage());
}
}
else {
Log.i("CameraView", "No camera found!");
}
return camera;
}
}
You can just keep using the old API; it's fully functional even on new devices.
Otherwise, you can just replace the TextureView with a SurfaceView easily enough; instead of creating a Surface from TextureView's SurfaceTexture, get a Surface from SurfaceView's SurfaceHolder in surfaceChanged.
So my Manifest.xml is correct:
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus"/>
<application>
...
I also release the camera in the onPause and onDestroy functions, using this function:
private void releaseCamera() {
// stop and release camera
if (mCamera != null) {
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
//mPreview instance of CameraPreview (referenced below)
if (mPreview != null) {
mPreview.setCamera(null);
}
}
I call mCamera = Camera.open(); only once, during on onResume.
Even though I do everything exactly as specified by every example I have found on Stack Overflow and other sources, Camera.open() would occasionally fail.
Some use cases:
Open Camera Activity with WiFi enabled (Camera Works)
Open Camera Activity with no network connection (Camera Fails) //Bizarre
On catching RuntimeException, then calling mCamera.takePicture(), activity crashes and reloads. (Camera Works)
So I am completely stumped... Any ideas would be greatly appreciated :D
As requested, find the relevant code below:
CameraPreiview
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private Camera mCamera;
public CameraPreview(Context context, Camera camera) {
super(context);
mCamera = camera;
mHolder = getHolder();
mHolder.addCallback(this);
// deprecated setting, but required on Android versions prior to 3.0
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void surfaceCreated(SurfaceHolder holder) {
try {
// create the surface and start camera preview
if (mCamera != null) {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
}
} catch (IOException e) {
Log.d(VIEW_LOG_TAG, "Error setting camera preview: " + e.getMessage());
}
}
public void refreshCamera(Camera camera) {
if (mHolder.getSurface() == null) {
// preview surface does not exist
return;
}
// stop preview before making changes
try {
mCamera.stopPreview();
} catch (Exception e) {
// ignore: tried to stop a non-existent preview
}
// set preview size and make any resize, rotate or
// reformatting changes here
// start preview with new settings
setCamera(camera);
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (Exception e) {
Log.d(VIEW_LOG_TAG, "Error starting camera preview: " + e.getMessage());
}
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// If your preview can change or rotate, take care of those events here.
// Make sure to stop the preview before resizing or reformatting it.
refreshCamera(mCamera);
}
public void setCamera(Camera camera) {
//method to set a camera instance
mCamera = camera;
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
if (mCamera != null) {
mCamera.stopPreview();
mCamera.release();
}
}
}
SampleActivity
public class CameraActivity extends Activity {
private static final String TAG = "CameraActivity";
private Camera mCamera;
private CameraPreview mPreview;
private Camera.PictureCallback mPicture;
private ImageButton capture;
private Context myContext;
private LinearLayout cameraPreview;
// Required to remove the camera if onPause did not
#Override
public void onDestroy() {
releaseCamera();
super.onDestroy();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera_test);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
myContext = this;
initialize();
}
//Code to find the id for the traditional back facing camera
private int findBackFacingCamera() {
int cameraId = -1;
//Search for the back facing camera
//get the number of cameras
int numberOfCameras = Camera.getNumberOfCameras();
//for every camera check
for (int i = 0; i < numberOfCameras; i++) {
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(i, info);
if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
cameraId = i;
cameraFront = false;
break;
}
}
return cameraId;
}
#Override
public void onResume() {
super.onResume();
//If there is no camera on the device, inform the user.
if (!hasCamera(myContext)) {
Toast toast = Toast.makeText(myContext, "Sorry, your phone does not have a camera!", Toast.LENGTH_LONG);
toast.show();
}
//release the camera if it's in use.
releaseCamera();
if (mCamera == null) {
//if the back facing camera does not exist
if (findBackFacingCamera() < 0) {
Toast.makeText(this, "No back facing camera found.", Toast.LENGTH_LONG).show();
} else {
try {
//Open the back facing camera, note that Camera.open() gives the same error.
mCamera = Camera.open(findBackFacingCamera());
mCamera.setDisplayOrientation(90); //Show in Portrait
mPicture = getPictureCallback();
mPreview.refreshCamera(mCamera);
} catch (RuntimeException e) {
e.printStackTrace();
Toast.makeText(this, "Could not connect to camera.", Toast.LENGTH_LONG).show();
}
}
}
}
public void initialize() {
cameraPreview = (LinearLayout) findViewById(R.id.camera_preview);
mPreview = new CameraPreview(myContext, mCamera);
cameraPreview.addView(mPreview);
capture = (ImageButton) findViewById(R.id.button_capture);
capture.setOnClickListener(captureListener);
}
#Override
protected void onPause() {
super.onPause();
//when on Pause, release camera in order to be used from other applications
releaseCamera();
}
private void captureComplete() {
//...
}
private Camera.PictureCallback getPictureCallback() {
Camera.PictureCallback picture = new Camera.PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
//make a new picture file
mFile = ImageUtility.getOutputMediaFile(ImageUtility.MEDIA_TYPE_IMAGE);
Log.i(TAG, (mFile == null) + ": ");
if (mFile == null) {
return;
}
mPreview.refreshCamera(mCamera);
mBackgroundHandler.post(new ImageUtility.ImageSaver(getApplicationContext(), data, mFile, new Callable() {
#Override
public Object call() throws Exception {
captureComplete();
return null;
}
}));
}
};
return picture;
}
View.OnClickListener captureListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
if (mCamera != null) {
mCamera.autoFocus(new Camera.AutoFocusCallback() {
#Override
public void onAutoFocus(boolean success, Camera camera) {
mPicture = getPictureCallback();
mPreview.refreshCamera(camera);
camera.takePicture(null, null, mPicture);
}
});
} else {
throw new NullPointerException();
}
}
};
private void releaseCamera() {
// stop and release camera
if (mCamera != null) {
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
if (mPreview != null) {
mPreview.setCamera(null);
}
}
}
Some changes you can do, since i had also been working on Camera api since 3 months..
In your CameraPreview Constructor, add this line
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) // add this
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
In your surfaceDestroyed() method in CameraPreview you should remove the callback right after releasing camera resources:
this.getHolder().removeCallback(this);
Besides changing the above I couldn't figure much out of your CameraActivity since you've used AutoFocus and other features.... I used FrameLayout as the container to run the preview in.