pull/696/head
Mattia Iavarone 6 years ago
parent 22931a4c55
commit e789f9c62d
  1. 4
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/engine/CameraIntegrationTest.java
  2. 39
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/CameraEngine.java
  3. 35
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/orchestrator/CameraOrchestrator.java
  4. 20
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/orchestrator/CameraStateOrchestrator.java

@ -314,11 +314,11 @@ public abstract class CameraIntegrationTest<E extends CameraEngine> extends Base
@Test @Test
public void testOpenClose() { public void testOpenClose() {
assertEquals(controller.getState(), CameraState.OFF); assertEquals(CameraState.OFF, controller.getState());
openSync(true); openSync(true);
assertTrue(controller.getState().isAtLeast(CameraState.ENGINE)); assertTrue(controller.getState().isAtLeast(CameraState.ENGINE));
closeSync(true); closeSync(true);
assertEquals(controller.getState(), CameraState.OFF); assertEquals(CameraState.OFF, controller.getState());
} }
@Test @Test

@ -408,23 +408,24 @@ public abstract class CameraEngine implements
private Task<Void> startEngine() { private Task<Void> startEngine() {
return mOrchestrator.scheduleStateChange(CameraState.OFF, CameraState.ENGINE, return mOrchestrator.scheduleStateChange(CameraState.OFF, CameraState.ENGINE,
true, true,
new Callable<Task<Void>>() { new Callable<Task<CameraOptions>>() {
@Override @Override
public Task<Void> call() { public Task<CameraOptions> call() {
if (!collectCameraInfo(mFacing)) { if (!collectCameraInfo(mFacing)) {
LOG.e("onStartEngine:", "No camera available for facing", mFacing); LOG.e("onStartEngine:", "No camera available for facing", mFacing);
throw new CameraException(CameraException.REASON_NO_CAMERA); throw new CameraException(CameraException.REASON_NO_CAMERA);
} }
return onStartEngine().onSuccessTask( return onStartEngine();
new SuccessContinuation<CameraOptions, Void>() { }
@NonNull }).onSuccessTask(new SuccessContinuation<CameraOptions, Void>() {
@Override @NonNull
public Task<Void> then(@Nullable CameraOptions cameraOptions) { @Override
if (cameraOptions == null) throw new RuntimeException("Null options!"); public Task<Void> then(@Nullable CameraOptions cameraOptions) {
mCallback.dispatchOnCameraOpened(cameraOptions); // Put this on the outer task so we're sure it's called after getState() is changed.
return Tasks.forResult(null); // This was breaking some tests on rare occasions.
} if (cameraOptions == null) throw new RuntimeException("Null options!");
}); mCallback.dispatchOnCameraOpened(cameraOptions);
return Tasks.forResult(null);
} }
}); });
} }
@ -437,12 +438,14 @@ public abstract class CameraEngine implements
new Callable<Task<Void>>() { new Callable<Task<Void>>() {
@Override @Override
public Task<Void> call() { public Task<Void> call() {
return onStopEngine().addOnSuccessListener(new OnSuccessListener<Void>() { return onStopEngine();
@Override }
public void onSuccess(Void aVoid) { }).addOnSuccessListener(new OnSuccessListener<Void>() {
mCallback.dispatchOnCameraClosed(); @Override
} public void onSuccess(Void aVoid) {
}); // Put this on the outer task so we're sure it's called after getState() is OFF.
// This was breaking some tests on rare occasions.
mCallback.dispatchOnCameraClosed();
} }
}); });
} }

