Implement callback dispatcher

pull/82/head
Mattia Iavarone 8 years ago
parent 21206bdd26
commit 7a213b6b97
  1. 12
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraCallbacksTest.java
  2. 45
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java
  3. 60
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java
  4. 2
      cameraview/src/main/java/com/otaliastudios/cameraview/FrameProcessor.java

@ -38,6 +38,7 @@ public class CameraCallbacksTest extends BaseTest {
private CameraView camera; private CameraView camera;
private CameraListener listener; private CameraListener listener;
private FrameProcessor processor;
private MockCameraController mockController; private MockCameraController mockController;
private MockCameraPreview mockPreview; private MockCameraPreview mockPreview;
private Task<Boolean> task; private Task<Boolean> task;
@ -50,6 +51,7 @@ public class CameraCallbacksTest extends BaseTest {
public void run() { public void run() {
Context context = context(); Context context = context();
listener = mock(CameraListener.class); listener = mock(CameraListener.class);
processor = mock(FrameProcessor.class);
camera = new CameraView(context) { camera = new CameraView(context) {
@Override @Override
protected CameraController instantiateCameraController(CameraCallbacks callbacks) { protected CameraController instantiateCameraController(CameraCallbacks callbacks) {
@ -70,6 +72,7 @@ public class CameraCallbacksTest extends BaseTest {
}; };
camera.instantiatePreview(); camera.instantiatePreview();
camera.addCameraListener(listener); camera.addCameraListener(listener);
camera.addFrameProcessor(processor);
task = new Task<>(); task = new Task<>();
task.listen(); task.listen();
} }
@ -294,4 +297,13 @@ public class CameraCallbacksTest extends BaseTest {
Bitmap bitmap = BitmapFactory.decodeByteArray(result, 0, result.length); Bitmap bitmap = BitmapFactory.decodeByteArray(result, 0, result.length);
return new int[]{ bitmap.getWidth(), bitmap.getHeight() }; return new int[]{ bitmap.getWidth(), bitmap.getHeight() };
} }
@Test
public void testProcessFrame() {
completeTask().when(processor).process(any(Frame.class));
camera.mCameraCallbacks.dispatchFrame(new byte[]{0, 1, 2, 3}, 1000, 90);
assertNotNull(task.await(200));
verify(processor, times(1)).process(any(Frame.class));
}
} }

@ -3,6 +3,7 @@ package com.otaliastudios.cameraview;
import android.content.Context; import android.content.Context;
import android.location.Location; import android.location.Location;
import android.support.annotation.NonNull;
import android.support.test.filters.MediumTest; import android.support.test.filters.MediumTest;
import android.support.test.runner.AndroidJUnit4; import android.support.test.runner.AndroidJUnit4;
import android.view.MotionEvent; import android.view.MotionEvent;
@ -551,5 +552,49 @@ public class CameraViewTest extends BaseTest {
//endregion //endregion
//region Lists of listeners and processors
@Test
public void testCameraListenerList() {
assertTrue(cameraView.mListeners.isEmpty());
CameraListener listener = new CameraListener() {};
cameraView.addCameraListener(listener);
assertEquals(cameraView.mListeners.size(), 1);
cameraView.removeCameraListener(listener);
assertEquals(cameraView.mListeners.size(), 0);
cameraView.addCameraListener(listener);
cameraView.addCameraListener(listener);
assertEquals(cameraView.mListeners.size(), 2);
cameraView.clearCameraListeners();
assertTrue(cameraView.mListeners.isEmpty());
}
@Test
public void testFrameProcessorsList() {
assertTrue(cameraView.mFrameProcessors.isEmpty());
FrameProcessor processor = new FrameProcessor() {
public void process(@NonNull Frame frame) {}
};
cameraView.addFrameProcessor(processor);
assertEquals(cameraView.mFrameProcessors.size(), 1);
cameraView.removeFrameProcessor(processor);
assertEquals(cameraView.mFrameProcessors.size(), 0);
cameraView.addFrameProcessor(processor);
cameraView.addFrameProcessor(processor);
assertEquals(cameraView.mFrameProcessors.size(), 2);
cameraView.clearFrameProcessors();
assertTrue(cameraView.mFrameProcessors.isEmpty());
}
//endregion
// TODO: test permissions // TODO: test permissions
} }

@ -61,8 +61,9 @@ public class CameraView extends FrameLayout {
private CameraPreview mCameraPreview; private CameraPreview mCameraPreview;
private OrientationHelper mOrientationHelper; private OrientationHelper mOrientationHelper;
private CameraController mCameraController; private CameraController mCameraController;
private ArrayList<CameraListener> mListeners = new ArrayList<>(2);
private MediaActionSound mSound; private MediaActionSound mSound;
/* for tests */ ArrayList<CameraListener> mListeners = new ArrayList<>(2);
/* for tests */ ArrayList<FrameProcessor> mFrameProcessors = new ArrayList<>(1);
// Views // Views
GridLinesLayout mGridLinesLayout; GridLinesLayout mGridLinesLayout;
@ -74,6 +75,7 @@ public class CameraView extends FrameLayout {
// Threading // Threading
private Handler mUiHandler; private Handler mUiHandler;
private WorkerHandler mWorkerHandler; private WorkerHandler mWorkerHandler;
private WorkerHandler mFrameProcessorsHandler;
public CameraView(@NonNull Context context) { public CameraView(@NonNull Context context) {
super(context, null); super(context, null);
@ -119,6 +121,7 @@ public class CameraView extends FrameLayout {
mCameraController = instantiateCameraController(mCameraCallbacks); mCameraController = instantiateCameraController(mCameraCallbacks);
mUiHandler = new Handler(Looper.getMainLooper()); mUiHandler = new Handler(Looper.getMainLooper());
mWorkerHandler = WorkerHandler.get("CameraViewWorker"); mWorkerHandler = WorkerHandler.get("CameraViewWorker");
mFrameProcessorsHandler = WorkerHandler.get("FrameProcessorsWorker");
// Views // Views
mGridLinesLayout = new GridLinesLayout(context); mGridLinesLayout = new GridLinesLayout(context);
@ -1092,6 +1095,40 @@ public class CameraView extends FrameLayout {
} }
/**
* Adds a {@link FrameProcessor} instance to be notified of
* new frames in the preview stream.
*
* @param processor a frame processor.
*/
public void addFrameProcessor(FrameProcessor processor) {
if (processor != null) {
mFrameProcessors.add(processor);
}
}
/**
* Remove a {@link FrameProcessor} that was previously registered.
*
* @param processor a frame processor
*/
public void removeFrameProcessor(FrameProcessor processor) {
if (processor != null) {
mFrameProcessors.remove(processor);
}
}
/**
* Clears the list of {@link FrameProcessor} that have been registered
* to preview frames.
*/
public void clearFrameProcessors() {
mFrameProcessors.clear();
}
/** /**
* Asks the camera to capture an image of the current scene. * Asks the camera to capture an image of the current scene.
* This will trigger {@link CameraListener#onPictureTaken(byte[])} if a listener * This will trigger {@link CameraListener#onPictureTaken(byte[])} if a listener
@ -1310,6 +1347,7 @@ public class CameraView extends FrameLayout {
void dispatchOnFocusEnd(@Nullable Gesture trigger, boolean success, PointF where); void dispatchOnFocusEnd(@Nullable Gesture trigger, boolean success, PointF where);
void dispatchOnZoomChanged(final float newValue, final PointF[] fingers); void dispatchOnZoomChanged(final float newValue, final PointF[] fingers);
void dispatchOnExposureCorrectionChanged(float newValue, float[] bounds, PointF[] fingers); void dispatchOnExposureCorrectionChanged(float newValue, float[] bounds, PointF[] fingers);
void dispatchFrame(byte[] frame, long time, int rotation);
} }
private class Callbacks implements CameraCallbacks { private class Callbacks implements CameraCallbacks {
@ -1321,6 +1359,9 @@ public class CameraView extends FrameLayout {
private Integer mDisplayOffset; private Integer mDisplayOffset;
private Integer mDeviceOrientation; private Integer mDeviceOrientation;
// Frame processing
private Frame mFrame;
Callbacks() {} Callbacks() {}
@Override @Override
@ -1557,6 +1598,23 @@ public class CameraView extends FrameLayout {
} }
}); });
} }
@Override
public void dispatchFrame(final byte[] frame, final long time, final int rotation) {
mLogger.i("dispatchFrame", time, rotation, "processors:", mFrameProcessors.size());
if (mFrameProcessors.isEmpty()) return;
if (mFrame == null) mFrame = new Frame();
mFrameProcessorsHandler.post(new Runnable() {
@Override
public void run() {
mFrame.set(frame, time, rotation);
for (FrameProcessor processor : mFrameProcessors) {
processor.process(mFrame);
}
}
});
}
} }
//endregion //endregion

@ -5,7 +5,7 @@ import android.support.annotation.WorkerThread;
/** /**
* A FrameProcessor will process {@link Frame}s coming from the camera preview. * A FrameProcessor will process {@link Frame}s coming from the camera preview.
* It must be passed to {@link Cameraview#setFrameProcessor(FrameProcessor)}. * It must be passed to {@link CameraView#addFrameProcessor(FrameProcessor)}.
*/ */
public interface FrameProcessor { public interface FrameProcessor {

Loading…
Cancel
Save