I have a custom camera app , that previews the camera video output on SurfaceView and attempts to take pictures , the pics should be will be processed by “xzing “scanner API to decode any barcodes in the image.
My app previews correctly and does not throw any errors or expectations, however my onPictureTaken(byte[] imgData, Camera camera) method & PictureCallback are never called, therefore I’m unable to get the image & continue with further scanning.
Below is the actual code for activity that takes care of Camera logic. The onPictureTaken(byte[] imgData, Camera camera) method & PictureCallback are in this class(ScanVinFromBarcodeActivity.java) see blow :
package com.ty.tyownerspoc.barcode;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.ChecksumException;
import com.google.zxing.FormatException;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.NotFoundException;
import com.google.zxing.RGBLuminanceSource;
import com.google.zxing.Reader;
import com.google.zxing.Result;
import com.google.zxing.common.HybridBinarizer;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.Point;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.PictureCallback;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.Display;
import android.view.SurfaceView;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.ty.tyownerspoc.R;
public class ScanVinFromBarcodeActivity extends Activity {
private Camera globalCamera;
private int cameraId = 0;
private TextView VINtext = null;
private View scanButton = null;
// bitmap from camera
private Bitmap bmpOfTheImageFromCamera = null;
// global flag whether a camera has been detected
private boolean isThereACamera = false;
// surfaceView for preview object
private FrameLayout frameLayoutBarcodeScanner = null;
private CameraPreview newCameraPreview = null;
private SurfaceView surfaceViewBarcodeScanner = null;
private int counter = 0;
private volatile boolean finishedPictureTask = false;
/*
* This method , finds FEATURE_CAMERA, opens the camera, set parameters ,
* add CameraPreview to layout, set camera surface holder, start preview
*/
private void initializeGlobalCamera() {
try {
if (!getPackageManager().hasSystemFeature(
PackageManager.FEATURE_CAMERA)) {
Toast.makeText(this, "No camera on this device",
Toast.LENGTH_LONG).show();
} else { // check for front camera ,and get the ID
cameraId = findFrontFacingCamera();
if (cameraId < 0) {
Toast.makeText(this, "No front facing camera found.",
Toast.LENGTH_LONG).show();
} else {
Log.d("ClassScanViewBarcodeActivity",
"camera was found , ID: " + cameraId);
// camera was found , set global camera flag to true
isThereACamera = true;
// OPEN
globalCamera = Camera.open(cameraId);
// pass surfaceView to CameraPreview
newCameraPreview = new CameraPreview(this, globalCamera);
// pass CameraPreview to Layout
frameLayoutBarcodeScanner.addView(newCameraPreview);
try {
globalCamera
.setPreviewDisplay(surfaceViewBarcodeScanner
.getHolder());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// PREVIEW
globalCamera.startPreview();
Log.d("ClassScanViewBarcodeActivity",
"camera opened & previewing");
}
}// end else ,check for front camera
}// end try
catch (Exception exc) {
// in case of exception release resources & cleanup
if (globalCamera != null) {
globalCamera.stopPreview();
globalCamera.setPreviewCallback(null);
globalCamera.release();
globalCamera = null;
}
Log.d("ClassScanViewBarcodeActivity initializeGlobalCamera() exception:",
exc.getMessage());
}// end catch
}
// onCreate, instantiates layouts & surfaceView used for video preview
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_barcode_vin_scanner);
Log.d("ClassScanViewBarcodeActivity", "onCreate ");
// create surfaceView for previewing of camera image
frameLayoutBarcodeScanner = (FrameLayout) findViewById(R.id.FrameLayoutForPreview);
surfaceViewBarcodeScanner = (SurfaceView) findViewById(R.id.surfaceViewBarcodeScanner);
initializeGlobalCamera();
// create text area & scan button
VINtext = (TextView) findViewById(R.id.mytext);
scanButton = findViewById(R.id.webbutton);
// on click listener, onClick take a picture
scanButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
try {
// if true take a picture
if (isThereACamera) {
Log.d("ClassScanViewBarcodeActivity",
"setOnClickListener() isThereACamera: "+ isThereACamera);
//set picture format to JPEG, everytime makesure JPEg callback is called
Parameters parameters = globalCamera.getParameters();
parameters.setPictureFormat(ImageFormat.JPEG);
globalCamera.setParameters(parameters);
//take pic , should call Callback
globalCamera.takePicture(null, null, jpegCallback);
// wait 1 sec , than start preview again
Thread.sleep(1000);
//STOP
globalCamera.stopPreview();
//start previewing again onthe SurfaceView in case use wants to take another pic/scan
globalCamera.startPreview();
}
}// end try
catch (Exception exc) {
// in case of exception release resources & cleanup
if (globalCamera != null) {
globalCamera.stopPreview();
globalCamera.setPreviewCallback(null);
globalCamera.release();
globalCamera = null;
}
Log.d("ClassScanViewBarcodeActivity setOnClickListener() exceprtion:",
exc.getMessage());
}// end catch
}// end on Click
});// end OnClickListener() implementation
}// end onCreate
#Override
protected void onResume() {
Log.d("ClassScanViewBarcodeActivity, onResume() globalCamera:",
String.valueOf(globalCamera));
if (globalCamera != null) {
// START PREVIEW
globalCamera.startPreview();
} else {
initializeGlobalCamera();
}
super.onResume();
}
#Override
protected void onStop() {
if (globalCamera != null) {
globalCamera.stopPreview();
globalCamera.setPreviewCallback(null);
globalCamera.release();
globalCamera = null;
}
super.onStop();
}
//callback used by takePicture()
PictureCallback jpegCallback = new PictureCallback() {
public void onPictureTaken(byte[] imgData, Camera camera) {
BinaryBitmap bitmap = null;
try {
Log.d("ClassScanViewBarcodeActivity" ,"onPictureTaken()");
//save image to sd card
savePicture(imgData);
// get the bitmap from camera imageData
bmpOfTheImageFromCamera = BitmapFactory.decodeByteArray(
imgData, 0, imgData.length);
if (bmpOfTheImageFromCamera != null) {
//Galaxy S3 , incorrect rotation issue rotate to correct rotation
Matrix matrix = new Matrix();
matrix.postRotate(90);
Bitmap rotatedBitmap = Bitmap.createBitmap(bmpOfTheImageFromCamera, 0, 0, bmpOfTheImageFromCamera.getWidth(), bmpOfTheImageFromCamera.getHeight(), matrix, true);
// convert bitmap to binary bitmap
bitmap = cameraBytesToBinaryBitmap(rotatedBitmap);
if (bitmap != null) {
// decode the VIN
String VIN = decodeBitmapToString(bitmap);
Log.d("***ClassScanViewBarcodeActivity ,onPictureTaken(): VIN ",
VIN);
VINtext.setText(VIN);
} else {
Log.d("ClassScanViewBarcodeActivity ,onPictureTaken(): bitmap=",String.valueOf(bitmap));
}
} else {
Log.d("ClassScanViewBarcodeActivity , onPictureTaken(): bmpOfTheImageFromCamera = ",
String.valueOf(bmpOfTheImageFromCamera));
}
}// end try
catch (Exception exc) {
exc.getMessage();
Log.d("ClassScanViewBarcodeActivity , scanButton.setOnClickListener(): exception = ",
exc.getMessage());
}
}// end onPictureTaken()
};// jpegCallback implementation
/*
* created savePicture(byte [] data) for testing
*/
public void savePicture(byte [] data)
{
Log.d( "ScanVinFromBarcodeActivity " , "savePicture(byte [] data)");
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyymmddhhmmss");
String date = dateFormat.format(new Date());
String photoFile = "Picture_"+counter+"_"+ date + ".jpg";
File sdDir = Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
String filename =sdDir + File.separator + photoFile;
File pictureFile = new File(filename);
try {
FileOutputStream fos = new FileOutputStream(pictureFile);
fos.write(data);
fos.close();
Toast.makeText(this, "New Image saved:" + photoFile,
Toast.LENGTH_LONG).show();
} catch (Exception error) {
Log.d( "File not saved: " , error.getMessage());
Toast.makeText(this, "Image could not be saved.",
Toast.LENGTH_LONG).show();
}
counter++;
}
private int findFrontFacingCamera() {
int cameraId = -1;
// Search for the front facing camera
int numberOfCameras = Camera.getNumberOfCameras();
for (int i = 0; i < numberOfCameras; i++) {
CameraInfo info = new CameraInfo();
Camera.getCameraInfo(i, info);
if (info.facing == CameraInfo.CAMERA_FACING_BACK) {
Log.d("ClassScanViewBarcodeActivity , findFrontFacingCamera(): ",
"Camera found");
cameraId = i;
break;
}
}
return cameraId;
}// end findFrontFacingCamera()
#Override
protected void onPause() {
if (globalCamera != null) {
globalCamera.stopPreview();
globalCamera.setPreviewCallback(null);
globalCamera.release();
globalCamera = null;
}
super.onPause();
}// end onPause()
public String decodeBitmapToString(BinaryBitmap bitmap) {
Reader reader = null;
Result result = null;
String textResult = null;
try {
reader = new MultiFormatReader();
if (bitmap != null) {
result = reader.decode(bitmap);
if (result != null) {
textResult = result.getText();
} else {
Log.d("ClassScanViewBarcodeActivity , String decodeBitmapToString (BinaryBitmap bitmap): result = ",
String.valueOf(result));
}
} else {
Log.d("ClassScanViewBarcodeActivity , String decodeBitmapToString (BinaryBitmap bitmap): bitmap = ",
String.valueOf(bitmap));
}
/*
* byte[] rawBytes = result.getRawBytes(); BarcodeFormat format =
* result.getBarcodeFormat(); ResultPoint[] points =
* result.getResultPoints();
*/
} catch (NotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ChecksumException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (FormatException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return textResult;
}// end decodeBitmapToString (BinaryBitmap bitmap)
public BinaryBitmap cameraBytesToBinaryBitmap(Bitmap bitmap) {
BinaryBitmap binaryBitmap = null;
if (bitmap != null) {
int[] pixels = new int[bitmap.getHeight() * bitmap.getWidth()];
bitmap.getPixels(pixels, 0, 0, bitmap.getWidth() - 1,
bitmap.getHeight() - 1, bitmap.getWidth(),
bitmap.getHeight());
RGBLuminanceSource source = new RGBLuminanceSource(
bitmap.getWidth(), bitmap.getHeight(), pixels);
HybridBinarizer bh = new HybridBinarizer(source);
binaryBitmap = new BinaryBitmap(bh);
} else {
Log.d("ClassScanViewBarcodeActivity , cameraBytesToBinaryBitmap (Bitmap bitmap): bitmap = ",
String.valueOf(bitmap));
}
return binaryBitmap;
}
#SuppressLint("NewApi")
#SuppressWarnings("deprecation")
/*
* The method getScreenOrientation() return screen orientation either
* landscape or portrait. IF width < height , than orientation = portrait,
* ELSE landscape For backwards compatibility we use to methods to detect
* the orientation. The first method is for API versions prior to 13 or
* HONEYCOMB.
*/
public int getScreenOrientation() {
int currentapiVersion = android.os.Build.VERSION.SDK_INT;
// if API version less than 13
Display getOrient = getWindowManager().getDefaultDisplay();
int orientation = Configuration.ORIENTATION_UNDEFINED;
if (currentapiVersion < android.os.Build.VERSION_CODES.HONEYCOMB) {
// Do something for API version less than HONEYCOMB
if (getOrient.getWidth() == getOrient.getHeight()) {
orientation = Configuration.ORIENTATION_SQUARE;
} else {
if (getOrient.getWidth() < getOrient.getHeight()) {
orientation = Configuration.ORIENTATION_PORTRAIT;
} else {
orientation = Configuration.ORIENTATION_LANDSCAPE;
}
}
} else {
// Do something for API version greater or equal to HONEYCOMB
Point size = new Point();
this.getWindowManager().getDefaultDisplay().getSize(size);
int width = size.x;
int height = size.y;
if (width < height) {
orientation = Configuration.ORIENTATION_PORTRAIT;
} else {
orientation = Configuration.ORIENTATION_LANDSCAPE;
}
}
return orientation;
}// end getScreenOrientation()
}// end class ScanVinFromBarcodeActivity
Preview class used to display live camera on SurfaceView (CameraPreview.java):
package com.ty.tyownerspoc.barcode;
import java.io.IOException;
import java.util.List;
import android.content.Context;
import android.hardware.Camera;
import android.hardware.Camera.Size;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.ViewGroup;
import android.hardware.Camera.CameraInfo;
/** A basic Camera preview class */
public class CameraPreview extends SurfaceView implements
SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private Camera mCamera;
private Context context;
public CameraPreview(Context context, Camera camera) {
super(context);
mCamera = camera;
this.context = context;
// 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);
}
public void surfaceCreated(SurfaceHolder holder) {
// The Surface has been created, now tell the camera where to draw the
// preview.
try {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
} catch (IOException e) {
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
// empty. Take care of releasing the Camera preview in your activity.
}
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.
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
}
Camera.Parameters p = mCamera.getParameters();
// get width & height of the SurfaceView
int SurfaceViewWidth = this.getWidth();
int SurfaceViewHeight = this.getHeight();
List<Size> sizes = p.getSupportedPreviewSizes();
Size optimalSize = getOptimalPreviewSize(sizes, SurfaceViewWidth, SurfaceViewHeight);
// set parameters
p.setPreviewSize(optimalSize.width, optimalSize.height);
/*rotate the image by 90 degrees clockwise , in order to correctly displayed the image , images seem to be -90 degrees (counter clockwise) rotated
* I even tried setting it to p.setRotation(0); , but still no effect.
*/
mCamera.setDisplayOrientation(90);
mCamera.setParameters(p);
// start preview with new settings
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (Exception e) {
Log.d("CameraPreview , surfaceCreated() , orientation: ",
String.valueOf(e.getMessage()));
}
}// end surfaceChanged()
static Size getOptimalPreviewSize(List <Camera.Size>sizes, int w, int h) {
final double ASPECT_TOLERANCE = 0.1;
final double MAX_DOWNSIZE = 1.5;
double targetRatio = (double) w / h;
if (sizes == null) return null;
Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
// Try to find an size match aspect ratio and size
for (Camera.Size size : sizes) {
double ratio = (double) size.width / size.height;
double downsize = (double) size.width / w;
if (downsize > MAX_DOWNSIZE) {
//if the preview is a lot larger than our display surface ignore it
//reason - on some phones there is not enough heap available to show the larger preview sizes
continue;
}
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
// Cannot find the one match the aspect ratio, ignore the requirement
//keep the max_downsize requirement
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Size size : sizes) {
double downsize = (double) size.width / w;
if (downsize > MAX_DOWNSIZE) {
continue;
}
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}
//everything else failed, just take the closest match
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Size size : sizes) {
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}
return optimalSize;
}
}//end Preview class
Layout for the “ScanVinFromBarcodeActivity” activity (activity_barcode_vin_scanner.xml):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:padding="20dip" >
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:padding="20dip"
android:text="#string/decode_label"
android:textColor="#color/mbackground1" />
<TextView
android:id="#+id/mytext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#color/mbackground2"
android:gravity="center_horizontal"
android:padding="20dip"
android:textColor="#color/mytextcolor" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:padding="20dip"
android:text="#string/continue_label"
android:textColor="#color/mytextcolor" />
<Button
android:id="#+id/webbutton"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/web_button"
android:textColor="#color/mytextcolor" />
<FrameLayout
android:id="#+id/FrameLayoutForPreview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1">
<SurfaceView
android:id="#+id/surfaceViewBarcodeScanner"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</FrameLayout>
</LinearLayout>
Any help will be appreciated.
Thanks
Updated & working onTakePicturen method, the that works , the PictureCallback returns (stopPreview & StartPreview were moved to inside this method in its finally block).
public void onPictureTaken(byte[] imgData, Camera camera) {
BinaryBitmap bitmap;
try {
Log.d("ClassScanViewBarcodeActivity" ,"onPictureTaken()");
//save image to sd card
savePicture(imgData);
// get the bitmap from camera imageData
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;
//down sample
bmpOfTheImageFromCamera = BitmapFactory.decodeByteArray(
imgData, 0, imgData.length,options);
if (bmpOfTheImageFromCamera != null) {
Log.d("***ClassScanViewBarcodeActivity ,onPictureTaken(): bmpOfTheImageFromCamera getByteCount(): ",
String.valueOf(bmpOfTheImageFromCamera.getByteCount()));
//Galaxy S3 , incorrect rotation issue rotate to correct rotation
/*Matrix matrix = new Matrix();
matrix.postRotate(90);
Bitmap rotatedBitmap = Bitmap.createBitmap(bmpOfTheImageFromCamera, 0, 0, bmpOfTheImageFromCamera.getWidth(), bmpOfTheImageFromCamera.getHeight(), matrix, true);*/
// convert bitmap to binary bitmap
bitmap = cameraBytesToBinaryBitmap(bmpOfTheImageFromCamera);
if (bitmap != null) {
// decode the VIN
String VIN = decodeBitmapToString(bitmap);
Log.d("***ClassScanViewBarcodeActivity ,onPictureTaken(): VIN ",
VIN);
VINtext.setText(VIN);
} else {
Log.d("ClassScanViewBarcodeActivity ,onPictureTaken(): bitmap=",String.valueOf(bitmap));
}
} else {
Log.d("ClassScanViewBarcodeActivity , onPictureTaken(): bmpOfTheImageFromCamera = ",
String.valueOf(bmpOfTheImageFromCamera));
}
}// end try
catch (Exception exc) {
exc.getMessage();
Log.d("ClassScanViewBarcodeActivity , scanButton.setOnClickListener(): exception = ",
exc.getMessage());
}
finally
{
globalCamera.stopPreview();
//start previewing again onthe SurfaceView in case use wants to take another pic/scan
globalCamera.startPreview();
}
}// end onPictureTaken()
};// jpegCallback implementation
The only thing I can find that might be the culprit is that you're starting the preview again before the jpegCallback has returned. According to the javadoc, this is not allowed:
After calling this method, you must not call startPreview() or take another picture until the JPEG callback has returned.
The callback is actually put on the UI thread queue - which will be suspended in your Thread.sleep() and the same thread will have called stopPreview() and startPreview() before actually coming to the callback. Generally speaking, if you call Thread.sleep() on the UI thread - you're doing it wrong. So, hopefully if you remove the sleep() and put what comes after it in the jpegCallback your problem should be solved.
Related
I want to implement zoom function while video recording in android.But, I am not able to access Camera parameters or Camera startSmoothZoom() method in neither main activity nor surface class. If you access camera parametera in media recorder method(prepareMediaRecorder()) it will throw null pointer exception.
this activity class- in prepareMediaRecorder() method not able to access camera parameters and also not able to set startSmoothZoom(). here camera object giving null pointer exception.
public class CustomCameraPreview extends BaseActivity implements
OnClickListener, AlertPositiveListener, OrientationListener,
ActivityCompat.OnRequestPermissionsResultCallback {
RelativeLayout mLayout;
MediaRecorder mediaRecorder;
private PictureCallback mPictureCallback = new PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
try {
cameraData = data;
captureAngle = getRotation();
mBitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
}
catch (OutOfMemoryError e){
System.gc();
mBitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
}
// int rotation=getRotation();
Matrix matrix = new Matrix();
matrix.postRotate(getRotation());
/*mBitmap = Bitmap.createBitmap(mBitmap, 0, 0, mBitmap.getWidth(),
mBitmap.getHeight(), matrix, true);*/
mBitmap = Bitmap.createBitmap(mBitmap, 0, 0, mBitmap.getWidth(),
mBitmap.getHeight(), matrix, false);
if (mBitmap != null) {
mButtonRetake.setEnabled(true);
} else {
Message.displayToast(CustomCameraPreview.this,
getString(R.string.picture_not_taken));
}
mCamera.release();
mButtonCapture.setEnabled(false);
}
};
protected void onCreate(){
initiCameraForVideo();
}
private void initiCameraForVideo() {
params = mCamera.getParameters();
mButtonCapture.setBackgroundResource(R.drawable.videostart);
mShowCamera = new CameraSurfaceHolder(CustomCameraPreview.this,
mCamera);
mLayout = (RelativeLayout) findViewById(R.id.relativeLayout);
mLayout.removeAllViews();
mLayout.addView(mShowCamera);
List<Camera.Size> mSizeList_Video = null;// params.getSupportedPreviewSizes();
if (params.getSupportedVideoSizes() != null) {
mSizeList_Video = params.getSupportedVideoSizes();
} else {
// Video sizes may be null, which indicates that all the
// supported preview sizes are supported for video
// recording.
mSizeList_Video = mCamera.getParameters()
.getSupportedPreviewSizes();
}
}
#Override
public void onClick(View v) {
int viewId = v.getId();
switch (viewId) {
case R.id.button_Capture:
releaseCamera();
if (!prepareMediaRecorder()) {
Message.displayToast(
CustomCameraPreview.this,
getString(R.string.somethign_went_wrong));
} else {
mediaRecorder.start();
recording = true;
}
}
private boolean prepareMediaRecorder() \*method to setup media player to record video
{
mCamera = isCameraAvailable();
mediaRecorder = new MediaRecorder();
mCamera.unlock();
mediaRecorder.setCamera(mCamera);
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
if(CamcorderProfile.hasProfile(findCameraID(), CamcorderProfile.QUALITY_480P)) {
mediaRecorder.setProfile(CamcorderProfile
.get(CamcorderProfile.QUALITY_480P));
}else{
mediaRecorder.setProfile(CamcorderProfile
.get(CamcorderProfile.QUALITY_LOW));
}
mediaRecorder.setOutputFile(getOutputVideoFile());
mediaRecorder.setMaxDuration(60000);
// mediaRecorder.setMaxFileSize(100 * 1000 * 1000);
mediaRecorder.setPreviewDisplay(mShowCamera.getHolder().getSurface());
try {
mediaRecorder.prepare();
}
}
#Override
protected void onPause() {
super.onPause();
releaseMediaRecorder();
releaseCamera();
}
private void releaseCamera() {
if (mCamera != null) {
mCamera.release();
mCamera = null;
}
}
and this surface preview class-
public class CameraSurfaceHolder extends SurfaceView implements
SurfaceHolder.Callback {
private static final String TAG = "CameraSurfaceHolder";
Context mContext;
private String errorMessage = "";
private SurfaceHolder mSurfaceHolder;
private Camera mCamera;
public CameraSurfaceHolder(Context context, Camera camera) {
super(context);
mContext = context;
mCamera = camera;
mSurfaceHolder = getHolder();
mSurfaceHolder.addCallback(this);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
/*if (holder.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
setCameraDisplayOrientation((Activity)mContext, Camera.CameraInfo.CAMERA_FACING_BACK, mCamera);
// start preview with new settings
try {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
}
*/
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
try {
mCamera.setPreviewDisplay(mSurfaceHolder);
mCamera.startPreview();
} catch (Exception e) {
Logger.ex(e);
}
}
public void setCameraDisplayOrientation(Activity activity, int cameraId,
Camera camera) {
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(cameraId, info);
/*Display mDisplay = ((WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
int rotation = mDisplay.getDisplayId();*/
int rotation = activity.getWindowManager().getDefaultDisplay()
.getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
}
int result;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360; // compensate the mirror
} else { // back-facing
result = (info.orientation - degrees + 360) % 360;
}
Camera.Parameters mParameters = camera.getParameters();
mParameters.setRotation(rotation);
camera.setDisplayOrientation(result);
camera.setParameters(mParameters);
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
}
in above class I added some code like below and when user touch on camera preview its throwing null pointer exception in onTouchEvent() on access of camera paramters. Also tried like I set again camera object to surface in activity after configure media recorder(prepareMediaRecorder()), but zoom function working but video is not recording.
#Override
public boolean onTouchEvent(MotionEvent event) {
// Get the pointer ID
Camera.Parameters params = mCamera.getParameters();
int action = event.getAction();
if (event.getPointerCount() > 1) {
// handle multi-touch events
if (action == MotionEvent.ACTION_POINTER_DOWN) {
mDist = getFingerSpacing(event);
} else if (action == MotionEvent.ACTION_MOVE && params.isZoomSupported()) {
mCamera.cancelAutoFocus();
handleZoom(event, params);
}
} else {
// handle single touch events
if (action == MotionEvent.ACTION_UP) {
handleFocus(event, params);
}
}
return true;
}
private void handleZoom(MotionEvent event, Camera.Parameters params) {
int maxZoom = params.getMaxZoom();
int zoom = params.getZoom();
float newDist = getFingerSpacing(event);
if (newDist > mDist) {
//zoom in
if (zoom < maxZoom)
zoom++;
} else if (newDist < mDist) {
//zoom out
if (zoom > 0)
zoom--;
}
mDist = newDist;
params.setZoom(zoom);
mCamera.setParameters(params);
}
public void handleFocus(MotionEvent event, Camera.Parameters params) {
int pointerId = event.getPointerId(0);
int pointerIndex = event.findPointerIndex(pointerId);
// Get the pointer's current position
float x = event.getX(pointerIndex);
float y = event.getY(pointerIndex);
List<String> supportedFocusModes = params.getSupportedFocusModes();
if (supportedFocusModes != null && supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
mCamera.autoFocus(new Camera.AutoFocusCallback() {
#Override
public void onAutoFocus(boolean b, Camera camera) {
// currently set to auto-focus on single touch
}
});
}
}
/** Determine the space between the first two fingers */
private float getFingerSpacing(MotionEvent event) {
// ...
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return (float)Math.sqrt(x * x + y * y);
}
i am trying to make an app that detects faces. The code below has no errors and from what i can see should be working fine. However when I run it, i get the following:
FATAL EXCEPTION: main
Process: org.bytefish.videofacedetection, PID: 12075
java.lang.NullPointerException: Attempt to invoke virtual method 'void
android.hardware.Camera.setFaceDetectionListener(android.hardware.Camera$FaceDetectionListener)'
on a null object reference
at org.bytefish.videofacedetection.
CameraActivity.surfaceCreated(CameraActivity.java:100)
at android.view.SurfaceView.updateWindow(SurfaceView.java:582)
at android.view.SurfaceView$3.onPreDraw(SurfaceView.java:177)
at
android.view.ViewTreeObserver.dispatchOnPreDraw(ViewTreeObserver.java:944)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2055)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1107)
at
android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6013)
at
android.view.Choreographer$CallbackRecord.run(Choreographer.java:858)
at android.view.Choreographer.doCallbacks(Choreographer.java:670)
at android.view.Choreographer.doFrame(Choreographer.java:606)
at
android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:844)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
The code is below, an all caps comment shows line 100 and the error
import android.app.Activity;
import android.content.Context;
import android.hardware.Camera;
import android.hardware.Camera.Face;
import android.hardware.Camera.FaceDetectionListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.util.Log;
import android.view.OrientationEventListener;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.ViewGroup.LayoutParams;
import android.widget.Toast;
import java.util.List;
public class CameraActivity extends Activity
implements SurfaceHolder.Callback {
public static final String TAG = CameraActivity.class.getSimpleName();
private Camera mCamera;
// We need the phone orientation to correctly draw the overlay:
private int mOrientation;
private int mOrientationCompensation;
private OrientationEventListener mOrientationEventListener;
// Let's keep track of the display rotation and orientation also:
private int mDisplayRotation;
private int mDisplayOrientation;
// Holds the Face Detection result:
private Camera.Face[] mFaces;
// The surface view for the camera data
private SurfaceView mView;
// Draw rectangles and other fancy stuff:
private FaceOverlayView mFaceView;
// Log all errors:
private final CameraErrorCallback mErrorCallback = new CameraErrorCallback();
/**
* Sets the faces for the overlay view, so it can be updated
* and the face overlays will be drawn again.
*/
private FaceDetectionListener faceDetectionListener = new FaceDetectionListener() {
#Override
public void onFaceDetection(Face[] faces, Camera camera) {
Log.d("onFaceDetection", "Number of Faces:" + faces.length);
// Update the view now!
mFaceView.setFaces(faces);
}
};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mView = new SurfaceView(this);
setContentView(mView);
// Now create the OverlayView:
mFaceView = new FaceOverlayView(this);
addContentView(mFaceView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
// Create and Start the OrientationListener:
mOrientationEventListener = new SimpleOrientationEventListener(this);
mOrientationEventListener.enable();
}
#Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
SurfaceHolder holder = mView.getHolder();
holder.addCallback(this);
}
#Override
protected void onPause() {
mOrientationEventListener.disable();
super.onPause();
}
#Override
protected void onResume() {
mOrientationEventListener.enable();
super.onResume();
}
#Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
mCamera = Camera.open();
mCamera.setFaceDetectionListener(faceDetectionListener);
mCamera.startFaceDetection();
try {
mCamera.setPreviewDisplay(surfaceHolder);
} catch (Exception e) {
Log.e(TAG, "Could not preview the image.", e);
}
}
#Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {
// We have no surface, return immediately:
if (surfaceHolder.getSurface() == null) {
return;
}
// Try to stop the current preview:
try {
mCamera.stopPreview();
} catch (Exception e) {
// Ignore...
}
configureCamera(width, height);
setDisplayOrientation();
setErrorCallback();
// Everything is configured! Finally start the camera preview again:
mCamera.startPreview();
}
private void setErrorCallback() {
mCamera.setErrorCallback(mErrorCallback);
}
private void setDisplayOrientation() {
// Now set the display orientation:
mDisplayRotation = Util.getDisplayRotation(CameraActivity.this);
mDisplayOrientation = Util.getDisplayOrientation(mDisplayRotation, 0);
mCamera.setDisplayOrientation(mDisplayOrientation);
if (mFaceView != null) {
mFaceView.setDisplayOrientation(mDisplayOrientation);
}
}
private void configureCamera(int width, int height) {
Camera.Parameters parameters = mCamera.getParameters();
// Set the PreviewSize and AutoFocus:
setOptimalPreviewSize(parameters, width, height);
setAutoFocus(parameters);
// And set the parameters:
mCamera.setParameters(parameters);
}
private void setOptimalPreviewSize(Camera.Parameters cameraParameters, int width, int height) {
List<Camera.Size> previewSizes = cameraParameters.getSupportedPreviewSizes();
float targetRatio = (float) width / height;
Camera.Size previewSize = Util.getOptimalPreviewSize(this, previewSizes, targetRatio);
cameraParameters.setPreviewSize(previewSize.width, previewSize.height);
}
private void setAutoFocus(Camera.Parameters cameraParameters) {
cameraParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
}
#Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
mCamera.setPreviewCallback(null);
mCamera.setFaceDetectionListener(null);
mCamera.setErrorCallback(null);
mCamera.release();
mCamera = null;
}
/**
* We need to react on OrientationEvents to rotate the screen and
* update the views.
*/
private class SimpleOrientationEventListener extends OrientationEventListener {
public SimpleOrientationEventListener(Context context) {
super(context, SensorManager.SENSOR_DELAY_NORMAL);
}
#Override
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 or sky, we still have
// the correct orientation.
if (orientation == ORIENTATION_UNKNOWN) return;
mOrientation = Util.roundOrientation(orientation, mOrientation);
// When the screen is unlocked, display rotation may change. Always
// calculate the up-to-date orientationCompensation.
int orientationCompensation = mOrientation
+ Util.getDisplayRotation(CameraActivity.this);
if (mOrientationCompensation != orientationCompensation) {
mOrientationCompensation = orientationCompensation;
mFaceView.setOrientation(mOrientationCompensation);
}
}
}
}
When i try to open it in android studio it opens fine with no errors but dies when i try to run it.
I am new to android so any tips would be much appreciated. Thanks a ton for your help and time.
Ok, it looks like you are not getting the camera object right
this code will init the front camera properly
part1
cameraId = findFrontFacingCamera();
if (cameraId < 0) {
Toast.makeText(this, "No camera found.",
Toast.LENGTH_LONG).show();
} else {
mcamera = Camera.open(cameraId);
}
part2
private int findFrontFacingCamera() {
int cameraId = -1;
// Search for the front facing camera
int numberOfCameras = Camera.getNumberOfCameras();
for (int i = 0; i < numberOfCameras; i++) {
CameraInfo info = new CameraInfo();
Camera.getCameraInfo(i, info);
if (info.facing == CameraInfo.CAMERA_FACING_FRONT) {
Log.d(DEBUG_TAG, "Camera found");
cameraId = i;
break;
}
}
return cameraId;
}
please try and let me know.
I'm trying to build a custom Camera in Android without using the Camera API (Using intents). I have a MainActivity.java, I need to call the CameraPreview class in order to see the camera preview, but I don't know how to call that class.
I tried
public CameraPreview cameraPreview;
and later
mPreview = new CameraPreview(this, mCamera);
if(hasCamera) {
// Create an instance of Camera
mCamera = getCameraInstance();
// Create our Preview view and set it as the content of our activity.
mPreview = new CameraPreview(this, mCamera);
FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
preview.addView(mPreview);
}
It doesn't work. Please advice!
My code compiles but I don't see the preview on screen.
public class MainActivity extends AppCompatActivity implements SensorEventListener{
public CameraPreview cameraPreview;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
hasCamera = checkCameraHardware(this);
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
if (mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) != null){
// Success! There's a accelerometer.
mAccel = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
Toast.makeText(MainActivity.this, "Accelerometer Found!", Toast.LENGTH_SHORT).show();
}
else {
// Failure! No accelerometer.
Toast.makeText(MainActivity.this, "Accelerometer Not Found!", Toast.LENGTH_SHORT).show();
}
if(hasCamera) {
// Create an instance of Camera
mCamera = getCameraInstance();
// Create our Preview view and set it as the content of our activity.
mPreview = new CameraPreview(this, mCamera);
FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
preview.addView(mPreview);
/* Button captureButton = (Button) findViewById(R.id.button_capture);
captureButton.setOnClickListener(
new View.OnClickListener() {
#Override
public void onClick(View v) {
// get an image from the camera
mCamera.takePicture(null, null, mPicture);
}
}
); */
}
//Accelerometer Handling
mAccel = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mSensorManager.registerListener(this, mAccel, SensorManager.SENSOR_DELAY_NORMAL);
if(safeToTakePicture) {
// cameraPreview.surfaceCreated(mHolder);
mCamera.takePicture(null, null, mPicture);
safeToTakePicture = false;
}
}
#Override
public void onSensorChanged(SensorEvent event) {
long currentTime = System.currentTimeMillis();
// update every 1 sec
if((currentTime - prevTime) > 10000){
mDiff = currentTime - prevTime;
mX = event.values[0];
mY = event.values[1];
mZ = event.values[2];
float speed = Math.abs(mX + mY + mZ - mPrev_x - mPrev_y - mPrev_z) / mDiff;
if (speed > SHAKE_THRESHOLD) {
Log.d("sensor", "Shake detected with speed: " + speed);
mCamera.takePicture(null, null, mPicture);
safeToTakePicture = false;
Toast.makeText(this, "Shake detected with speed: " + speed, Toast.LENGTH_SHORT).show();
}
}
mPrev_x = mX;
mPrev_y = mY;
mPrev_z = mZ;
prevTime = currentTime;
// mCamera.takePicture(null, null, mPicture);
// Toast.makeText(MainActivity.this, "SensorChanged Event! x:" + event.values[0], Toast.LENGTH_SHORT).show();
}
private boolean checkCameraHardware(Context context) {
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
// this device has a camera
return true;
} else {
// no camera on this device
return false;
}
}
/** A safe way to get an instance of the Camera object. */
//Deprecated Camera instance, but ok
public static Camera getCameraInstance(){
Camera c = null;
try {
c = Camera.open(); // attempt to get a Camera instance
}
catch (Exception e){
// Camera is not available (in use or does not exist)
}
return c; // returns null if camera is unavailable
}
// receive data in a JPEG format,
// you must implement an Camera.PictureCallback interface to receive the image data and write it to a file
private Camera.PictureCallback mPicture = new Camera.PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE);
if (pictureFile == null){
Log.d(TAG, "Error creating media file, check storage permissions: " /* +
e.getMessage()*/);
return;
}
try {
FileOutputStream fos = new FileOutputStream(pictureFile);
fos.write(data);
fos.close();
} catch (FileNotFoundException e) {
Log.d(TAG, "File not found: " + e.getMessage());
} catch (IOException e) {
Log.d(TAG, "Error accessing file: " + e.getMessage());
}
}
};
private static File getOutputMediaFile(int type){
// To be safe, you should check that the SDCard is mounted
// using Environment.getExternalStorageState() before doing this.
File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES), "MyCameraApp");
// This location works best if you want the created images to be shared
// between applications and persist after your app has been uninstalled.
// Create the storage directory if it does not exist
if (! mediaStorageDir.exists()){
if (! mediaStorageDir.mkdirs()){
Log.d("MyCameraApp", "failed to create directory");
return null;
}
}
// Create a media file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
File mediaFile;
if (type == MEDIA_TYPE_IMAGE){
mediaFile = new File(mediaStorageDir.getPath() + File.separator +
"IMG_"+ timeStamp + ".jpg");
} else if(type == MEDIA_TYPE_VIDEO) {
mediaFile = new File(mediaStorageDir.getPath() + File.separator +
"VID_"+ timeStamp + ".mp4");
} else {
return null;
}
return mediaFile;
}
public final void onAccuracyChanged(Sensor sensor, int accuracy){
//mCamera.takePicture(null, null, mPicture);
//Toast.makeText(this, "Taking a picture now", Toast.LENGTH_SHORT).show();
}
#Override
protected void onPause() {
super.onPause();
letGo();
}
#Override
protected void onDestroy() {
super.onDestroy();
letGo();
}
private void letGo(){
if(mCamera != null){
mCamera.stopPreview();
mCamera.setPreviewCallback(null);
mCamera.release();
mCamera = null;
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
// Surface will be destroyed when we return, so stop the preview.
if (mCamera != null) {
// Call stopPreview() to stop updating the preview surface.
mCamera.stopPreview();
}
}
/**
* When this function returns, mCamera will be null.
*/
private void stopPreviewAndFreeCamera() {
if (mCamera != null) {
// Call stopPreview() to stop updating the preview surface.
mCamera.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()).
mCamera.release();
mCamera = null;
}
}
}
CameraPreview
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private static final String TAG = MainActivity.class.getSimpleName();
SurfaceView mSurfaceView;
private Camera mCamera;
private boolean safeToTakePicture = false;
public CameraPreview(Context context, Camera camera) {
super(context);
mCamera = camera;
mSurfaceView = new SurfaceView(context);
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = mSurfaceView.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) {
// The Surface has been created, now tell the camera where to draw the preview.
try {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
} catch (IOException e) {
Log.d(TAG, "Error setting camera preview: " + e.getMessage());
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
// empty. Take care of releasing the Camera preview in your activity.
}
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.
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
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
safeToTakePicture = true;
} catch (Exception e){
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
}
}
}
Ok guys, sharing my stable variant,
Here is improved Custom camera preview class (make attention! preview size set to 800x600 and image size around 5mpx for better performance, this can be removed, look in code):
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private Camera mCamera;
private Context mContext;
private Camera.PreviewCallback previewCallback;
private Camera.AutoFocusCallback autoFocusCallback;
private static final String TAG = "CameraPreview";
private List<Camera.Size> mSupportedPreviewSizes;
private Camera.Size mPreviewSize;
public CameraPreview(Context context, Camera camera,
Camera.PreviewCallback previewCb,
Camera.AutoFocusCallback autoFocusCb) {
super(context);
mContext = context;
mCamera = camera;
previewCallback = previewCb;
autoFocusCallback = autoFocusCb;
// supported preview sizes
mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
// for(Camera.Size str: mSupportedPreviewSizes)
// Log.e(TAG, str.width + "/" + str.height);
// for(Camera.Size str: mCamera.getParameters().getSupportedPictureSizes())
// Log.e(TAG, str.width + "/" + str.height);
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
// // deprecated setting, but required on Android versions prior to 3.0
// mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
/*
* 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
// Camera.Parameters parameters = mCamera.getParameters();
// parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
// mCamera.setParameters(parameters);
Camera.Parameters params = mCamera.getParameters();
for(Camera.Size str: params.getSupportedPictureSizes()) {
if (str.width >= 2400 && str.width <= 2600 && str.height >= 1800 && str.height <= 2000) {
params.setPictureSize(str.width, str.height);
Log.e("Picture size: ", str.width + "/" + str.height);
}
}
for(Camera.Size str: params.getSupportedPreviewSizes())
if(str.width == 800 && str.height == 600){
params.setPreviewSize(str.width, str.height);
Log.e("Preview size: ", str.width + "/" + str.height);
}
mCamera.setParameters(params);
mCamera.setDisplayOrientation(90);
mCamera.setPreviewDisplay(mHolder);
mCamera.setPreviewCallback(previewCallback);
mCamera.startPreview();
mCamera.autoFocus(autoFocusCallback);
if(!(SettingsService.settings.autofocusOn))
mCamera.cancelAutoFocus();
} catch (Exception e) {
Log.d("DBG", "Error starting camera preview: " + e.getMessage());
}
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
this.getHolder().removeCallback(this);
try {
mCamera.stopPreview();
mCamera.release();
}catch(Exception e){
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
if (mSupportedPreviewSizes != null) {
mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
}
float ratio;
if(mPreviewSize.height >= mPreviewSize.width)
ratio = (float) mPreviewSize.height / (float) mPreviewSize.width;
else
ratio = (float) mPreviewSize.width / (float) mPreviewSize.height;
// One of these methods should be used, second method squishes preview slightly
setMeasuredDimension(width, (int) (width * ratio));
// setMeasuredDimension((int) (width * ratio), height);
}
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.height / size.width;
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;
}
}
And here is usage in activity or fragment:
public class ScanFragment extends Fragment {
private View mFragmentView;
private static Camera mCamera;
private CameraPreview mPreview;
private Handler autoFocusHandler;
private Context mContext;
public boolean previewing = true;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mContext = (MainActivity)getActivity();
// Inflate the layout for this fragment
mFragmentView = inflater.inflate(R.layout.fragment_scan, container, false);
}
public void cameraPreviewStop(){
previewing = false;
if(mCamera != null) {
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
}
}
public void cameraPreviewStart(){
try {
if (mCamera != null) {
mCamera.setPreviewCallback(previewCb);
mCamera.startPreview();
previewing = true;
mCamera.autoFocus(autoFocusCB);
}else{
initControls();
cameraPreviewStart();
}
}catch (Exception e){
releaseCamera();
initControls();
cameraPreviewStart();
}
}
public void initControls() {
getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
autoFocusHandler = new Handler();
try{
mCamera = getCameraInstance();
}catch(Exception e){
}
// Instance barcode scanner
initPreviewCamera();
}
public void initPreviewCamera(){
autoFocusCB = new Camera.AutoFocusCallback() {
public void onAutoFocus(boolean success, Camera camera) {
autoFocusHandler.postDelayed(doAutoFocus, 1000);
}
};
mPreview = new CameraPreview(mContext, mCamera, previewCb,
autoFocusCB);
FrameLayout preview = (FrameLayout) mFragmentView.findViewById(R.id.cameraPreview);
preview.removeAllViews();
preview.addView(mPreview);
}
/**
* 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;
}
public void releaseCamera() {
if (mCamera != null) {
previewing = false;
try {
mCamera.setPreviewCallback(null);
mCamera.release();
}catch (Exception e){
}
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 imageFromCamera = new Image(size.width, size.height, "Y800");
// Do something...
}
};
// Mimic continuous auto-focusing
Camera.AutoFocusCallback autoFocusCB = new Camera.AutoFocusCallback() {
public void onAutoFocus(boolean success, Camera camera) {
autoFocusHandler.postDelayed(doAutoFocus, 1000);
}
};
}
If in activity then override some things:
#Override
protected void onResume() {
super.onResume();
if(previewing) {
initControls();
cameraPreviewStart();
}
}
#Override
protected void onStop() {
super.onStop();
releaseCamera();
}
And the fragment view with camera fragment (you can add here your custom views and anything you want to customize, size of preview, absolutely anything you want):
<FrameLayout 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"
xmlns:local="http://schemas.android.com/apk/res-auto">
<FrameLayout
android:id="#+id/cameraPreview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>
And final note, this is very important! Especially for Android api 23+ devices, from android 6.0 you must use only one instance of camera if you got null on camera object then you are trying to open camera that is already opened. Have a nice day ;)
I have been struggling with this bug on and off for the last month or so. Everytime that I think I have fixed it it seems to come back in some form.
It is the old Android "Image Rotated 90 degrees" bug. I have read countless Posts on here (StackOverFlow) aswell as tried numerous methods but just cannot seem to fix it.
I am still getting images that are rotated incorrectly.
In my application a user chooses his/her profile Picture, which is then set to an ImageView. The image is chosen from the Phones Gallery
Two days ago I implemented the Following Code, this worked for all the images I tested it with on my Phone. However when one of my Beta testers tried it, his images were once again rotated. He sent me the images for testing but they were displaying fine on my Phone. Hence why I am getting more and more frustrated.
This is the method I am using to get the Images orientation:
// Gets an Images Orientation
public static int getOrientationEXIF(Context context, Uri uri) {
int orientation = 0;
try {
ExifInterface exif = new ExifInterface(uri.getPath());
orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
orientation = 90;
return orientation;
case ExifInterface.ORIENTATION_ROTATE_180:
orientation = 180;
return orientation;
}
} catch (IOException e) {
e.printStackTrace();
}
return 0;
}
I then get a rotated Bitmap using this Method:
// Rotate a Bitmap
public static Bitmap rotate(float rotationValue, String filePath) {
Bitmap original= BitmapFactory.decodeFile(filePath);
int width = original.getWidth();
int height = original.getHeight();
Matrix matrix = new Matrix();
matrix.postRotate(rotationValue);
Bitmap rotated = Bitmap.createBitmap(original, 0, 0, width, height, matrix, true);
return rotated;
}
I am just not sure what to do anymore.
I would really like it if someone could help me figure this out
Thank you in advance
UPDATE
I just saw the following line of Code in my Log after implementing the suggested Methods:
JHEAD can't open 'file:/external/images/media/3885'
I am not sure what this means
UPDATE #2
I think I may have fixed the problem, I got the proper image path for the file.
You need to account for all orientations not just 90 or 180. I am using this
File curFile = new File("path-to-file"); // ... This is an image file from my device.
Bitmap rotatedBitmap;
try {
ExifInterface exif = new ExifInterface(curFile.getPath());
int rotation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
int rotationInDegrees = exifToDegrees(rotation);
Matrix matrix = new Matrix();
if (rotation != 0f) {matrix.preRotate(rotationInDegrees);}
rotatedBitmap = Bitmap.createBitmap(bitmap,0,0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
}catch(IOException ex){
Log.e(TAG, "Failed to get Exif data", ex);
}
and:
/**
* Gets the Amount of Degress of rotation using the exif integer to determine how much
* we should rotate the image.
* #param exifOrientation - the Exif data for Image Orientation
* #return - how much to rotate in degress
*/
private static int exifToDegrees(int exifOrientation) {
if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_90) { return 90; }
else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_180) { return 180; }
else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_270) { return 270; }
return 0;
}
This problem really sucks ! I notice this is an issue when choosing photos rather than taking them. I found the answer buried in the code for this cropping lib which seemed to always display things correctly https://github.com/jdamcd/android-crop while cropping (despite it sometimes returning things with wrong orientation afterwards). Anyways, first start off by choosing the photo the way I choose in this answer: Pick image from fragment always return resultcode 0 in some devices
Next do this where you need to:
private void setRotationVariables(Uri uri)
{
m_rotationInDegrees = ImageOrientationUtil.getExifRotation(ImageOrientationUtil
.getFromMediaUri(
this,
getContentResolver(),
uri));
}
Here is the class:
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.provider.MediaStore;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import java.io.Closeable;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class ImageOrientationUtil
{
private static final String SCHEME_FILE = "file";
private static final String SCHEME_CONTENT = "content";
public static void closeSilently(#Nullable Closeable c) {
if (c == null) return;
try {
c.close();
} catch (Throwable t) {
// Do nothing
}
}
public static int getExifRotation(File imageFile) {
if (imageFile == null) return 0;
try {
ExifInterface exif = new ExifInterface(imageFile.getAbsolutePath());
// We only recognize a subset of orientation tag values
switch (exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED)) {
case ExifInterface.ORIENTATION_ROTATE_90:
return 90;
case ExifInterface.ORIENTATION_ROTATE_180:
return 180;
case ExifInterface.ORIENTATION_ROTATE_270:
return 270;
default:
return ExifInterface.ORIENTATION_UNDEFINED;
}
} catch (IOException e) {
// Log.e("Error getting Exif data", e);
return 0;
}
}
#Nullable
public static File getFromMediaUri(Context context, ContentResolver resolver, Uri uri) {
if (uri == null) return null;
if (SCHEME_FILE.equals(uri.getScheme())) {
return new File(uri.getPath());
} else if (SCHEME_CONTENT.equals(uri.getScheme())) {
final String[] filePathColumn = { MediaStore.MediaColumns.DATA, MediaStore.MediaColumns.DISPLAY_NAME };
Cursor cursor = null;
try {
cursor = resolver.query(uri, filePathColumn, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
final int columnIndex = (uri.toString().startsWith("content://com.google.android.gallery3d")) ?
cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME) :
cursor.getColumnIndex(MediaStore.MediaColumns.DATA);
// Picasa images on API 13+
if (columnIndex != -1) {
String filePath = cursor.getString(columnIndex);
if (!TextUtils.isEmpty(filePath)) {
return new File(filePath);
}
}
}
} catch (IllegalArgumentException e) {
// Google Drive images
return getFromMediaUriPfd(context, resolver, uri);
} catch (SecurityException ignored) {
// Nothing we can do
} finally {
if (cursor != null) cursor.close();
}
}
return null;
}
private static String getTempFilename(Context context) throws IOException {
File outputDir = context.getCacheDir();
File outputFile = File.createTempFile("image", "tmp", outputDir);
return outputFile.getAbsolutePath();
}
#Nullable
private static File getFromMediaUriPfd(Context context, ContentResolver resolver, Uri uri) {
if (uri == null) return null;
FileInputStream input = null;
FileOutputStream output = null;
try {
ParcelFileDescriptor pfd = resolver.openFileDescriptor(uri, "r");
FileDescriptor fd = pfd.getFileDescriptor();
input = new FileInputStream(fd);
String tempFilename = getTempFilename(context);
output = new FileOutputStream(tempFilename);
int read;
byte[] bytes = new byte[4096];
while ((read = input.read(bytes)) != -1) {
output.write(bytes, 0, read);
}
return new File(tempFilename);
} catch (IOException ignored) {
// Nothing we can do
} finally {
closeSilently(input);
closeSilently(output);
}
return null;
}
}
mochilogic answer is very good but his comment is also correct:
sorry, " do this where you need to " is so vague.."
I cannot add all of this in comment to mochilogic answer so I will write this here: If you dont like touse it in setRotationVariables(data.getData) - this is another way to use the class ImageOrientationUtil from his answer and this method:
private void setRotationVariables(Uri uri)
{
m_rotationInDegrees = ImageOrientationUtil.getExifRotation
(ImageOrientationUtil.getFromMediaUri(
this,
getContentResolver(),
uri));
}
You can send Uri from the galery to this method make it return the correct rotation in degrees by memebr as he does or by value as I did:
private static int setRotationVariables(Uri uri) {
int rotationInDegrees = ImageOrientationUtil.getExifRotation(ImageOrientationUtil
.getFileFromMediaUri(
appCtx,
appCtx.getContentResolver(),
uri));
Log.d(TAG, "setRotationVariables:" + "according to original Image Uri Exif details we need to rotate in "+rotationInDegrees + " Degrees");
return rotationInDegrees;
}
and then on the calling function after you scale your Uri to bitmap you can create bitmap using this rotationInDegrees with a matrix.
you can see it in my code here in this method I take Uri and scale it and rotate it and then returns it as bitmap.
but first - basicaly this is what you need:
int rotationDegree = setRotationVariables(uri);
if (rotationDegree > 0) {
Matrix matrix = new Matrix();
matrix.setRotate(rotationDegree);
Log.w(TAG, "recreating bitmap with rotation of " + rotationDegree + " degrees" );
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
}
This is the full method code if anyones need it..
public static Bitmap getScaledBitmapFromUri(Uri uri) throws FileNotFoundException, IOException {
final int TRY_SCALE_TO_THIS_SIZE = 1024;
Log.d(TAG, "getScaledBitmapFromUri:: calling setRotationVariables() to figure rotationDegree");
int rotationDegree = setRotationVariables(uri);
Context ctx = MessagingApp.getContext();
InputStream input = ctx.getContentResolver().openInputStream(uri);
BitmapFactory.Options onlyBoundsOptions = new BitmapFactory.Options();
onlyBoundsOptions.inJustDecodeBounds = true;
onlyBoundsOptions.inDither = true;//optional
onlyBoundsOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//optional
BitmapFactory.decodeStream(input, null, onlyBoundsOptions);
input.close();
if ( (onlyBoundsOptions.outWidth == -1) || (onlyBoundsOptions.outHeight == -1) )
return null;
int BiggestOriginalSize = (onlyBoundsOptions.outHeight > onlyBoundsOptions.outWidth) ? onlyBoundsOptions.outHeight : onlyBoundsOptions.outWidth;
//we will add 1 to Math.round (BiggestOriginalSize / (double)TRY_SCALE_TO_THIS_SIZE) in order to harden the scaling(we need smaller images for this project!)
double ratio = (BiggestOriginalSize > TRY_SCALE_TO_THIS_SIZE) ? (1 + Math.round(BiggestOriginalSize / (double) TRY_SCALE_TO_THIS_SIZE)) : 1.0;
Log.w(TAG, "getScaledBitmapFromUri:: originalSize: " + BiggestOriginalSize + "PX, TRY_SCALE_TO_THIS_SIZE (if original is bigger):" + TRY_SCALE_TO_THIS_SIZE +"PX");
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
bitmapOptions.inSampleSize = getPowerOfTwoForSampleRatio(ratio); //this one will give abstract results (sometimes bigger then TRY_SCALE_TO_THIS_SIZE)
Log.w(TAG, format("bitmapOptions.inSampleSize: " + bitmapOptions.inSampleSize));
bitmapOptions.inJustDecodeBounds = false; //check this out!!! maybe delete?
bitmapOptions.inDither = true;//optional
bitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//optional
//bitmapOptions.rogetrotationInDegrees
input = ctx.getContentResolver().openInputStream(uri);
Bitmap bitmap = BitmapFactory.decodeStream(input, null, bitmapOptions);
//bitmap = findExactInSampleSize(onlyBoundsOptions, TRY_SCALE_TO_THIS_SIZE, bitmap); // this one will never give bigger length then TRY_SCALE_TO_THIS_SIZE
if (rotationDegree > 0) {
Matrix matrix = new Matrix();
matrix.setRotate(rotationDegree);
Log.w(TAG, "recreating bitmap with rotation of " + rotationDegree + " degrees" );
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
}
Log.w(TAG, "after decodeStream : bitmap.getWidth(): " + bitmap.getWidth() + "PX, bitmap.getHeight(): " + bitmap.getHeight() +"PX.");
input.close();
return bitmap;
}
I have a canvas which I'm displaying a picture from the media store as its background and then I'm drawing on it. I'd like to save the background+what has been drawn over it but I'm only able to save the background, which is useless for me. I am trying to understand what I'm doing wrong. I basically did what people suggest here but it didn't help.
Here is the code which I have:
EDIT: THIS TIME I UPLOADED ALL THE CODE IN HERE
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.util.ArrayList;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.provider.MediaStore;
import android.view.Display;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Toast;
public class CanvasDrawingActivity extends Activity {
private ArrayList<Path> _graphics = new ArrayList<Path>();
private Paint mPaint;
public static final int GET_FROM_GALLERY = 1;
public static final int IMAGE_CAPTURE = 0;
public static final int SELECT_IMAGE_FROM_DEVICE = 1;
public Bitmap myBitmap;
public int isFirstTime = 0;
public DrawingPanel mPanel;
static int id = 1;
public Uri fileUri ;
#Override
public void onCreate(Bundle savedInstanceState) {
mPanel = new DrawingPanel(this);
mPanel.setId(findId());
super.onCreate(savedInstanceState);
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
//https://stackoverflow.com/questions/10937659/android-image-doesnt-save-using-native-camera-app-on-nexus-s
File f = new File(Environment.getExternalStorageDirectory().getPath(), String.format("%d.jpg", System.currentTimeMillis()));
fileUri= Uri.fromFile(f);
intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
startActivityForResult(intent, IMAGE_CAPTURE);
setContentView(R.layout.main);
}
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu, menu);
return true;
}
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.save:
savePicture();
return true;
case R.id.clear:
// startActivity(new Intent(this, Clear.class));
return true;
default:
return super.onOptionsItemSelected(item);
}
}
public void savePicture(){
long now = System.currentTimeMillis();
OutputStream fos;
try {
fos = new FileOutputStream(String.format(Environment.getExternalStorageDirectory().getAbsolutePath()+"/edited_%d.jpg",now));
myBitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
finish(); //close the activity.
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
//Detects request codes
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == IMAGE_CAPTURE && resultCode == RESULT_OK) {
Uri result;
result = fileUri;
//refresh storage
sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + Environment.getExternalStorageDirectory())));
try {
myBitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), result);
startDrawing();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
else if (resultCode == Activity.RESULT_CANCELED){
}
}
public void startDrawing(){
setContentView(mPanel);
mPaint = new Paint();
mPaint.setDither(true);
mPaint.setColor(0xFFFFFF00);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(3);
}
public int findId(){
View v = findViewById(id);
while (v != null){
v = findViewById(++id);
}
return id++;
}
class DrawingPanel extends SurfaceView implements SurfaceHolder.Callback {
private DrawingThread _thread;
private Path path;
public DrawingPanel(Context context) {
super(context);
setDrawingCacheEnabled(true);
getHolder().addCallback(this);
_thread = new DrawingThread(getHolder(), this);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
synchronized (_thread.getSurfaceHolder()) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
path = new Path();
path.moveTo(event.getX(), event.getY());
path.lineTo(event.getX(), event.getY());
}else if(event.getAction() == MotionEvent.ACTION_MOVE){
path.lineTo(event.getX(), event.getY());
if(_graphics.size() > 0) {
_graphics.remove(_graphics.size() - 1);
}
_graphics.add(path);
}else if(event.getAction() == MotionEvent.ACTION_UP){
path.lineTo(event.getX(), event.getY());
_graphics.remove(_graphics.size() - 1);
_graphics.add(path);
}
return true;
}
}
//Source: http://thinkandroid.wordpress.com/2009/12/25/resizing-a-bitmap/
public Bitmap getResizedBitmap(Bitmap bm, int newHeight, int newWidth) {
int width = bm.getWidth();
int height = bm.getHeight();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
// create a matrix for the manipulation
Matrix matrix = new Matrix();
// resize the bit map
matrix.postScale(scaleWidth, scaleHeight);
// recreate the new Bitmap
Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, false);
return resizedBitmap;
}
/**
* Converts a immutable bitmap to a mutable bitmap. This operation doesn't allocates
* more memory that there is already allocated.
*
* #param imgIn - Source image. It will be released, and should not be used more
* #return a copy of imgIn, but muttable.
*/
public Bitmap convertToMutable(Bitmap imgIn) {
try {
//this is the file going to use temporally to save the bytes.
// This file will not be a image, it will store the raw image data.
File file = new File(Environment.getExternalStorageDirectory() + File.separator + "temp.tmp");
//Open an RandomAccessFile
//Make sure you have added uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
//into AndroidManifest.xml file
RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
// get the width and height of the source bitmap.
int width = imgIn.getWidth();
int height = imgIn.getHeight();
Config type = imgIn.getConfig();
//Copy the byte to the file
//Assume source bitmap loaded using options.inPreferredConfig = Config.ARGB_8888;
FileChannel channel = randomAccessFile.getChannel();
MappedByteBuffer map = channel.map(MapMode.READ_WRITE, 0, imgIn.getRowBytes()*height);
imgIn.copyPixelsToBuffer(map);
//recycle the source bitmap, this will be no longer used.
imgIn.recycle();
System.gc();// try to force the bytes from the imgIn to be released
//Create a new bitmap to load the bitmap again. Probably the memory will be available.
imgIn = Bitmap.createBitmap(width, height, type);
map.position(0);
//load it back from temporary
imgIn.copyPixelsFromBuffer(map);
//close the temporary file and channel , then delete that also
channel.close();
randomAccessFile.close();
// delete the temp file
file.delete();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return imgIn;
}
#Override
public void onDraw(Canvas canvas) {
Display display = getWindowManager().getDefaultDisplay();
int width = display.getWidth();
int height = display.getHeight();
myBitmap = getResizedBitmap(myBitmap, height, width);
if (isFirstTime<3){
myBitmap = convertToMutable(myBitmap);
canvas.drawBitmap(myBitmap, 0, 0, null);
isFirstTime++;
}
for (Path path : _graphics) {
//canvas.drawPoint(graphic.x, graphic.y, mPaint);
canvas.drawPath(path, mPaint);
}
}
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
_thread.setRunning(true);
_thread.start();
}
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
boolean retry = true;
_thread.setRunning(false);
while (retry) {
try {
_thread.join();
retry = false;
} catch (InterruptedException e) {
// we will try it again and again...
}
}
}
}
class DrawingThread extends Thread {
private SurfaceHolder _surfaceHolder;
private DrawingPanel _panel;
private boolean _run = false;
public DrawingThread(SurfaceHolder surfaceHolder, DrawingPanel panel) {
_surfaceHolder = surfaceHolder;
_panel = panel;
}
public void setRunning(boolean run) {
_run = run;
}
public SurfaceHolder getSurfaceHolder() {
return _surfaceHolder;
}
#Override
public void run() {
Canvas c;
while (_run) {
c = null;
try {
c = _surfaceHolder.lockCanvas(null);
synchronized (_surfaceHolder) {
if (c!=null)
_panel.onDraw(c);
}
} finally {
// do this in a finally so that if an exception is thrown
// during the above, we don't leave the Surface in an
// inconsistent state
if (c != null) {
_surfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
}
}
This is main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:id="#+id/main_view">
</LinearLayout>
and menu.xml:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="#+id/save"
android:title="Save"/>
<item
android:id="#+id/clear"
android:title="Clear"/>
</menu>
Any help would be appreciated. Thanks!!
EDIT: I got it to work!! Here it is!
#Override
public void onCreate(Bundle savedInstanceState) {
mPanel = new DrawingPanel(this);
mPanel.setId(findId());
super.onCreate(savedInstanceState);
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
//https://stackoverflow.com/questions/10937659/android-image-doesnt-save-using-native-camera-app-on-nexus-s
File f = new File(Environment.getExternalStorageDirectory().getPath(), String.format("%d.jpg", System.currentTimeMillis()));
fileUri= Uri.fromFile(f);
intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
startActivityForResult(intent, IMAGE_CAPTURE);
setContentView(R.layout.main);
}
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu, menu);
return true;
}
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.save:
savePicture();
return true;
case R.id.clear:
// startActivity(new Intent(this, Clear.class));
return true;
default:
return super.onOptionsItemSelected(item);
}
}
public void savePicture(){
long now = System.currentTimeMillis();
OutputStream fos;
try {
//SAVE THIS ONE- replicate all paths
Canvas canvas = new Canvas(myBitmap);
for (Path path : _graphicsToSave)
{
canvas.drawPath(path, mPaint);
}
fos = new FileOutputStream(String.format(Environment.getExternalStorageDirectory().getAbsolutePath()+"/edited_%d.jpg",now));
myBitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
finish(); //close the activity.
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
//Detects request codes
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == IMAGE_CAPTURE && resultCode == RESULT_OK) {
Uri result;
result = fileUri;
//refresh storage
sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + Environment.getExternalStorageDirectory())));
try {
myBitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), result);
//resize the picture to match the screen size
Display display = getWindowManager().getDefaultDisplay();
int width = display.getWidth();
int height = display.getHeight();
myBitmap = getResizedBitmap(myBitmap, height, width);
startDrawing();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
else if (resultCode == Activity.RESULT_CANCELED){
}
}
//Source: http://thinkandroid.wordpress.com/2009/12/25/resizing-a-bitmap/
public Bitmap getResizedBitmap(Bitmap bm, int newHeight, int newWidth) {
int width = bm.getWidth();
int height = bm.getHeight();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
// create a matrix for the manipulation
Matrix matrix = new Matrix();
// resize the bit map
matrix.postScale(scaleWidth, scaleHeight);
// recreate the new Bitmap
Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, false);
return resizedBitmap;
}
public void startDrawing(){
setContentView(mPanel);
mPaint = new Paint();
mPaint.setDither(true);
mPaint.setColor(0xFFFFFF00);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(3);
}
public int findId(){
View v = findViewById(id);
while (v != null){
v = findViewById(++id);
}
return id++;
}
class DrawingPanel extends SurfaceView implements SurfaceHolder.Callback {
private DrawingThread _thread;
private Path path;
public DrawingPanel(Context context) {
super(context);
setDrawingCacheEnabled(true);
getHolder().addCallback(this);
_thread = new DrawingThread(getHolder(), this);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
synchronized (_thread.getSurfaceHolder()) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
path = new Path();
path.moveTo(event.getX(), event.getY());
path.lineTo(event.getX(), event.getY());
}else if(event.getAction() == MotionEvent.ACTION_MOVE){
path.lineTo(event.getX(), event.getY());
if(_graphics.size() > 0) {
_graphics.remove(_graphics.size() - 1);
}
_graphics.add(path);
}else if(event.getAction() == MotionEvent.ACTION_UP){
path.lineTo(event.getX(), event.getY());
_graphics.remove(_graphics.size() - 1);
_graphics.add(path);
_graphicsToSave.add(path);
}
return true;
}
}
#Override
public void onDraw(Canvas canvas) {
if (isFirstTime<3){
canvas.drawBitmap(myBitmap, 0, 0, null);
isFirstTime++;
}
for (Path path : _graphics) {
canvas.drawPath(path, mPaint);
}
}
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
_thread.setRunning(true);
_thread.start();
}
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
boolean retry = true;
_thread.setRunning(false);
while (retry) {
try {
_thread.join();
retry = false;
} catch (InterruptedException e) {
// we will try it again and again...
}
}
}
}
class DrawingThread extends Thread {
private SurfaceHolder _surfaceHolder;
private DrawingPanel _panel;
private boolean _run = false;
public DrawingThread(SurfaceHolder surfaceHolder, DrawingPanel panel) {
_surfaceHolder = surfaceHolder;
_panel = panel;
}
public void setRunning(boolean run) {
_run = run;
}
public SurfaceHolder getSurfaceHolder() {
return _surfaceHolder;
}
#Override
public void run() {
Canvas c;
while (_run) {
c = null;
try {
c = _surfaceHolder.lockCanvas(null);
synchronized (_surfaceHolder) {
if (c!=null)
_panel.onDraw(c);
}
} finally {
// do this in a finally so that if an exception is thrown
// during the above, we don't leave the Surface in an
// inconsistent state
if (c != null) {
_surfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
}
}
View mView;
mView.setDrawingCacheEnabled(true);
Bitmap newimg = mView.getDrawingCache();
Please get a bitmap and try saving that bitmap. Let me know you need more help
To draw something, you need 4 basic components: A Bitmap to hold the pixels, a Canvas to host the draw calls (writing into the bitmap), a drawing primitive (e.g. Rect, Path, text, Bitmap), and a paint (to describe the colors and styles for the drawing).
Your current pasted code code shows that you are saving the myBitmap but it doesn't show whether you are setting the myBitmap as the bitmap for drawing or not. May be it's not the complete code.
The only reason i could think of for this problem is that you are not drawing on the bitmap that you are saving. Here is a link to the code I created for a finger paint. You can refer to this code to see how the bitmap is provided to the canvas and how path is drawn on the bitmap.