diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/PreviewTest.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/PreviewTest.java index 874eb3fb..ac9aa6fb 100644 --- a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/PreviewTest.java +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/PreviewTest.java @@ -12,8 +12,6 @@ import org.junit.Rule; import org.junit.Test; import java.util.concurrent.Semaphore; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; import static org.junit.Assert.*; @@ -26,7 +24,7 @@ public abstract class PreviewTest { private Preview preview; private ViewGroup parent; private Preview.SurfaceCallback callback; - private Semaphore lock; + private Size surfaceSize = new Size(1000, 1000); @Rule public ActivityTestRule rule = new ActivityTestRule<>(TestActivity.class); @@ -37,13 +35,19 @@ public abstract class PreviewTest { @Override public void run() { Context context = rule.getActivity(); + + // Using a parent so we know its size. parent = new FrameLayout(context); + parent.setLayoutParams(new ViewGroup.LayoutParams( + surfaceSize.getWidth(), surfaceSize.getHeight())); preview = createPreview(context, parent); callback = mock(Preview.SurfaceCallback.class); preview.setSurfaceCallback(callback); - rule.getActivity().setContentView(parent); - lock = new Semaphore(1, true); + // Add all to a decor view and to the activity. + FrameLayout decor = new FrameLayout(context); + decor.addView(parent); + rule.getActivity().setContentView(decor); } }); } @@ -88,52 +92,39 @@ public abstract class PreviewTest { @Test public void testCropCenter() throws Exception { - preview.setCropListener(new Preview.CropListener() { - @Override - public void onPreCrop() { - try { - lock.acquire(); - } catch (Exception e) { - e.printStackTrace(); - }; - } - - @Override - public void onPostCrop() { - lock.release(); - } - }); + Task cropTask = preview.mCropTask; // If aspect ratio is different, there should be a crop. - Size s = new Size(1000, 1000); - preview.onSurfaceAvailable(s.getWidth(), s.getHeight()); + preview.onSurfaceAvailable( + surfaceSize.getWidth(), + surfaceSize.getHeight()); // Not cropping. + cropTask.listen(); preview.setDesiredSize(100, 100); // Wait... - lock.acquire(); - assertEquals(AspectRatio.of(100, 100), getScaledAspectRatio()); + cropTask.await(); + assertEquals(100f / 100f, getScaledAspectRatio(), 0.01f); assertFalse(preview.isCropping()); - lock.release(); // Cropping. + cropTask.listen(); preview.setDesiredSize(160, 50); // Wait... - lock.acquire(); - assertEquals(AspectRatio.of(160, 50), getScaledAspectRatio()); + cropTask.await(); + assertEquals(160f / 50f, getScaledAspectRatio(), 0.01f); assertTrue(preview.isCropping()); - lock.release(); // Not cropping. + cropTask.listen(); preview.onSurfaceSizeChanged(1600, 500); // Wait... - lock.acquire(); - assertEquals(AspectRatio.of(160, 50), getScaledAspectRatio()); + cropTask.await(); + assertEquals(160f / 50f, getScaledAspectRatio(), 0.01f); assertFalse(preview.isCropping()); - lock.release(); } - private AspectRatio getScaledAspectRatio() { + private float getScaledAspectRatio() { Size size = preview.getSurfaceSize(); - int newWidth = (int) ((float) size.getWidth() * preview.getView().getScaleX()); - int newHeight = (int) ((float) size.getHeight() * preview.getView().getScaleY()); - return AspectRatio.of(newWidth, newHeight); + int newWidth = (int) (((float) size.getWidth()) * preview.getView().getScaleX()); + int newHeight = (int) (((float) size.getHeight()) * preview.getView().getScaleY()); + return AspectRatio.of(newWidth, newHeight).toFloat(); } } diff --git a/cameraview/src/main/utils/com/otaliastudios/cameraview/Task.java b/cameraview/src/main/utils/com/otaliastudios/cameraview/Task.java new file mode 100644 index 00000000..16ef3150 --- /dev/null +++ b/cameraview/src/main/utils/com/otaliastudios/cameraview/Task.java @@ -0,0 +1,72 @@ +package com.otaliastudios.cameraview; + +import android.support.annotation.NonNull; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * A naive implementation of {@link java.util.concurrent.CountDownLatch} + * to help in testing. + */ +class Task { + + private CountDownLatch mLatch; + private T mResult; + private int mCount; + + Task() { + } + + private boolean listening() { + return mLatch != null; + } + + void listen() { + if (listening()) throw new RuntimeException("Should not happen."); + mResult = null; + mLatch = new CountDownLatch(1); + } + + void start() { + if (!listening()) mCount++; + } + + void end(T result) { + if (mCount > 0) { + mCount--; + return; + } + + if (listening()) { // Should be always true. + mResult = result; + mLatch.countDown(); + } + } + + T await() { + try { + mLatch.await(); + } catch (Exception e) { + e.printStackTrace(); + } + T result = mResult; + mResult = null; + mLatch = null; + return result; + } + + T await(long time, @NonNull TimeUnit unit) { + try { + mLatch.await(time, unit); + } catch (Exception e) { + e.printStackTrace(); + } + T result = mResult; + mResult = null; + mLatch = null; + return result; + } + + +} diff --git a/cameraview/src/main/views/com/otaliastudios/cameraview/Preview.java b/cameraview/src/main/views/com/otaliastudios/cameraview/Preview.java index 2cd9fdc0..216fa462 100644 --- a/cameraview/src/main/views/com/otaliastudios/cameraview/Preview.java +++ b/cameraview/src/main/views/com/otaliastudios/cameraview/Preview.java @@ -10,6 +10,9 @@ abstract class Preview { protected final static CameraLogger LOG = CameraLogger.create(Preview.class.getSimpleName()); + // Used for testing. + Task mCropTask = new Task(); + // This is used to notify CameraImpl to recompute its camera Preview size. // After that, CameraView will need a new layout pass to adapt to the Preview size. interface SurfaceCallback { @@ -109,13 +112,13 @@ abstract class Preview { * However that should be already managed by the framework. */ private final void crop() { - onPreCrop(); + mCropTask.start(); getView().post(new Runnable() { @Override public void run() { if (mDesiredHeight == 0 || mDesiredWidth == 0 || mSurfaceHeight == 0 || mSurfaceWidth == 0) { - onPostCrop(); + mCropTask.end(null); return; } @@ -133,7 +136,7 @@ abstract class Preview { getView().setScaleY(scaleY); LOG.i("crop:", "applied scaleX=", scaleX); LOG.i("crop:", "applied scaleY=", scaleY); - onPostCrop(); + mCropTask.end(null); } }); } @@ -145,27 +148,7 @@ abstract class Preview { * @return true if cropping */ final boolean isCropping() { - return getView().getScaleX() > 1 || getView().getScaleY() > 1; - } - - - // Utils for testing. - interface CropListener { - void onPreCrop(); - void onPostCrop(); - } - - private CropListener cropListener; - - private void onPreCrop() { - if (cropListener != null) cropListener.onPreCrop(); - } - - private void onPostCrop() { - if (cropListener != null) cropListener.onPostCrop(); - } - - void setCropListener(CropListener listener) { - cropListener = listener; + // Account for some error + return getView().getScaleX() > 1.02f || getView().getScaleY() > 1.02f; } }