Ok so I have a class that extends SurfaceView and overrides
surfaceChanged - just calls startPreview
surfaceCreated - opens camera, edits params *, sets surfaceHolder
surfaceDestroyed - calls stopPreview, release camera
this all work great because when the orientation is Portrait:
from surfaceCreated *
m_camera = Camera.open(); Camera.Parameters p = m_camera.getParameters(); if (getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE) { p.set("orientation", "portrait"); // CameraApi is a wrapper to check for backwards compatibility if (CameraApi.isSetRotationSupported()) { CameraApi.setRotation(p, 90); } }
However, everytime the orientation changes it calls Camera.open()... which as you may know is quite an expensive operation, causing the transitions to be not so smooth.
When i force the orientation to landscape, the preview is great. Create only gets called once which works because the preview is in landscape the camera is always what the user sees. However, I need a way to set the orientation of the actual picture taken when in portrait. When I force landscape though, the surface never gets recreated and the parameters are never set when the camera is held in portrait.
So how can I do one of the following (exclusively)?
Hold onto m_camera between onDestroy and onCreate when orientation changes so that the transition is smooth
Force landscape and detect orientation changes another way... rotating the final snaped picture if held in portrait.
Also, if I am off base can someone point me in a better direction? Thank you.
The expression deviceOrientationDegrees * sign + 360 converts device rotation from counterclockwise to clockwise for back-facing cameras (for example, converting 270 degrees counterclockwise to 90 degrees clockwise).
PreviewView is a subclass of FrameLayout . To display the camera feed, it uses either a SurfaceView or TextureView , provides a preview surface to the camera when it's ready, tries to keep it valid as long as the camera is using it, and when released prematurely, provides a new surface if the camera is still in use.
Check orientation as your way and use this to change orientation: setRequestedOrientation (ActivityInfo. SCREEN_ORIENTATION_PORTRAIT); I hope it helps.
Provides a dedicated drawing surface embedded inside of a view hierarchy. You can control the format of this surface and, if you like, its size; the SurfaceView takes care of placing the surface at the correct location on the screen.
The way I implemented it:
private Camera mCamera; private OrientationEventListener mOrientationEventListener; private int mOrientation = -1; private static final int ORIENTATION_PORTRAIT_NORMAL = 1; private static final int ORIENTATION_PORTRAIT_INVERTED = 2; private static final int ORIENTATION_LANDSCAPE_NORMAL = 3; private static final int ORIENTATION_LANDSCAPE_INVERTED = 4; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // force Landscape layout setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR | ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); /* Your other initialization code here */ } @Override protected void onResume() { super.onResume(); if (mOrientationEventListener == null) { mOrientationEventListener = new OrientationEventListener(this, SensorManager.SENSOR_DELAY_NORMAL) { @Override public void onOrientationChanged(int orientation) { // determine our orientation based on sensor response int lastOrientation = mOrientation; if (orientation >= 315 || orientation < 45) { if (mOrientation != ORIENTATION_PORTRAIT_NORMAL) { mOrientation = ORIENTATION_PORTRAIT_NORMAL; } } else if (orientation < 315 && orientation >= 225) { if (mOrientation != ORIENTATION_LANDSCAPE_NORMAL) { mOrientation = ORIENTATION_LANDSCAPE_NORMAL; } } else if (orientation < 225 && orientation >= 135) { if (mOrientation != ORIENTATION_PORTRAIT_INVERTED) { mOrientation = ORIENTATION_PORTRAIT_INVERTED; } } else { // orientation <135 && orientation > 45 if (mOrientation != ORIENTATION_LANDSCAPE_INVERTED) { mOrientation = ORIENTATION_LANDSCAPE_INVERTED; } } if (lastOrientation != mOrientation) { changeRotation(mOrientation, lastOrientation); } } }; } if (mOrientationEventListener.canDetectOrientation()) { mOrientationEventListener.enable(); } } @Override protected void onPause() { super.onPause(); mOrientationEventListener.disable(); } /** * Performs required action to accommodate new orientation * @param orientation * @param lastOrientation */ private void changeRotation(int orientation, int lastOrientation) { switch (orientation) { case ORIENTATION_PORTRAIT_NORMAL: mSnapButton.setImageDrawable(getRotatedImage(android.R.drawable.ic_menu_camera, 270)); mBackButton.setImageDrawable(getRotatedImage(android.R.drawable.ic_menu_revert, 270)); Log.v("CameraActivity", "Orientation = 90"); break; case ORIENTATION_LANDSCAPE_NORMAL: mSnapButton.setImageResource(android.R.drawable.ic_menu_camera); mBackButton.setImageResource(android.R.drawable.ic_menu_revert); Log.v("CameraActivity", "Orientation = 0"); break; case ORIENTATION_PORTRAIT_INVERTED: mSnapButton.setImageDrawable(getRotatedImage(android.R.drawable.ic_menu_camera, 90)); mBackButton.setImageDrawable(getRotatedImage(android.R.drawable.ic_menu_revert, 90)); Log.v("CameraActivity", "Orientation = 270"); break; case ORIENTATION_LANDSCAPE_INVERTED: mSnapButton.setImageDrawable(getRotatedImage(android.R.drawable.ic_menu_camera, 180)); mBackButton.setImageDrawable(getRotatedImage(android.R.drawable.ic_menu_revert, 180)); Log.v("CameraActivity", "Orientation = 180"); break; } } /** * Rotates given Drawable * @param drawableId Drawable Id to rotate * @param degrees Rotate drawable by Degrees * @return Rotated Drawable */ private Drawable getRotatedImage(int drawableId, int degrees) { Bitmap original = BitmapFactory.decodeResource(getResources(), drawableId); Matrix matrix = new Matrix(); matrix.postRotate(degrees); Bitmap rotated = Bitmap.createBitmap(original, 0, 0, original.getWidth(), original.getHeight(), matrix, true); return new BitmapDrawable(rotated); }
And then in your PictureCallback set metadata to indicate rotation level:
private Camera.PictureCallback mJpegCallback = new Camera.PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) { try { // Populate image metadata ContentValues image = new ContentValues(); // additional picture metadata image.put(Media.DISPLAY_NAME, [picture name]); image.put(Media.MIME_TYPE, "image/jpg"); image.put(Media.TITLE, [picture title]); image.put(Media.DESCRIPTION, [picture description]); image.put(Media.DATE_ADDED, [some time]); image.put(Media.DATE_TAKEN, [some time]); image.put(Media.DATE_MODIFIED, [some time]); // do not rotate image, just put rotation info in switch (mOrientation) { case ORIENTATION_PORTRAIT_NORMAL: image.put(Media.ORIENTATION, 90); break; case ORIENTATION_LANDSCAPE_NORMAL: image.put(Media.ORIENTATION, 0); break; case ORIENTATION_PORTRAIT_INVERTED: image.put(Media.ORIENTATION, 270); break; case ORIENTATION_LANDSCAPE_INVERTED: image.put(Media.ORIENTATION, 180); break; } // store the picture Uri uri = getContentResolver().insert( Media.EXTERNAL_CONTENT_URI, image); try { Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); OutputStream out = getContentResolver().openOutputStream( uri); boolean success = bitmap.compress( Bitmap.CompressFormat.JPEG, 75, out); out.close(); if (!success) { finish(); // image output failed without any error, // silently finish } } catch (Exception e) { e.printStackTrace(); // handle exceptions } mResultIntent = new Intent(); mResultIntent.setData(uri); } catch (Exception e) { e.printStackTrace(); } finish(); } };
I hope it helps.
UPDATE Now when landscape based devices are appearing an additional check for it is required in OrientationEventListener.
Display display = ((WindowManager)getSystemService(WINDOW_SERVICE)).getDefaultDisplay(); if (display.getOrientation() == Surface.ROTATION_0) { // landscape oriented devices } else { // portrait oriented device }
Full code (a bit wasteful by LC, but easily demonstrates the approach)
@Override public void onOrientationChanged(int orientation) { // determine our orientation based on sensor response int lastOrientation = mOrientation; Display display = ((WindowManager)getSystemService(WINDOW_SERVICE)).getDefaultDisplay(); if (display.getOrientation() == Surface.ROTATION_0) { // landscape oriented devices if (orientation >= 315 || orientation < 45) { if (mOrientation != ORIENTATION_LANDSCAPE_NORMAL) { mOrientation = ORIENTATION_LANDSCAPE_NORMAL; } } else if (orientation < 315 && orientation >= 225) { if (mOrientation != ORIENTATION_PORTRAIT_INVERTED) { mOrientation = ORIENTATION_PORTRAIT_INVERTED; } } else if (orientation < 225 && orientation >= 135) { if (mOrientation != ORIENTATION_LANDSCAPE_INVERTED) { mOrientation = ORIENTATION_LANDSCAPE_INVERTED; } } else if (orientation <135 && orientation > 45) { if (mOrientation != ORIENTATION_PORTRAIT_NORMAL) { mOrientation = ORIENTATION_PORTRAIT_NORMAL; } } } else { // portrait oriented devices if (orientation >= 315 || orientation < 45) { if (mOrientation != ORIENTATION_PORTRAIT_NORMAL) { mOrientation = ORIENTATION_PORTRAIT_NORMAL; } } else if (orientation < 315 && orientation >= 225) { if (mOrientation != ORIENTATION_LANDSCAPE_NORMAL) { mOrientation = ORIENTATION_LANDSCAPE_NORMAL; } } else if (orientation < 225 && orientation >= 135) { if (mOrientation != ORIENTATION_PORTRAIT_INVERTED) { mOrientation = ORIENTATION_PORTRAIT_INVERTED; } } else if (orientation <135 && orientation > 45) { if (mOrientation != ORIENTATION_LANDSCAPE_INVERTED) { mOrientation = ORIENTATION_LANDSCAPE_INVERTED; } } } if (lastOrientation != mOrientation) { changeRotation(mOrientation, lastOrientation); } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With