I am making an application where I have a camera inside of a viewPager. I am wondering what would best be suited to "pause" and "resume" the camera so it doesn't hog resources when it is pre-loaded. I have the feeling that stopPreview is better suited for this as it does not release the camera but keeps it however it doesn't display the camera which is the main reason it hogs resources.
Enter & exit application: startCamera() & releaseCamera()
Tab visible & not visible: startPreview() & stop Preview()
Would this be a good rule of thumb?
I had a similar situation. :
If I kept camera (in ViewPager) in on state, the swipe were clunky and OOM exceptions were frequent.
Two options came in my mind:
shift the entire instance in a different thread
OR
use stopPreview() and startPreview()
I went with the second one :
However, instead of doing this on Fragment lifecycle callbacks I gave a button on the fragment which toggled the preview. Reason being, if user is swiping very fast, you can still receive OOm exception since the preview calls will be queued, especially if there are very few fragments in the viewPager.
In essence Release camera onPause(), acquire camera in onResume() and give a groovy button in fragment which will toggle your Preview on the surface!
Hello Karl I had the same things need to implement in view pager. I have circular viewer in which one fragment has the camera fragment. I want to handle the camera preview in such a way so it should not consume the camera resource.
As you know android view pager default load two fragment in to the memory. We implemented the view pager change listener and call the fragment method to start and stop the preview. even also destroy the camera preview in on destroy method of fragment.
class ViewPagerChangeListener implements ViewPager.OnPageChangeListener {
int currentPosition = DEFAULT_FRAGMENT;
#Override
public void onPageScrollStateChanged(int state) {
TimberLogger.d(TAG, "onPageScrollStateChanged");
}
#Override
public void onPageScrolled(int index, float arg1, int arg2) {
TimberLogger.d(TAG, "onPageScrolled" + index);
}
#Override
public void onPageSelected(int position) {
mWatchPosition = position;
TimberLogger.d(TAG, "onPageSelected" + mWatchPosition);
int newPosition = 0;
if (position > 4) {
newPosition = position;
}
TimberLogger.d(TAG, "newPosition" + newPosition);
/**
* Listener knows the new position and can call the interface method
* on new Fragment with the help of PagerAdapter. We can here call
* onResumeFragment() for new fragment and onPauseFragment() on the
* current one.
*/
// new fragment onResume
loadedFragment(newPosition).onResumeFragment();
// current fragment onPuase called
loadedFragment(currentPosition).onPauseFragment();
currentPosition = newPosition;
TimberLogger.d(TAG, "currentPosition" + currentPosition);
}
}
See the two method onResumeFragment and onPuaseFragment this two are the custom function each view pager fragment implements. In view pager change event we call the pause of current fragment and onResume of the new fragment.
// new fragment onResume
loadedFragment(newPosition).onResumeFragment();
// current fragment onPuase called
loadedFragment(currentPosition).onPauseFragment();
You can write your camera start preview inside custom method onResumeFragment and stop preview in onPauseFragment and also make sure you should override the onDestory() method of your camera fragment for release the camera resources.
The best solution will be to startCamera() in onResume(), and release it in onPause(), so you can handle, that camera is not free in onResume().
In ViewPager you can startPreview(), when fragment with it is selected, and stopPreview() otherwise. Also u can startPreview() in onCreateView() and stopPreview() in onDestroyView() in fragment.
This takes care of most of the operations CameraPreview.java:
package com.example.fela;
import android.app.Activity;
import android.content.Context;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.hardware.Camera.ErrorCallback;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.Size;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.WindowManager;
import java.util.List;
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder holder;
private Camera camera;
private int cameraId;
private Activity activity;
private CameraPreviewActivityInterface activityInterface;
public CameraPreview(Activity activity, int cameraId) {
super(activity);
try {
activityInterface = (CameraPreviewActivityInterface) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement ExampleFragmentCallbackInterface ");
}
this.activity = activity;
this.cameraId = cameraId;
holder = getHolder();
holder.addCallback(this);
// deprecated setting, but required on Android versions prior to 3.0
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void surfaceCreated(SurfaceHolder holder) {}
/**
* custom camera tweaks and startPreview()
*/
public void refreshCamera() {
if (holder.getSurface() == null || camera == null) {
// preview surface does not exist, camera not opened created yet
return;
}
Log.i(null, "CameraPreview refreshCamera()");
// stop preview before making changes
try {
camera.stopPreview();
} catch (Exception e) {
// ignore: tried to stop a non-existent preview
}
int rotation = ((WindowManager) activity.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();
int degrees = 0;
// specifically for back facing camera
switch (rotation) {
case Surface.ROTATION_0:
degrees = 90;
break;
case Surface.ROTATION_90:
degrees = 0;
break;
case Surface.ROTATION_180:
degrees = 270;
break;
case Surface.ROTATION_270:
degrees = 180;
break;
}
camera.setDisplayOrientation(degrees);
setCamera(camera);
try {
camera.setPreviewDisplay(holder);
camera.startPreview();
} catch (Exception e) {
// this error is fixed in the camera Error Callback (Error 100)
Log.d(VIEW_LOG_TAG, "Error starting camera preview: " + e.getMessage());
}
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
Log.i(null, "CameraPreview surfaceChanged()");
// if your preview can change or rotate, take care of those events here.
// make sure to stop the preview before resizing or reformatting it.
// do not start the camera if the tab isn't visible
if(activityInterface.getCurrentPage() == 1)
startCamera();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {}
public Camera getCameraInstance() {
Camera camera = Camera.open();
// parameters for camera
Parameters params = camera.getParameters();
params.set("jpeg-quality", 100);
params.set("iso", "auto");
params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
params.setPictureFormat(PixelFormat.JPEG);
// set the image dimensions
List<Size> sizes = params.getSupportedPictureSizes();
int max = 0, width = 0, height = 0;
for(Size size : sizes) {
if(max < (size.width*size.height)) {
max = (size.width*size.height);
width = size.width;
height = size.height;
}
}
params.setPictureSize(width, height);
camera.setParameters(params);
// primarily used to fix Error 100
camera.setErrorCallback(new ErrorCallback() {
#Override
public void onError(int error, Camera camera) {
if(error == Camera.CAMERA_ERROR_SERVER_DIED) {
releaseCamera();
startCamera();
}
}
});
return camera;
}
/**
* intitialize a new camera
*/
protected void startCamera() {
if(getCamera() == null)
setCamera(getCameraInstance());
refreshCamera();
}
/**
* release camera so other applications can utilize the camera
*/
protected void releaseCamera() {
// if already null then the camera has already been released before
if (getCamera() != null) {
getCamera().release();
setCamera(null);
}
}
public Camera getCamera() {
return camera;
}
public void setCamera(Camera camera) {
this.camera = camera;
}
public void setCameraId(int cameraId) {
this.cameraId = cameraId;
}
/**
* get the current viewPager page
*/
public interface CameraPreviewActivityInterface {
public int getCurrentPage();
}
}
In my FragmentCamera.java file:
private CameraPreview cameraPreview;
// code...
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// code...
cameraPreview = new CameraPreview(getActivity(), cameraId);
previewLayout.addView(cameraPreview);
// code...
}
// code...
#Override
public void onPause() {
super.onPause();
cameraPreview.releaseCamera();
}
#Override
public void onResume() {
super.onResume();
cameraPreview.startCamera();
}
protected void fragmentVisible() {
onResume();
}
protected void fragmentNotVisible() {
onPause();
}
And the MainActivity.java file (implements CameraPreviewActivityInterface):
viewPager.setOnPageChangeListener(new OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
currentPage = position;
if (currentPage == 1) {
fragmentCamera.fragmentVisible();
} else {
fragmentCamera.fragmentNotVisible();
}
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
#Override
public int getCurrentPage() {
return currentPage;
}
Related
My problem:
For some requirements i need two different xml layouts for my activity:
One for Landscape mode.
And another one for Landscape-reverse mode (upside-down of Landscape).
Unfortunately Android doesn't allow creating a separate layout for landscape-reverse (like we can do for portrait and landscape with layout-land and layout-port).
AFAIK, the only way is to change the activity-xml from java code.
What i've tried:
1) Override onConfigurationChanged() method to detect orientation changes, but i can't figure out if it's Landscape or Landscape-reverse:
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
Log.d("TEST","Landscape");
}
}
( Whith android:configChanges="keyboardHidden|orientation|screenSize|layoutDirection" in my activity tag in manifest)
2) Use an OrientationEventListener with SENSOR_DELAY_NORMAL as suggested in this answer but the device orientation changes before entering my if blocks, so i get a delayed update of the view:
mOrientationEventListener = new OrientationEventListener(this, SensorManager.SENSOR_DELAY_NORMAL){
#Override
public void onOrientationChanged(int orientation) {
if (orientation==0){
Log.e("TEST", "orientation-Portrait = "+orientation);
} else if (orientation==90){
Log.e("TEST", "orientation-Landscape = "+orientation);
} else if(orientation==180){
Log.e("TEST", "orientation-Portrait-rev = "+orientation);
}else if (orientation==270){
Log.e("TEST", "orientation-Landscape-rev = "+orientation);
} else if (orientation==360){
Log.e("TEST", "orientation-Portrait= "+orientation);
}
}};
My question:
Is there a better solution to change activity-layout between "Landscape" and "Landscape-reverse" orientation?
Any suggestions are highly appreciated.
Are you trying as suggested here?. You may handle an event with a different types of configuration reverse and standart by using activity attribute sensorLandscape
EDITED: Try to use Display.getOrientation as described here http://android-developers.blogspot.in/2010/09/one-screen-turn-deserves-another.html
And do not forget to set configChanges flag on activity in manifest to handle changes manualy in onConfigurationChanges().
So it seems like only way to do this is to listen SensorManager as frequently as possible.
SensorManager sensorMan = (SensorManager)getSystemService(SENSOR_SERVICE);
Sensor sensor = sensorMan.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
sensorMan.registerListener(...)
in order to achive this goal, you need to implement a rotation listener,
you need also to know that android destroy objects and recreate them
to load your layouts, values... based on configuration qualifiers
STEP 01: create a Java interface [rotationCallbackFn]
public interface rotationCallbackFn {
void onRotationChanged(int lastRotation, int newRotation);
}
STEP 02: create a Java class [rotationListenerHelper]
import android.content.Context;
import android.hardware.SensorManager;
import android.view.OrientationEventListener;
import android.view.WindowManager;
public class rotationListenerHelper {
private int lastRotation;
private WindowManager windowManager;
private OrientationEventListener orientationEventListener;
private rotationCallbackFn callback;
public rotationListenerHelper() {
}
public void listen(Context context, rotationCallbackFn callback) {
// registering the listening only once.
stop();
context = context.getApplicationContext();
this.callback = callback;
this.windowManager = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
this.orientationEventListener = new OrientationEventListener(context, SensorManager.SENSOR_DELAY_NORMAL) {
#Override
public void onOrientationChanged(int orientation) {
WindowManager localWindowManager = windowManager;
rotationCallbackFn localCallback = rotationListenerHelper.this.callback;
if(windowManager != null && localCallback != null) {
int newRotation = localWindowManager.getDefaultDisplay().getRotation();
if (newRotation != lastRotation) {
localCallback.onRotationChanged(lastRotation, newRotation);
lastRotation = newRotation;
}
}
}
};
this.orientationEventListener.enable();
lastRotation = windowManager.getDefaultDisplay().getRotation();
}
public void stop() {
if(this.orientationEventListener != null) {
this.orientationEventListener.disable();
}
this.orientationEventListener = null;
this.windowManager = null;
this.callback = null;
}
}
STEP 03: add these statements to your mainActivity
// declaration
private rotationListenerHelper rotationListener = null;
private Context mContext;
//...
/* constructor ----------------------------------------------------------------*/
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = this;
final int curOrientation = getWindowManager().getDefaultDisplay().getRotation();
switch (curOrientation) {
case 0:
//. SCREEN_ORIENTATION_PORTRAIT
setContentView(R.layout.your_layout_port);
break;
//----------------------------------------
case 2:
//. SCREEN_ORIENTATION_REVERSE_PORTRAIT
setContentView(R.layout.your_layout_port_rev);
break;
//----------------------------------------
case 1:
//. SCREEN_ORIENTATION_LANDSCAPE
setContentView(R.layout.your_layout_land);
break;
//----------------------------------------
case 3:
//. SCREEN_ORIENTATION_REVERSE_LANDSCAPE
setContentView(R.layout.your_layout_land_rev);
break;
//----------------------------------------
} /*endSwitch*/
rotationListener = new rotationListenerHelper();
rotationListener.listen(mContext, rotationCB);
//...
}
private rotationCallbackFn rotationCB = new rotationCallbackFn() {
#Override
public void onRotationChanged(int lastRotation, int newRotation) {
Log.d(TAG, "onRotationChanged: last " + (lastRotation) +" new " + (newRotation));
/**
* no need to recreate activity if screen rotate from portrait to landscape
* android do the job in order to reload resources
*/
if (
(lastRotation == 0 && newRotation == 2) ||
(lastRotation == 2 && newRotation == 0) ||
(lastRotation == 1 && newRotation == 3) ||
(lastRotation == 3 && newRotation == 1)
)
((Activity) mContext).recreate();
}
};
/* destructor -----------------------------------------------------------------*/
#Override
protected void onDestroy() {
rotationListener.stop();
rotationListener = null;
Log.i(TAG, "onDestroy: activity destroyed");
super.onDestroy();
}
FINAL STEP : ENJOY
I am using QRCodeReaderView https://github.com/dlazaro66/QRCodeReaderView for implementing my own QR Code Scanner and it works well but the camera still starts slow (3-4 seconds) and I came up with the idea to pre start previewing the camera before using (keeping the camera open when the focus is on the fragment that has the button to start scanning so it could be opened right away when needed) and I tried everything but it seems like I don't understand the concept and it still starts slow.
Here is the code for the QRCodeReaderView:
import com.google.zxing.BinaryBitmap;
import com.google.zxing.ChecksumException;
import com.google.zxing.FormatException;
import com.google.zxing.NotFoundException;
import com.google.zxing.PlanarYUVLuminanceSource;
import com.google.zxing.Result;
import com.google.zxing.ResultPoint;
import com.google.zxing.client.android.camera.open.CameraManager;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.QRCodeReader;
import java.io.IOException;
public class QRCodeReaderView extends SurfaceView implements SurfaceHolder.Callback, Camera.PreviewCallback {
public interface OnQRCodeReadListener {
public void onQRCodeRead(String text, PointF[] points);
public void cameraNotFound();
public void QRCodeNotFoundOnCamImage();
}
private OnQRCodeReadListener mOnQRCodeReadListener;
private static final String TAG = QRCodeReaderView.class.getName();
private QRCodeReader mQRCodeReader;
private int mPreviewWidth;
private int mPreviewHeight;
private SurfaceHolder mHolder;
private CameraManager mCameraManager;
public QRCodeReaderView(Context context) {
super(context);
init();
}
public QRCodeReaderView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public void setOnQRCodeReadListener(OnQRCodeReadListener onQRCodeReadListener) {
mOnQRCodeReadListener = onQRCodeReadListener;
}
public CameraManager getCameraManager() {
return mCameraManager;
}
#SuppressWarnings("deprecation")
private void init() {
if (checkCameraHardware(getContext())) {
mCameraManager = new CameraManager(getContext());
mHolder = this.getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); // Need to set this flag despite it's deprecated
} else {
Log.e(TAG, "Error: Camera not found");
if (mOnQRCodeReadListener != null) {
mOnQRCodeReadListener.cameraNotFound();
}
}
}
/**
* *************************************************
* SurfaceHolder.Callback,Camera.PreviewCallback
* **************************************************
*/
#Override
public void surfaceCreated(SurfaceHolder holder) {
try {
// Indicate camera, our View dimensions
mCameraManager.openDriver(holder, this.getWidth(), this.getHeight());
} catch (IOException e) {
Log.w(TAG, "Can not openDriver: " + e.getMessage());
mCameraManager.closeDriver();
}
try {
mQRCodeReader = new QRCodeReader();
mCameraManager.startPreview();
} catch (Exception e) {
Log.e(TAG, "Exception: " + e.getMessage());
mCameraManager.closeDriver();
}
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d(TAG, "surfaceDestroyed");
mCameraManager.getCamera().setPreviewCallback(null);
mCameraManager.getCamera().stopPreview();
mCameraManager.getCamera().release();
mCameraManager.closeDriver();
}
// Called when camera take a frame
#Override
public void onPreviewFrame(byte[] data, Camera camera) {
PlanarYUVLuminanceSource source = mCameraManager.buildLuminanceSource(data, mPreviewWidth, mPreviewHeight);
HybridBinarizer hybBin = new HybridBinarizer(source);
BinaryBitmap bitmap = new BinaryBitmap(hybBin);
try {
Result result = mQRCodeReader.decode(bitmap);
// Notify we found a QRCode
if (mOnQRCodeReadListener != null) {
// Transform resultPoints to View coordinates
PointF[] transformedPoints = transformToViewCoordinates(result.getResultPoints());
mOnQRCodeReadListener.onQRCodeRead(result.getText(), transformedPoints);
}
} catch (ChecksumException e) {
Log.d(TAG, "ChecksumException");
e.printStackTrace();
} catch (NotFoundException e) {
// Notify QR not found
if (mOnQRCodeReadListener != null) {
mOnQRCodeReadListener.QRCodeNotFoundOnCamImage();
}
} catch (FormatException e) {
Log.d(TAG, "FormatException");
e.printStackTrace();
} finally {
mQRCodeReader.reset();
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Log.d(TAG, "surfaceChanged");
if (mHolder.getSurface() == null) {
Log.e(TAG, "Error: preview surface does not exist");
return;
}
//preview_width = width;
//preview_height = height;
mPreviewWidth = mCameraManager.getPreviewSize().x;
mPreviewHeight = mCameraManager.getPreviewSize().y;
mCameraManager.stopPreview();
mCameraManager.getCamera().setPreviewCallback(this);
mCameraManager.getCamera().setDisplayOrientation(90); // Portrait mode
mCameraManager.startPreview();
}
/**
* Transform result to surfaceView coordinates
* <p/>
* This method is needed because coordinates are given in landscape camera coordinates.
* Now is working but transform operations aren't very explained
* <p/>
* TODO re-write this method explaining each single value
*
* #return a new PointF array with transformed points
*/
private PointF[] transformToViewCoordinates(ResultPoint[] resultPoints) {
PointF[] transformedPoints = new PointF[resultPoints.length];
int index = 0;
if (resultPoints != null) {
float previewX = mCameraManager.getPreviewSize().x;
float previewY = mCameraManager.getPreviewSize().y;
float scaleX = this.getWidth() / previewY;
float scaleY = this.getHeight() / previewX;
for (ResultPoint point : resultPoints) {
PointF tmppoint = new PointF((previewY - point.getY()) * scaleX, point.getX() * scaleY);
transformedPoints[index] = tmppoint;
index++;
}
}
return transformedPoints;
}
/**
* Check if this device has a camera
*/
private boolean checkCameraHardware(Context context) {
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
// this device has a camera
return true;
} else if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT)) {
// this device has a front camera
return true;
} else if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)) {
// this device has any camera
return true;
} else {
// no camera on this device
return false;
}
}
}
and here is my fragment that uses it:
package com.breadwallet.presenter.fragments;
import com.breadwallet.R;
import com.breadwallet.presenter.activities.ScanResultActivity;
import com.breadwallet.tools.animation.SpringAnimator;
import com.breadwallet.tools.qrcode.QRCodeReaderView;
public class MainFragmentDecoder extends Fragment implements QRCodeReaderView.OnQRCodeReadListener {
public static final String TAG = "MainFragmentDecoder";
private boolean accessGranted = true;
private TextView myTextView;
private static QRCodeReaderView mydecoderview;
private ImageView camera_guide_image;
private Intent intent;
public static MainFragmentDecoder mainFragmentDecoder;
private RelativeLayout layout;
public MainFragmentDecoder() {
mainFragmentDecoder = this;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_decoder, container, false);
intent = new Intent(getActivity(), ScanResultActivity.class);
myTextView = (TextView) rootView.findViewById(R.id.exampleTextView);
camera_guide_image = (ImageView) rootView.findViewById(R.id.camera_guide_image);
SpringAnimator.showExpandCameraGuide(camera_guide_image);
// Inflate the layout for this fragment
return rootView;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
layout = (RelativeLayout) getView().findViewById(R.id.fragment_decoder_layout);
mydecoderview = new QRCodeReaderView(getActivity().getApplicationContext());
mydecoderview.setOnQRCodeReadListener(mainFragmentDecoder);
if (mydecoderview != null)
mydecoderview.getCameraManager().startPreview();
}
/**
* Called when a QR is decoded
* "text" : the text encoded in QR
* "points" : points where QR control points are placed
*/
#Override
public void onQRCodeRead(String text, PointF[] points) {
synchronized (this) {
if (accessGranted) {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
accessGranted = true;
}
}, 300);
accessGranted = false;
// Log.e(TAG, "Activity STARTED!!!!!");
intent.putExtra("result", text);
startActivity(intent);
}
}
}
// Called when your device have no camera
#Override
public void cameraNotFound() {
Log.d(TAG, "No Camera found!");
}
// Called when there's no QR codes in the camera preview image
#Override
public void QRCodeNotFoundOnCamImage() {
// Log.d(TAG, "No QR Code found!");
}
#Override
public void onResume() {
super.onResume();
new CameraOpenerTask().execute();
}
#Override
public void onPause() {
super.onPause();
Log.e(TAG, "In onPause");
mydecoderview.getCameraManager().stopPreview();
layout.removeView(mydecoderview);
}
private class CameraOpenerTask extends AsyncTask {
#Override
protected Object doInBackground(Object[] params) {
return null;
}
#Override
protected void onPostExecute(Object o) {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
layout.addView(mydecoderview, 0);
}
}, 1300);
Log.e(TAG, "The camera started");
}
}
public void stopCamera() {
if (mydecoderview != null) {
mydecoderview.getCameraManager().stopPreview();
}
mydecoderview = null;
}
}
I tried:
camera.StartPreview() earlier than using it.
pre-create the mydecoderview and then simply make it visible when
pressing the button but it still takes 3-4 seconds to start it.
You could try photo app in CyanogenMod (11 version) firmware, maybe this is just that you're searching for?
Download from somewhere it source and add its to your code.
As my previous question, I am trying "GLSurfaceView + TextureView" to show camera preview in one GLSurfaceView and multiple TextureViews, but facing some problems...
In GLSurfaceView render thread, I tried to share built-in EGLContext to TextureView, create a EGL surface by TextureView's surfaceTexture, then use GLES to draw on it.
#Override
public void onDrawFrame(final GL10 gl) {
// GLES draw on GLSurfaceView
renderToTextureView();
}
private void renderToTextureView() {
saveEGLState();
for(TextureViewItem item : mTextureViewItemList) {
item.render(mSavedEglContext);
}
restoreEGLState();
}
private void saveEGLState() {
mSavedEglDisplay = EGL14.eglGetCurrentDisplay();
mSavedEglContext = EGL14.eglGetCurrentContext();
mSavedEglDrawSurface = EGL14.eglGetCurrentSurface(EGL14.EGL_DRAW);
mSavedEglReadSurface = EGL14.eglGetCurrentSurface(EGL14.EGL_READ);
}
private void restoreEGLState() {
if (!EGL14.eglMakeCurrent(mSavedEglDisplay, mSavedEglDrawSurface, mSavedEglReadSurface, mSavedEglContext)) {
throw new RuntimeException("eglMakeCurrent failed");
}
}
public class TextureViewItem implements TextureView.SurfaceTextureListener {
private static EglCore sEglCore;
private WindowSurface mWindowSurface;
public void render(EGLContext sharedContext) {
if(mSavedSurfaceTexture == null) return;
getWindowSurface(sharedContext).makeCurrent();
// GLES draw on TextureView
getWindowSurface(sharedContext).swapBuffers();
}
private WindowSurface getWindowSurface(EGLContext sharedContext) {
if(sEglCore == null) {
sEglCore = new EglCore(sharedContext, EglCore.FLAG_TRY_GLES3);
}
if(mWindowSurface == null) {
mWindowSurface = new WindowSurface(mEglCore, mSavedSurfaceTexture);
}
return mWindowSurface;
}
#Override
public void onSurfaceTextureAvailable(SurfaceTexture st, int width, int height) {
if (mSavedSurfaceTexture == null) {
mSavedSurfaceTexture = st;
}
}
#Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture st) {
if (mWindowSurface != null) {
mWindowSurface.release();
}
if (sEglCore != null) {
sEglCore.release();
}
mSavedSurfaceTexture = null;
return true;
}
}
Everything works fine except press "back" key. I call GLSurfaceView's onPause() when the activity pauses, it caused swapBuffers (EGL14.eglSwapBuffers) won't return...
Some suspected logcat messages also
W/WindowManager(1077): Window freeze timeout expired.
I/WindowManager(1077): Screen frozen for +2s42ms due to Window ..
Anyone knows why? And any way to solve this problem?
Thanks.
I have a DrawerLayout which contains a FrameLayout and ListView in my app, I have to show the camera in the FrameLayout, I've done that fine (as following code) and the camera works correctly. The problem is when moving from portrait orientation to (right-Landscape orientation or left-landscape orientation), or vice versa, it take the mobile a long time to make changes, the problem does not appear when moving from right-Landscape orientation or left-landscape orientation or vice versa.
How could I make this operation as fast as I can?
public class ShowCamera extends SurfaceView implements SurfaceHolder.Callback{
//This ShowCamera class is a helpful class
private SurfaceHolder holdMe;
private Camera theCamera;
public ShowCamera(Context context,Camera camera) {
super(context);
theCamera = camera;
holdMe = getHolder();
holdMe.addCallback(this);
}
#Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
try {
theCamera.setPreviewDisplay(holder);
theCamera.startPreview();
synchronized (holder) {
}
} catch (Exception e) {}
}
#Override
public void surfaceDestroyed(SurfaceHolder arg0) {
holdMe.removeCallback(this);
theCamera.release();
}
}
Now the original class is:
public class MainActivity extends Activity {
private DrawerLayout mDrawerLayout;
private ListView mDrawerList;
String[] options = {"op1", "op2", "op3", "op4", "op5"}; //for the DrawerLayout
int[] icons = { 0,R.drawable.hospital,R.drawable.education,R.drawable.police,R.drawable.food}; //for the DrawerLayout
private Camera cameraObject;
private ShowCamera showCamera;
public static Camera getCamIfAvailable(){
Camera cam = null;
try { cam = Camera.open();}
catch (Exception e){}
return cam;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
cameraObject = getCamIfAvailable();
new Thread(new Runnable()
{
int rotation = getWindowManager().getDefaultDisplay().getRotation();
#Override
public void run()
{
switch(rotation){
case 0: // portrait
cameraObject.setDisplayOrientation(90);
break;
case 1: // left Landscape
cameraObject.setDisplayOrientation(0);
break;
case 3: //right Landscape
cameraObject.setDisplayOrientation(180);
break;
}
}
}).start();
showCamera = new ShowCamera(this, cameraObject);
FrameLayout preview = (FrameLayout) findViewById(R.id.content_frame);
preview.addView(showCamera);
//.
//.
//.
//.
//. Here, code for Drawerlayout no problrm
//.
//.
//.
}
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// Pass any configuration change to the drawer toggls
mDrawerToggle.onConfigurationChanged(newConfig);
// Checks the orientation of the screen
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
}
}
.
.
.
.
} // End of the class MainActivity
Also I have put in the manifest file the following as first answer mentioned here:
<activity android:name=".MyActivity"
android:configChanges="orientation|screenSize"
android:label="#string/app_name">
Any help will be appreciated.
Camera.open() is an heavy operation for the system. It is only triggered when a configuration change is happening, such as when you rotate your device from landscape to portrait and vice-versa.
When flipping the device (from portrait to reversed portrait, or from landscape to reversed landscape) this doesn't trigger a configuration change, only the screen rendering is flipped and therefore you don't call Camera.open() again.
So, you won't be able to make it faster (even though I recommend you to take a look at the systrace tool to see what's really happening when you rotate your screen).
But, I strongly encourage you to try calling the Camera.open() from a background Thread to avoid freezing the UI.
I have a DrawerLayout which contains a FrameLayout and ListView in my app, I have to show the camera in the FrameLayout, I've done that fine (as following code) and the camera works correctly. The problem is when moving from portrait orientation to (right-Landscape orientation or left-landscape orientation), or vice versa, it take the mobile a long time to make changes, the problem does not appear when moving from right-Landscape orientation or left-landscape orientation or vice versa.
How could I make this operation as fast as I can?
public class ShowCamera extends SurfaceView implements SurfaceHolder.Callback{
//This ShowCamera class is a helpful class
private SurfaceHolder holdMe;
private Camera theCamera;
public ShowCamera(Context context,Camera camera) {
super(context);
theCamera = camera;
holdMe = getHolder();
holdMe.addCallback(this);
}
#Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
try {
theCamera.setPreviewDisplay(holder);
theCamera.startPreview();
synchronized (holder) {
}
} catch (Exception e) {}
}
#Override
public void surfaceDestroyed(SurfaceHolder arg0) {
holdMe.removeCallback(this);
theCamera.release();
}
}
Now the original class is:
public class MainActivity extends Activity {
private DrawerLayout mDrawerLayout;
private ListView mDrawerList;
String[] options = {"op1", "op2", "op3", "op4", "op5"}; //for the DrawerLayout
int[] icons = { 0,R.drawable.hospital,R.drawable.education,R.drawable.police,R.drawable.food}; //for the DrawerLayout
private Camera cameraObject;
private ShowCamera showCamera;
public static Camera getCamIfAvailable(){
Camera cam = null;
try { cam = Camera.open();}
catch (Exception e){}
return cam;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
cameraObject = getCamIfAvailable();
new Thread(new Runnable()
{
int rotation = getWindowManager().getDefaultDisplay().getRotation();
#Override
public void run()
{
switch(rotation){
case 0: // portrait
cameraObject.setDisplayOrientation(90);
break;
case 1: // left Landscape
cameraObject.setDisplayOrientation(0);
break;
case 3: //right Landscape
cameraObject.setDisplayOrientation(180);
break;
}
}
}).start();
showCamera = new ShowCamera(this, cameraObject);
FrameLayout preview = (FrameLayout) findViewById(R.id.content_frame);
preview.addView(showCamera);
//.
//.
//.
//.
//. Here, code for Drawerlayout no problrm
//.
//.
//.
}
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// Pass any configuration change to the drawer toggls
mDrawerToggle.onConfigurationChanged(newConfig);
// Checks the orientation of the screen
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
}
}
.
.
.
.
} // End of the class MainActivity
Also I have put in the manifest file the following as first answer mentioned here:
<activity android:name=".MyActivity"
android:configChanges="orientation|screenSize"
android:label="#string/app_name">
Any help will be appreciated.
Camera.open() is an heavy operation for the system. It is only triggered when a configuration change is happening, such as when you rotate your device from landscape to portrait and vice-versa.
When flipping the device (from portrait to reversed portrait, or from landscape to reversed landscape) this doesn't trigger a configuration change, only the screen rendering is flipped and therefore you don't call Camera.open() again.
So, you won't be able to make it faster (even though I recommend you to take a look at the systrace tool to see what's really happening when you rotate your screen).
But, I strongly encourage you to try calling the Camera.open() from a background Thread to avoid freezing the UI.