diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..dd6b4428 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,34 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +### Describe the bug +A clear and concise description of what the bug is. + +- CameraView version: *version number* +- Reproducible in official demo app: *yes/no* +- Device / Android version: *Pixel, API 28* + +### To Reproduce +Steps to reproduce the behavior, possibly in the demo app: +1. Go to '...' +2. Click on '....' +3. See error + +### Expected behavior +A clear and concise description of what you expected to happen. + +### Screenshots +If applicable, add screenshots to help explain your problem. + +### Logs +Use `CameraLogger.setLogLevel(LEVEL_VERBOSE)` to see all logs into LogCat. +Use `CameraLogger.registerLogger()` to export to file or crash reporting service. + +### APK +Link to a Github repo where the bug is reproducible. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..aadb0e3b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,17 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +### Problem to be addressed +A clear and concise description of what the problem is. + +### Describe the solution you'd like +A clear and concise description of what you want to happen. + +### Additional context +Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md new file mode 100644 index 00000000..a56b5e59 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/question.md @@ -0,0 +1,12 @@ +--- +name: Question +about: Question about CameraView usage +title: '' +labels: is:question +assignees: '' + +--- + +### How do I? +Describe your problem here. Please, read the docs first. +Questions not strictly related to CameraView should be asked elsewhere. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..62379506 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,10 @@ +### Before you go +Unless this is a simple fix (typos, bugs with obvious solution), please open an issue first. +If the edited files were covered by tests, updated tests are required for merging. Please look into the tests folders and make sure you cover new code. + +- Fixes ... (*issue number*) +- Tests: ... (*yes/no*) +- Docs updated: ... (*yes/no*) + +### Solution +If applicable, briefly describe how the issue was addressed. diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 00000000..4fa1ca73 --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,26 @@ +# Configuration for probot-stale - https://github.com/probot/stale + +# Number of days of inactivity before an Issue or Pull Request becomes stale +daysUntilStale: 20 + +# Number of days of inactivity before an Issue or Pull Request with the stale label is closed. +# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. +daysUntilClose: 7 + +# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable +exemptLabels: + - is:bug + - is:enhancement + - is:discussion + +# Label to use when marking as stale +staleLabel: status:stale + +# Comment to post when marking as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + activity in the last 20 days. It will be closed if no further activity + occurs within the next seven days. Thank you for your contributions. + +# Limit to only `issues` or `pulls` +only: issues diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md deleted file mode 100644 index 25801d86..00000000 --- a/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,28 +0,0 @@ -*If you are trying to report a bug, please file the information below. -Prerequisites:* - - - -- [ ] *I am using the latest version* -- [ ] *I can reproduce the bug in the demo app, and if not...* -- [ ] *I have tried to reproduce the bug in the demo app* - -#### Detailed steps to reproduce the issue: - -1. -2. -3. - -#### Expected behavior: - -#### Actual behavior: - -#### Screenshots: - -#### Interesting logs: - -*You can use `CameraLogger.setLogLevel(LEVEL_VERBOSE)` -to add logs to logcat, and `CameraLogger.registerLogger()` -to handle logs yourself (e.g. export to file or a crash reporting service) -so you can collect data from your users.* - diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/IntegrationTest.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/IntegrationTest.java index 4e42ce62..887bc85d 100644 --- a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/IntegrationTest.java +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/IntegrationTest.java @@ -626,5 +626,19 @@ public class IntegrationTest extends BaseTest { assert30Frames(processor); } + + @Test + public void testFrameProcessing_afterVideo() throws Exception { + FrameProcessor processor = mock(FrameProcessor.class); + camera.addFrameProcessor(processor); + camera.setSessionType(SessionType.VIDEO); + waitForOpen(true); + + camera.startCapturingVideo(null, 2000); + waitForVideoEnd(true); + + assert30Frames(processor); + } + //endregion } diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/Camera1.java b/cameraview/src/main/java/com/otaliastudios/cameraview/Camera1.java index 9fda44b4..b8af16b2 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/Camera1.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/Camera1.java @@ -684,6 +684,10 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera mCameraCallbacks.dispatchOnVideoTaken(mVideoFile); mVideoFile = null; } + if (mCamera != null) { + // This is needed to restore FrameProcessor. No re-allocation needed though. + mCamera.setPreviewCallbackWithBuffer(this); + } } @WorkerThread @@ -823,15 +827,22 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera mCamera.setParameters(params); mCameraCallbacks.dispatchOnFocusStart(gesture, p); // TODO this is not guaranteed to be called... Fix. - mCamera.autoFocus(new Camera.AutoFocusCallback() { - @Override - public void onAutoFocus(boolean success, Camera camera) { - // TODO lock auto exposure and white balance for a while - mCameraCallbacks.dispatchOnFocusEnd(gesture, success, p); - mHandler.get().removeCallbacks(mPostFocusResetRunnable); - mHandler.get().postDelayed(mPostFocusResetRunnable, mPostFocusResetDelay); - } - }); + try { + mCamera.autoFocus(new Camera.AutoFocusCallback() { + @Override + public void onAutoFocus(boolean success, Camera camera) { + // TODO lock auto exposure and white balance for a while + mCameraCallbacks.dispatchOnFocusEnd(gesture, success, p); + mHandler.get().removeCallbacks(mPostFocusResetRunnable); + mHandler.get().postDelayed(mPostFocusResetRunnable, mPostFocusResetDelay); + } + }); + } catch (RuntimeException e) { + // Handling random auto-focus exception on some devices + // See https://github.com/natario1/CameraView/issues/181 + LOG.e("startAutoFocus:", "Error calling autoFocus", e); + mCameraCallbacks.dispatchOnFocusEnd(gesture, false, p); + } } }); } diff --git a/cameraview/src/main/utils/com/otaliastudios/cameraview/CameraUtils.java b/cameraview/src/main/utils/com/otaliastudios/cameraview/CameraUtils.java index dd7a74e2..c6f9ff19 100644 --- a/cameraview/src/main/utils/com/otaliastudios/cameraview/CameraUtils.java +++ b/cameraview/src/main/utils/com/otaliastudios/cameraview/CameraUtils.java @@ -82,7 +82,7 @@ public class CameraUtils { public static void decodeBitmap(final byte[] source, final BitmapCallback callback) { decodeBitmap(source, Integer.MAX_VALUE, Integer.MAX_VALUE, callback); } - + /** * Decodes an input byte array and outputs a Bitmap that is ready to be displayed. * The difference with {@link android.graphics.BitmapFactory#decodeByteArray(byte[], int, int)} @@ -98,11 +98,30 @@ public class CameraUtils { */ @SuppressWarnings("WeakerAccess") public static void decodeBitmap(final byte[] source, final int maxWidth, final int maxHeight, final BitmapCallback callback) { + decodeBitmap(source, maxWidth, maxHeight, new BitmapFactory.Options(), callback); + } + + /** + * Decodes an input byte array and outputs a Bitmap that is ready to be displayed. + * The difference with {@link android.graphics.BitmapFactory#decodeByteArray(byte[], int, int)} + * is that this cares about orientation, reading it from the EXIF header. + * This is executed in a background thread, and returns the result to the original thread. + * + * The image is also downscaled taking care of the maxWidth and maxHeight arguments. + * + * @param source a JPEG byte array + * @param maxWidth the max allowed width + * @param maxHeight the max allowed height + * @param options the options to be passed to decodeByteArray + * @param callback a callback to be notified + */ + @SuppressWarnings("WeakerAccess") + public static void decodeBitmap(final byte[] source, final int maxWidth, final int maxHeight, final BitmapFactory.Options options, final BitmapCallback callback) { final Handler ui = new Handler(); WorkerHandler.run(new Runnable() { @Override public void run() { - final Bitmap bitmap = decodeBitmap(source, maxWidth, maxHeight); + final Bitmap bitmap = decodeBitmap(source, maxWidth, maxHeight, options); ui.post(new Runnable() { @Override public void run() { @@ -113,6 +132,20 @@ public class CameraUtils { }); } + /** + * Decodes an input byte array and outputs a Bitmap that is ready to be displayed. + * The difference with {@link android.graphics.BitmapFactory#decodeByteArray(byte[], int, int)} + * is that this cares about orientation, reading it from the EXIF header. + * + * The image is also downscaled taking care of the maxWidth and maxHeight arguments. + * + * @param source a JPEG byte array + * @param maxWidth the max allowed width + * @param maxHeight the max allowed height + */ + static Bitmap decodeBitmap(byte[] source, int maxWidth, int maxHeight) { + return decodeBitmap(source, maxWidth, maxHeight, new BitmapFactory.Options()); + } /** * Decodes an input byte array and outputs a Bitmap that is ready to be displayed. @@ -124,11 +157,12 @@ public class CameraUtils { * @param source a JPEG byte array * @param maxWidth the max allowed width * @param maxHeight the max allowed height + * @param options the options to be passed to decodeByteArray */ // TODO ignores flipping @SuppressWarnings({"SuspiciousNameCombination", "WeakerAccess"}) @WorkerThread - public static Bitmap decodeBitmap(byte[] source, int maxWidth, int maxHeight) { + public static Bitmap decodeBitmap(byte[] source, int maxWidth, int maxHeight, BitmapFactory.Options options) { if (maxWidth <= 0) maxWidth = Integer.MAX_VALUE; if (maxHeight <= 0) maxHeight = Integer.MAX_VALUE; int orientation; @@ -176,7 +210,6 @@ public class CameraUtils { Bitmap bitmap; if (maxWidth < Integer.MAX_VALUE || maxHeight < Integer.MAX_VALUE) { - BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeByteArray(source, 0, source.length, options);