diff --git a/modules/java/generator/android/java/org/opencv/android/CameraActivity.java b/modules/java/generator/android/java/org/opencv/android/CameraActivity.java new file mode 100644 index 0000000000..ea066048b1 --- /dev/null +++ b/modules/java/generator/android/java/org/opencv/android/CameraActivity.java @@ -0,0 +1,60 @@ +package org.opencv.android; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Build; +import android.util.AttributeSet; +import android.view.View; + +import java.util.ArrayList; +import java.util.List; + +import static android.Manifest.permission.CAMERA; + +public class CameraActivity extends Activity { + + private static final int CAMERA_PERMISSION_REQUEST_CODE = 200; + + protected List getCameraViewList() { + return new ArrayList(); + } + + protected void onCameraPermissionGranted() { + List cameraViews = getCameraViewList(); + if (cameraViews == null) { + return; + } + for (CameraBridgeViewBase cameraBridgeViewBase: cameraViews) { + if (cameraBridgeViewBase != null) { + cameraBridgeViewBase.setCameraPermissionGranted(); + } + } + } + + @Override + protected void onStart() { + super.onStart(); + boolean havePermission = true; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (checkSelfPermission(CAMERA) != PackageManager.PERMISSION_GRANTED) { + requestPermissions(new String[]{CAMERA}, CAMERA_PERMISSION_REQUEST_CODE); + havePermission = false; + } + } + if (havePermission) { + onCameraPermissionGranted(); + } + } + + @Override + @TargetApi(Build.VERSION_CODES.M) + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + if (requestCode == CAMERA_PERMISSION_REQUEST_CODE && grantResults.length > 0 + && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + onCameraPermissionGranted(); + } + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + } +} diff --git a/modules/java/generator/android/java/org/opencv/android/CameraBridgeViewBase.java b/modules/java/generator/android/java/org/opencv/android/CameraBridgeViewBase.java index 681b7ab974..4ee14e008f 100644 --- a/modules/java/generator/android/java/org/opencv/android/CameraBridgeViewBase.java +++ b/modules/java/generator/android/java/org/opencv/android/CameraBridgeViewBase.java @@ -48,6 +48,7 @@ public abstract class CameraBridgeViewBase extends SurfaceView implements Surfac protected int mPreviewFormat = RGBA; protected int mCameraIndex = CAMERA_ID_ANY; protected boolean mEnabled; + protected boolean mCameraPermissionGranted = false; protected FpsMeter mFpsMeter = null; public static final int CAMERA_ID_ANY = -1; @@ -219,9 +220,24 @@ public abstract class CameraBridgeViewBase extends SurfaceView implements Surfac } } + + /** + * This method is provided for clients, so they can signal camera permission has been granted. + * The actual onCameraViewStarted callback will be delivered only after setCameraPermissionGranted + * and enableView have been called and surface is available + */ + public void setCameraPermissionGranted() { + synchronized(mSyncObject) { + mCameraPermissionGranted = true; + checkCurrentState(); + } + } + + /** * This method is provided for clients, so they can enable the camera connection. - * The actual onCameraViewStarted callback will be delivered only after both this method is called and surface is available + * The actual onCameraViewStarted callback will be delivered only after setCameraPermissionGranted + * and enableView have been called and surface is available */ public void enableView() { synchronized(mSyncObject) { @@ -300,7 +316,7 @@ public abstract class CameraBridgeViewBase extends SurfaceView implements Surfac Log.d(TAG, "call checkCurrentState"); int targetState; - if (mEnabled && mSurfaceExist && getVisibility() == VISIBLE) { + if (mEnabled && mCameraPermissionGranted && mSurfaceExist && getVisibility() == VISIBLE) { targetState = STARTED; } else { targetState = STOPPED; diff --git a/samples/android/15-puzzle/src/org/opencv/samples/puzzle15/Puzzle15Activity.java b/samples/android/15-puzzle/src/org/opencv/samples/puzzle15/Puzzle15Activity.java index df428b570a..d289189dea 100644 --- a/samples/android/15-puzzle/src/org/opencv/samples/puzzle15/Puzzle15Activity.java +++ b/samples/android/15-puzzle/src/org/opencv/samples/puzzle15/Puzzle15Activity.java @@ -1,6 +1,7 @@ package org.opencv.samples.puzzle15; import org.opencv.android.BaseLoaderCallback; +import org.opencv.android.CameraActivity; import org.opencv.android.LoaderCallbackInterface; import org.opencv.android.OpenCVLoader; import org.opencv.core.Mat; @@ -9,7 +10,6 @@ import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener; import org.opencv.android.JavaCameraView; import android.os.Bundle; -import android.app.Activity; import android.util.Log; import android.view.Menu; import android.view.MenuItem; @@ -17,7 +17,10 @@ import android.view.MotionEvent; import android.view.View; import android.view.WindowManager; -public class Puzzle15Activity extends Activity implements CvCameraViewListener, View.OnTouchListener { +import java.util.Collections; +import java.util.List; + +public class Puzzle15Activity extends CameraActivity implements CvCameraViewListener, View.OnTouchListener { private static final String TAG = "Sample::Puzzle15::Activity"; @@ -86,6 +89,11 @@ public class Puzzle15Activity extends Activity implements CvCameraViewListener, } } + @Override + protected List getCameraViewList() { + return Collections.singletonList(mOpenCvCameraView); + } + public void onDestroy() { super.onDestroy(); if (mOpenCvCameraView != null) diff --git a/samples/android/camera-calibration/src/org/opencv/samples/cameracalibration/CameraCalibrationActivity.java b/samples/android/camera-calibration/src/org/opencv/samples/cameracalibration/CameraCalibrationActivity.java index 77353a702c..53cf266c1e 100644 --- a/samples/android/camera-calibration/src/org/opencv/samples/cameracalibration/CameraCalibrationActivity.java +++ b/samples/android/camera-calibration/src/org/opencv/samples/cameracalibration/CameraCalibrationActivity.java @@ -14,6 +14,7 @@ package org.opencv.samples.cameracalibration; import org.opencv.android.BaseLoaderCallback; +import org.opencv.android.CameraActivity; import org.opencv.android.CameraBridgeViewBase; import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame; import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2; @@ -21,7 +22,6 @@ import org.opencv.android.LoaderCallbackInterface; import org.opencv.android.OpenCVLoader; import org.opencv.core.Mat; -import android.app.Activity; import android.app.ProgressDialog; import android.content.res.Resources; import android.os.AsyncTask; @@ -36,12 +36,16 @@ import android.view.View.OnTouchListener; import android.view.WindowManager; import android.widget.Toast; -public class CameraCalibrationActivity extends Activity implements CvCameraViewListener2, OnTouchListener { +import java.util.Collections; +import java.util.List; + +public class CameraCalibrationActivity extends CameraActivity implements CvCameraViewListener2, OnTouchListener { private static final String TAG = "OCVSample::Activity"; private CameraBridgeViewBase mOpenCvCameraView; private CameraCalibrator mCalibrator; private OnCameraFrameRender mOnCameraFrameRender; + private Menu mMenu; private int mWidth; private int mHeight; @@ -101,6 +105,11 @@ public class CameraCalibrationActivity extends Activity implements CvCameraViewL } } + @Override + protected List getCameraViewList() { + return Collections.singletonList(mOpenCvCameraView); + } + public void onDestroy() { super.onDestroy(); if (mOpenCvCameraView != null) @@ -111,7 +120,7 @@ public class CameraCalibrationActivity extends Activity implements CvCameraViewL public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); getMenuInflater().inflate(R.menu.calibration, menu); - + mMenu = menu; return true; } @@ -119,9 +128,9 @@ public class CameraCalibrationActivity extends Activity implements CvCameraViewL public boolean onPrepareOptionsMenu (Menu menu) { super.onPrepareOptionsMenu(menu); menu.findItem(R.id.preview_mode).setEnabled(true); - if (!mCalibrator.isCalibrated()) + if (mCalibrator != null && !mCalibrator.isCalibrated()) { menu.findItem(R.id.preview_mode).setEnabled(false); - + } return true; } @@ -199,6 +208,10 @@ public class CameraCalibrationActivity extends Activity implements CvCameraViewL mCalibrator = new CameraCalibrator(mWidth, mHeight); if (CalibrationResult.tryLoad(this, mCalibrator.getCameraMatrix(), mCalibrator.getDistortionCoefficients())) { mCalibrator.setCalibrated(); + } else { + if (mMenu != null && !mCalibrator.isCalibrated()) { + mMenu.findItem(R.id.preview_mode).setEnabled(false); + } } mOnCameraFrameRender = new OnCameraFrameRender(new CalibrationFrameRender(mCalibrator)); diff --git a/samples/android/color-blob-detection/src/org/opencv/samples/colorblobdetect/ColorBlobDetectionActivity.java b/samples/android/color-blob-detection/src/org/opencv/samples/colorblobdetect/ColorBlobDetectionActivity.java index a9608f42c7..9c1e906cf4 100644 --- a/samples/android/color-blob-detection/src/org/opencv/samples/colorblobdetect/ColorBlobDetectionActivity.java +++ b/samples/android/color-blob-detection/src/org/opencv/samples/colorblobdetect/ColorBlobDetectionActivity.java @@ -1,8 +1,10 @@ package org.opencv.samples.colorblobdetect; +import java.util.Collections; import java.util.List; import org.opencv.android.BaseLoaderCallback; +import org.opencv.android.CameraActivity; import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame; import org.opencv.android.LoaderCallbackInterface; import org.opencv.android.OpenCVLoader; @@ -27,7 +29,7 @@ import android.view.WindowManager; import android.view.View.OnTouchListener; import android.view.SurfaceView; -public class ColorBlobDetectionActivity extends Activity implements OnTouchListener, CvCameraViewListener2 { +public class ColorBlobDetectionActivity extends CameraActivity implements OnTouchListener, CvCameraViewListener2 { private static final String TAG = "OCVSample::Activity"; private boolean mIsColorSelected = false; @@ -99,6 +101,11 @@ public class ColorBlobDetectionActivity extends Activity implements OnTouchListe } } + @Override + protected List getCameraViewList() { + return Collections.singletonList(mOpenCvCameraView); + } + public void onDestroy() { super.onDestroy(); if (mOpenCvCameraView != null) diff --git a/samples/android/face-detection/src/org/opencv/samples/facedetect/FdActivity.java b/samples/android/face-detection/src/org/opencv/samples/facedetect/FdActivity.java index d7d4359515..979c3da785 100644 --- a/samples/android/face-detection/src/org/opencv/samples/facedetect/FdActivity.java +++ b/samples/android/face-detection/src/org/opencv/samples/facedetect/FdActivity.java @@ -4,8 +4,11 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.util.Collections; +import java.util.List; import org.opencv.android.BaseLoaderCallback; +import org.opencv.android.CameraActivity; import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame; import org.opencv.android.LoaderCallbackInterface; import org.opencv.android.OpenCVLoader; @@ -28,7 +31,7 @@ import android.view.Menu; import android.view.MenuItem; import android.view.WindowManager; -public class FdActivity extends Activity implements CvCameraViewListener2 { +public class FdActivity extends CameraActivity implements CvCameraViewListener2 { private static final String TAG = "OCVSample::Activity"; private static final Scalar FACE_RECT_COLOR = new Scalar(0, 255, 0, 255); @@ -150,6 +153,11 @@ public class FdActivity extends Activity implements CvCameraViewListener2 { } } + @Override + protected List getCameraViewList() { + return Collections.singletonList(mOpenCvCameraView); + } + public void onDestroy() { super.onDestroy(); mOpenCvCameraView.disableView(); diff --git a/samples/android/image-manipulations/src/org/opencv/samples/imagemanipulations/ImageManipulationsActivity.java b/samples/android/image-manipulations/src/org/opencv/samples/imagemanipulations/ImageManipulationsActivity.java index 13b9e42a84..5ab5e5f06d 100644 --- a/samples/android/image-manipulations/src/org/opencv/samples/imagemanipulations/ImageManipulationsActivity.java +++ b/samples/android/image-manipulations/src/org/opencv/samples/imagemanipulations/ImageManipulationsActivity.java @@ -1,8 +1,11 @@ package org.opencv.samples.imagemanipulations; import java.util.Arrays; +import java.util.Collections; +import java.util.List; import org.opencv.android.BaseLoaderCallback; +import org.opencv.android.CameraActivity; import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame; import org.opencv.android.LoaderCallbackInterface; import org.opencv.android.OpenCVLoader; @@ -18,14 +21,13 @@ import org.opencv.android.CameraBridgeViewBase; import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2; import org.opencv.imgproc.Imgproc; -import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.WindowManager; -public class ImageManipulationsActivity extends Activity implements CvCameraViewListener2 { +public class ImageManipulationsActivity extends CameraActivity implements CvCameraViewListener2 { private static final String TAG = "OCVSample::Activity"; public static final int VIEW_MODE_RGBA = 0; @@ -121,6 +123,11 @@ public class ImageManipulationsActivity extends Activity implements CvCameraView } } + @Override + protected List getCameraViewList() { + return Collections.singletonList(mOpenCvCameraView); + } + public void onDestroy() { super.onDestroy(); if (mOpenCvCameraView != null) diff --git a/samples/android/tutorial-1-camerapreview/src/org/opencv/samples/tutorial1/Tutorial1Activity.java b/samples/android/tutorial-1-camerapreview/src/org/opencv/samples/tutorial1/Tutorial1Activity.java index 2984c295c9..4c9a39b3f7 100644 --- a/samples/android/tutorial-1-camerapreview/src/org/opencv/samples/tutorial1/Tutorial1Activity.java +++ b/samples/android/tutorial-1-camerapreview/src/org/opencv/samples/tutorial1/Tutorial1Activity.java @@ -1,6 +1,7 @@ package org.opencv.samples.tutorial1; import org.opencv.android.BaseLoaderCallback; +import org.opencv.android.CameraActivity; import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame; import org.opencv.android.LoaderCallbackInterface; import org.opencv.android.OpenCVLoader; @@ -8,16 +9,16 @@ import org.opencv.core.Mat; import org.opencv.android.CameraBridgeViewBase; import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2; -import android.app.Activity; import android.os.Bundle; import android.util.Log; -import android.view.Menu; import android.view.MenuItem; import android.view.SurfaceView; import android.view.WindowManager; -import android.widget.Toast; -public class Tutorial1Activity extends Activity implements CvCameraViewListener2 { +import java.util.Collections; +import java.util.List; + +public class Tutorial1Activity extends CameraActivity implements CvCameraViewListener2 { private static final String TAG = "OCVSample::Activity"; private CameraBridgeViewBase mOpenCvCameraView; @@ -82,6 +83,11 @@ public class Tutorial1Activity extends Activity implements CvCameraViewListener2 } } + @Override + protected List getCameraViewList() { + return Collections.singletonList(mOpenCvCameraView); + } + public void onDestroy() { super.onDestroy(); if (mOpenCvCameraView != null) diff --git a/samples/android/tutorial-2-mixedprocessing/src/org/opencv/samples/tutorial2/Tutorial2Activity.java b/samples/android/tutorial-2-mixedprocessing/src/org/opencv/samples/tutorial2/Tutorial2Activity.java index bcef8886da..617247afa9 100644 --- a/samples/android/tutorial-2-mixedprocessing/src/org/opencv/samples/tutorial2/Tutorial2Activity.java +++ b/samples/android/tutorial-2-mixedprocessing/src/org/opencv/samples/tutorial2/Tutorial2Activity.java @@ -1,6 +1,7 @@ package org.opencv.samples.tutorial2; import org.opencv.android.BaseLoaderCallback; +import org.opencv.android.CameraActivity; import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame; import org.opencv.android.LoaderCallbackInterface; import org.opencv.android.OpenCVLoader; @@ -10,14 +11,16 @@ import org.opencv.android.CameraBridgeViewBase; import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2; import org.opencv.imgproc.Imgproc; -import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.WindowManager; -public class Tutorial2Activity extends Activity implements CvCameraViewListener2 { +import java.util.Collections; +import java.util.List; + +public class Tutorial2Activity extends CameraActivity implements CvCameraViewListener2 { private static final String TAG = "OCVSample::Activity"; private static final int VIEW_MODE_RGBA = 0; @@ -107,6 +110,11 @@ public class Tutorial2Activity extends Activity implements CvCameraViewListener2 } } + @Override + protected List getCameraViewList() { + return Collections.singletonList(mOpenCvCameraView); + } + public void onDestroy() { super.onDestroy(); if (mOpenCvCameraView != null) diff --git a/samples/android/tutorial-3-cameracontrol/src/org/opencv/samples/tutorial3/Tutorial3Activity.java b/samples/android/tutorial-3-cameracontrol/src/org/opencv/samples/tutorial3/Tutorial3Activity.java index 1800bfe1d3..c487f11e8d 100644 --- a/samples/android/tutorial-3-cameracontrol/src/org/opencv/samples/tutorial3/Tutorial3Activity.java +++ b/samples/android/tutorial-3-cameracontrol/src/org/opencv/samples/tutorial3/Tutorial3Activity.java @@ -1,11 +1,14 @@ package org.opencv.samples.tutorial3; import java.text.SimpleDateFormat; +import java.util.Collections; import java.util.Date; import java.util.List; import java.util.ListIterator; import org.opencv.android.BaseLoaderCallback; +import org.opencv.android.CameraActivity; +import org.opencv.android.CameraBridgeViewBase; import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame; import org.opencv.android.LoaderCallbackInterface; import org.opencv.android.OpenCVLoader; @@ -13,7 +16,6 @@ import org.opencv.core.Mat; import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2; import android.annotation.SuppressLint; -import android.app.Activity; import android.hardware.Camera.Size; import android.os.Bundle; import android.os.Environment; @@ -28,11 +30,14 @@ import android.view.View.OnTouchListener; import android.view.WindowManager; import android.widget.Toast; -public class Tutorial3Activity extends Activity implements CvCameraViewListener2, OnTouchListener { +public class Tutorial3Activity extends CameraActivity implements CvCameraViewListener2, OnTouchListener { private static final String TAG = "OCVSample::Activity"; private Tutorial3View mOpenCvCameraView; private List mResolutionList; + private Menu mMenu; + private boolean mCameraStarted = false; + private boolean mMenuItemsCreated = false; private MenuItem[] mEffectMenuItems; private SubMenu mColorEffectsMenu; private MenuItem[] mResolutionMenuItems; @@ -97,13 +102,21 @@ public class Tutorial3Activity extends Activity implements CvCameraViewListener2 } } + @Override + protected List getCameraViewList() { + return Collections.singletonList(mOpenCvCameraView); + } + public void onDestroy() { super.onDestroy(); if (mOpenCvCameraView != null) mOpenCvCameraView.disableView(); } + @Override public void onCameraViewStarted(int width, int height) { + mCameraStarted = true; + setupMenuItems(); } public void onCameraViewStopped() { @@ -115,38 +128,43 @@ public class Tutorial3Activity extends Activity implements CvCameraViewListener2 @Override public boolean onCreateOptionsMenu(Menu menu) { + mMenu = menu; + setupMenuItems(); + return true; + } + + private void setupMenuItems() { + if (mMenu == null || !mCameraStarted || mMenuItemsCreated) { + return; + } List effects = mOpenCvCameraView.getEffectList(); if (effects == null) { Log.e(TAG, "Color effects are not supported by device!"); - return true; + return; } - mColorEffectsMenu = menu.addSubMenu("Color Effect"); + mColorEffectsMenu = mMenu.addSubMenu("Color Effect"); mEffectMenuItems = new MenuItem[effects.size()]; int idx = 0; ListIterator effectItr = effects.listIterator(); - while(effectItr.hasNext()) { - String element = effectItr.next(); - mEffectMenuItems[idx] = mColorEffectsMenu.add(1, idx, Menu.NONE, element); - idx++; + for (String effect: effects) { + mEffectMenuItems[idx] = mColorEffectsMenu.add(1, idx, Menu.NONE, effect); + idx++; } - mResolutionMenu = menu.addSubMenu("Resolution"); + mResolutionMenu = mMenu.addSubMenu("Resolution"); mResolutionList = mOpenCvCameraView.getResolutionList(); mResolutionMenuItems = new MenuItem[mResolutionList.size()]; - ListIterator resolutionItr = mResolutionList.listIterator(); idx = 0; - while(resolutionItr.hasNext()) { - Size element = resolutionItr.next(); + for (Size resolution: mResolutionList) { mResolutionMenuItems[idx] = mResolutionMenu.add(2, idx, Menu.NONE, - Integer.valueOf(element.width).toString() + "x" + Integer.valueOf(element.height).toString()); + Integer.valueOf(resolution.width).toString() + "x" + Integer.valueOf(resolution.height).toString()); idx++; - } - - return true; + } + mMenuItemsCreated = true; } public boolean onOptionsItemSelected(MenuItem item) {