The user registration of any kind of application selecting user profile picture is unavoidable. I am always meshed up with setting up an image to the profile picture image view. After a long struggle, I figured out the proper way to handle image byte from Gallery and custom camera using Camera API. I would like to share the complete flow one by one.
We have two options to pick a picture from Gallery and to take the picture from Custom Camera.
By passing camera id either back or front facing to open method to initiate Camera from surfaceCreated() method of SurfaceView. The camera preview is started at surfaceChanged().
The raw image byte needs to do fine tuning based on CameraID so that the taken picture is assigned to ImageView without inverted.
I have uploaded the full source code in GitHub,
https://github.com/JayaprakashR-Zealot/SetProfilePicture
Kindly raise your queries in the comment section.
Happy Coding!
Cheers!
We have two options to pick a picture from Gallery and to take the picture from Custom Camera.
From Gallery
By using Intent, We open device gallery.
Intent intent = new Intent(); intent.setType("image/*"); intent.setAction(Intent.ACTION_GET_CONTENT); startActivityForResult(Intent.createChooser(intent, "Select Picture"), PICK_IMAGE_REQUEST);
Once user pick their desired picture from Gallery, the image byte is received at onActivityResult()
if (requestCode == PICK_IMAGE_REQUEST && resultCode == RESULT_OK && data != null && data.getData() != null) { try{ Uri selectedImage = data.getData(); InputStream imageStream = null; try { imageStream = getContentResolver().openInputStream( selectedImage); } catch (FileNotFoundException e) { e.printStackTrace(); } Bitmap bmp = BitmapFactory.decodeStream(imageStream); ByteArrayOutputStream stream = new ByteArrayOutputStream(); bmp.compress(Bitmap.CompressFormat.JPEG, 50, stream); imgProfilePic.setImageBitmap(bmp); } catch (Exception ex){ Log.e(TAG,"IOException:"+ex.getLocalizedMessage()); finish(); } }
The selected image Uri is decoded into Bitmap with compression using ByteArrayOutputStream. The resulted Bitmap is assigned to ImageView.
From Custom Camera
private void checkCameraPermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { Log.d(TAG, "SDK >= 23"); if (this.checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { Log.d(TAG, "Request permission"); ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, CAMERA_PERMISSION_REQUEST_CODE); } else { Log.d(TAG, "Permission granted: taking pic"); openCamera(); } } else { Log.d(TAG, "Android < 6.0"); openCamera(); } } private void openCamera(){ Intent intent=new Intent(ProfileActivity.this,AndroidCameraUtils.class); startActivityForResult(intent,TAKE_IMAGE_REQUEST); }
We have to check Camera permission before start the Custom camera activity. The SurfaceView is placed in the XML layout. We have to implement SurfaceHolder.Callback at Custom Camera activity.
@Overridepublic void surfaceCreated(SurfaceHolder holder) { // TODO Auto-generated method stub camera = Camera.open(Camera.CameraInfo.CAMERA_FACING_FRONT); } @Overridepublic void surfaceDestroyed(SurfaceHolder holder) { // TODO Auto-generated method stub camera.stopPreview(); camera.release(); camera = null; previewing = false; }@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { if(previewing){ camera.stopPreview(); previewing = false; } if(isBackCameraClicked){ camera.release(); camera = null; camera = Camera.open(Camera.CameraInfo.CAMERA_FACING_FRONT); } if (camera != null){ try { setCameraDisplayOrientation(AndroidCameraUtils.this, Camera.CameraInfo.CAMERA_FACING_FRONT, camera); camera.setPreviewDisplay(surfaceHolder); camera.startPreview(); previewing = true; } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
By passing camera id either back or front facing to open method to initiate Camera from surfaceCreated() method of SurfaceView. The camera preview is started at surfaceChanged().
With JPGcallback, takePicture() method is called. The image byte is received from
public void onTakePictureClick(View v){ camera.takePicture(null,null, myPictureCallback_JPG); }
myPictureCallback_JPG.
Camera.PictureCallback myPictureCallback_JPG = new Camera.PictureCallback(){
@Override public void onPictureTaken(byte[] arg0, Camera arg1) {
// TODO Auto-generated method stub if(arg0!=null){
saveCameraImage(arg0);
}
}};
The raw image byte needs to do fine tuning based on CameraID so that the taken picture is assigned to ImageView without inverted.
To perform matrix rotations/mirrors depending on camera that took the photo then adding matrix value to Bitmap and create a new one. Finally, 528*528 Bitmap is cropped out. As per my understanding, this method is necessary to receive proper Bitmap data from Image byte.private Bitmap makeSquare(byte[] data, int cameraID) { int width; int height; Matrix matrix = new Matrix(); Camera.CameraInfo info = new Camera.CameraInfo(); Camera.getCameraInfo(cameraID, info); // Convert ByteArray to Bitmap Bitmap bitPic = BitmapFactory.decodeByteArray(data, 0, data.length); width = bitPic.getWidth(); height = bitPic.getHeight(); if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { float[] mirrorY = { -1, 0, 0, 0, 1, 0, 0, 0, 1}; Matrix matrixMirrorY = new Matrix(); matrixMirrorY.setValues(mirrorY); matrix.postConcat(matrixMirrorY); } matrix.postRotate(90); // Create new Bitmap out of the old one Bitmap bitPicFinal = Bitmap.createBitmap(bitPic, 0, 0, width, height,matrix, true); bitPic.recycle(); int desWidth; int desHeight; desWidth = bitPicFinal.getWidth(); desHeight = desWidth; Bitmap croppedBitmap = Bitmap.createBitmap(bitPicFinal, 0,bitPicFinal. getHeight() / 2 - bitPicFinal.getWidth() / 2,desWidth, desHeight); croppedBitmap = Bitmap.createScaledBitmap(croppedBitmap, 528, 528, true); return croppedBitmap; }
I have uploaded the full source code in GitHub,
https://github.com/JayaprakashR-Zealot/SetProfilePicture
Kindly raise your queries in the comment section.
Happy Coding!
Cheers!