@ -40,9 +40,9 @@ public class CameraOrchestrator {
protected static class Token { protected static class Token {
public final String name; public final String name;
public final Task<Void> task; public final Task<?> task;
private Token(@NonNull String name, @NonNull Task<Void> task) { private Token(@NonNull String name, @NonNull Task<?> task) {
this.name = name; this.name = name;
this.task = task; this.task = task;
} }
@ -76,28 +76,29 @@ public class CameraOrchestrator {
}); });
} }
@SuppressWarnings("unchecked")
@NonNull @NonNull
public Task<Void> schedule(@NonNull final String name, public <T> Task<T> schedule(@NonNull final String name,
final boolean dispatchExceptions, final boolean dispatchExceptions,
@NonNull final Callable<Task<Void>> job) { @NonNull final Callable<Task<T>> job) {
LOG.i(name.toUpperCase(), "- Scheduling."); LOG.i(name.toUpperCase(), "- Scheduling.");
final TaskCompletionSource<Void> source = new TaskCompletionSource<>(); final TaskCompletionSource<T> source = new TaskCompletionSource<>();
final WorkerHandler handler = mCallback.getJobWorker(name); final WorkerHandler handler = mCallback.getJobWorker(name);
synchronized (mLock) { synchronized (mLock) {
applyCompletionListener(mJobs.getLast().task, handler, applyCompletionListener(mJobs.getLast().task, handler,
new OnCompleteListener<Void>() { new OnCompleteListener() {
@Override @Override
public void onComplete(@NonNull Task<Void> task) { public void onComplete(@NonNull Task task) {
synchronized (mLock) { synchronized (mLock) {
mJobs.removeFirst(); mJobs.removeFirst();
ensureToken(); ensureToken();
} }
try { try {
LOG.i(name.toUpperCase(), "- Executing."); LOG.i(name.toUpperCase(), "- Executing.");
Task<Void> inner = job.call(); Task<T> inner = job.call();
applyCompletionListener(inner, handler, new OnCompleteListener<Void>() { applyCompletionListener(inner, handler, new OnCompleteListener<T>() {
@Override @Override
public void onComplete(@NonNull Task<Void> task) { public void onComplete(@NonNull Task<T> task) {
Exception e = task.getException(); Exception e = task.getException();
if (e != null) { if (e != null) {
LOG.w(name.toUpperCase(), "- Finished with ERROR.", e); LOG.w(name.toUpperCase(), "- Finished with ERROR.", e);
@ -110,7 +111,7 @@ public class CameraOrchestrator {
source.trySetException(new CancellationException()); source.trySetException(new CancellationException());
} else { } else {
LOG.i(name.toUpperCase(), "- Finished."); LOG.i(name.toUpperCase(), "- Finished.");
source.trySetResult(null); source.trySetResult(task.getResult());
} }
} }
}); });
@ -153,7 +154,7 @@ public class CameraOrchestrator {
mCallback.getJobWorker(name).remove(mDelayedJobs.get(name)); mCallback.getJobWorker(name).remove(mDelayedJobs.get(name));
mDelayedJobs.remove(name); mDelayedJobs.remove(name);
} }
Token token = new Token(name, Tasks.<Void>forResult(null)); Token token = new Token(name, Tasks.forResult(null));
//noinspection StatementWithEmptyBody //noinspection StatementWithEmptyBody
while (mJobs.remove(token)) { /* do nothing */ } while (mJobs.remove(token)) { /* do nothing */ }
ensureToken(); ensureToken();
@ -163,14 +164,14 @@ public class CameraOrchestrator {
private void ensureToken() { private void ensureToken() {
synchronized (mLock) { synchronized (mLock) {
if (mJobs.isEmpty()) { if (mJobs.isEmpty()) {
mJobs.add(new Token("BASE", Tasks.<Void>forResult(null))); mJobs.add(new Token("BASE", Tasks.forResult(null)));
} }
} }
} }
private static void applyCompletionListener(@NonNull final Task<Void> task, private static <T> void applyCompletionListener(@NonNull final Task<T> task,
@NonNull WorkerHandler handler, @NonNull WorkerHandler handler,
@NonNull final OnCompleteListener<Void> listener) { @NonNull final OnCompleteListener<T> listener) {
if (task.isComplete()) { if (task.isComplete()) {
handler.run(new Runnable() { handler.run(new Runnable() {
@Override @Override

@ -47,19 +47,19 @@ public class CameraStateOrchestrator extends CameraOrchestrator {
} }
@NonNull @NonNull
public Task<Void> scheduleStateChange(@NonNull final CameraState fromState, public <T> Task<T> scheduleStateChange(@NonNull final CameraState fromState,
@NonNull final CameraState toState, @NonNull final CameraState toState,
boolean dispatchExceptions, boolean dispatchExceptions,
@NonNull final Callable<Task<Void>> stateChange) { @NonNull final Callable<Task<T>> stateChange) {
final int changeCount = ++mStateChangeCount; final int changeCount = ++mStateChangeCount;
mTargetState = toState; mTargetState = toState;
final boolean isTearDown = !toState.isAtLeast(fromState); final boolean isTearDown = !toState.isAtLeast(fromState);
final String name = isTearDown ? fromState.name() + " << " + toState.name() final String name = isTearDown ? fromState.name() + " << " + toState.name()
: fromState.name() + " >> " + toState.name(); : fromState.name() + " >> " + toState.name();
return schedule(name, dispatchExceptions, new Callable<Task<Void>>() { return schedule(name, dispatchExceptions, new Callable<Task<T>>() {
@Override @Override
public Task<Void> call() throws Exception { public Task<T> call() throws Exception {
if (getCurrentState() != fromState) { if (getCurrentState() != fromState) {
LOG.w(name.toUpperCase(), "- State mismatch, aborting. current:", LOG.w(name.toUpperCase(), "- State mismatch, aborting. current:",
getCurrentState(), "from:", fromState, "to:", toState); getCurrentState(), "from:", fromState, "to:", toState);
@ -67,9 +67,9 @@ public class CameraStateOrchestrator extends CameraOrchestrator {
} else { } else {
Executor executor = mCallback.getJobWorker(name).getExecutor(); Executor executor = mCallback.getJobWorker(name).getExecutor();
return stateChange.call().continueWithTask(executor, return stateChange.call().continueWithTask(executor,
new Continuation<Void, Task<Void>>() { new Continuation<T, Task<T>>() {
@Override @Override
public Task<Void> then(@NonNull Task<Void> task) { public Task<T> then(@NonNull Task<T> task) {
if (task.isSuccessful() || isTearDown) { if (task.isSuccessful() || isTearDown) {
mCurrentState = toState; mCurrentState = toState;
} }
@ -78,9 +78,9 @@ public class CameraStateOrchestrator extends CameraOrchestrator {
}); });
} }
} }
}).addOnCompleteListener(new OnCompleteListener<Void>() { }).addOnCompleteListener(new OnCompleteListener<T>() {
@Override @Override
public void onComplete(@NonNull Task<Void> task) { public void onComplete(@NonNull Task<T> task) {
if (changeCount == mStateChangeCount) { if (changeCount == mStateChangeCount) {
mTargetState = mCurrentState; mTargetState = mCurrentState;
} }

Loading…
Cancel
Save