diff --git a/fotoapparat/src/main/java/io/fotoapparat/hardware/v1/Camera1.java b/fotoapparat/src/main/java/io/fotoapparat/hardware/v1/Camera1.java index 2cb5f8f1..6139c4f6 100644 --- a/fotoapparat/src/main/java/io/fotoapparat/hardware/v1/Camera1.java +++ b/fotoapparat/src/main/java/io/fotoapparat/hardware/v1/Camera1.java @@ -14,10 +14,15 @@ import io.fotoapparat.hardware.CameraDevice; import io.fotoapparat.hardware.CameraException; import io.fotoapparat.hardware.Capabilities; +import io.fotoapparat.hardware.operators.ParametersOperator; import io.fotoapparat.hardware.orientation.OrientationUtils; import io.fotoapparat.hardware.provider.AvailableLensPositionsProvider; import io.fotoapparat.hardware.provider.V1AvailableLensPositionProvider; import io.fotoapparat.hardware.v1.capabilities.CapabilitiesFactory; +import io.fotoapparat.hardware.v1.parameters.SplitParametersOperator; +import io.fotoapparat.hardware.v1.parameters.SupressExceptionsParametersOperator; +import io.fotoapparat.hardware.v1.parameters.SwitchOnFailureParametersOperator; +import io.fotoapparat.hardware.v1.parameters.UnsafeParametersOperator; import io.fotoapparat.lens.FocusResult; import io.fotoapparat.log.Logger; import io.fotoapparat.parameter.LensPosition; @@ -58,10 +63,6 @@ private static void throwOnFailSetDisplaySurface(Object displaySurface, IOExcept throw new CameraException("Unable to set display surface: " + displaySurface, e); } - private static void throwOnFailSetParameters(Parameters parameters, Exception e) { - throw new CameraException("Failed to set parameters: " + parameters, e); - } - @Override public void open(LensPosition lensPosition) { recordMethod(); @@ -188,16 +189,27 @@ private int computeImageOrientation(int screenRotationDegrees, public void updateParameters(Parameters parameters) { recordMethod(); - Camera.Parameters cameraParameters = parametersConverter.convert( - parameters, - camera.getParameters() + parametersOperator().updateParameters(parameters); + } + + @NonNull + private SwitchOnFailureParametersOperator parametersOperator() { + ParametersOperator unsafeParametersOperator = new UnsafeParametersOperator( + camera, + parametersConverter ); - try { - camera.setParameters(cameraParameters); - } catch (Exception e) { - throwOnFailSetParameters(parameters, e); - } + ParametersOperator fallbackOperator = new SplitParametersOperator( + new SupressExceptionsParametersOperator( + unsafeParametersOperator, + logger + ) + ); + + return new SwitchOnFailureParametersOperator( + unsafeParametersOperator, + fallbackOperator + ); } @Override diff --git a/fotoapparat/src/main/java/io/fotoapparat/hardware/v1/parameters/SplitParametersOperator.java b/fotoapparat/src/main/java/io/fotoapparat/hardware/v1/parameters/SplitParametersOperator.java new file mode 100644 index 00000000..1be38ece --- /dev/null +++ b/fotoapparat/src/main/java/io/fotoapparat/hardware/v1/parameters/SplitParametersOperator.java @@ -0,0 +1,33 @@ +package io.fotoapparat.hardware.v1.parameters; + +import io.fotoapparat.hardware.operators.ParametersOperator; +import io.fotoapparat.parameter.Parameters; + +/** + * Instead of updating all parameters at once, updates each parameter one by one. + */ +public class SplitParametersOperator implements ParametersOperator { + + private final ParametersOperator wrapped; + + public SplitParametersOperator(ParametersOperator wrapped) { + this.wrapped = wrapped; + } + + @Override + public void updateParameters(Parameters parameters) { + for (Parameters.Type type : parameters.storedTypes()) { + wrapped.updateParameters( + newParameters(type, parameters.getValue(type)) + ); + } + } + + private Parameters newParameters(Parameters.Type type, Object value) { + Parameters parameters = new Parameters(); + parameters.putValue(type, value); + + return parameters; + } + +} diff --git a/fotoapparat/src/main/java/io/fotoapparat/hardware/v1/parameters/SupressExceptionsParametersOperator.java b/fotoapparat/src/main/java/io/fotoapparat/hardware/v1/parameters/SupressExceptionsParametersOperator.java new file mode 100644 index 00000000..b1a892b1 --- /dev/null +++ b/fotoapparat/src/main/java/io/fotoapparat/hardware/v1/parameters/SupressExceptionsParametersOperator.java @@ -0,0 +1,31 @@ +package io.fotoapparat.hardware.v1.parameters; + +import io.fotoapparat.hardware.operators.ParametersOperator; +import io.fotoapparat.log.Logger; +import io.fotoapparat.parameter.Parameters; + +/** + * Tries to update parameters. If that fails, suppresses the exception and writes relevant + * information to log. + */ +public class SupressExceptionsParametersOperator implements ParametersOperator { + + private final ParametersOperator wrapped; + private final Logger logger; + + public SupressExceptionsParametersOperator(ParametersOperator wrapped, + Logger logger) { + this.wrapped = wrapped; + this.logger = logger; + } + + @Override + public void updateParameters(Parameters parameters) { + try { + wrapped.updateParameters(parameters); + } catch (Exception e) { + logger.log("Unable to set parameters: " + parameters); + } + } + +} diff --git a/fotoapparat/src/main/java/io/fotoapparat/hardware/v1/parameters/SwitchOnFailureParametersOperator.java b/fotoapparat/src/main/java/io/fotoapparat/hardware/v1/parameters/SwitchOnFailureParametersOperator.java new file mode 100644 index 00000000..9fe88483 --- /dev/null +++ b/fotoapparat/src/main/java/io/fotoapparat/hardware/v1/parameters/SwitchOnFailureParametersOperator.java @@ -0,0 +1,29 @@ +package io.fotoapparat.hardware.v1.parameters; + +import io.fotoapparat.hardware.operators.ParametersOperator; +import io.fotoapparat.parameter.Parameters; + +/** + * Tries to execute first operator. If that fails, tries to execute the second one. + */ +public class SwitchOnFailureParametersOperator implements ParametersOperator { + + private final ParametersOperator first; + private final ParametersOperator second; + + public SwitchOnFailureParametersOperator(ParametersOperator first, + ParametersOperator second) { + this.first = first; + this.second = second; + } + + @Override + public void updateParameters(Parameters parameters) { + try { + first.updateParameters(parameters); + } catch (Exception e) { + second.updateParameters(parameters); + } + } + +} diff --git a/fotoapparat/src/main/java/io/fotoapparat/hardware/v1/parameters/UnsafeParametersOperator.java b/fotoapparat/src/main/java/io/fotoapparat/hardware/v1/parameters/UnsafeParametersOperator.java new file mode 100644 index 00000000..89ce3184 --- /dev/null +++ b/fotoapparat/src/main/java/io/fotoapparat/hardware/v1/parameters/UnsafeParametersOperator.java @@ -0,0 +1,35 @@ +package io.fotoapparat.hardware.v1.parameters; + +import android.hardware.Camera; + +import io.fotoapparat.hardware.operators.ParametersOperator; +import io.fotoapparat.hardware.v1.ParametersConverter; +import io.fotoapparat.parameter.Parameters; + +/** + * Updates parameters of camera, possibly throwing a {@link RuntimeException} if something goes + * wrong. We can't really know why camera might reject parameters, so it should be expected. + */ +@SuppressWarnings("deprecation") +public class UnsafeParametersOperator implements ParametersOperator { + + private final Camera camera; + private final ParametersConverter parametersConverter; + + public UnsafeParametersOperator(Camera camera, + ParametersConverter parametersConverter) { + this.camera = camera; + this.parametersConverter = parametersConverter; + } + + @Override + public void updateParameters(Parameters parameters) { + Camera.Parameters cameraParameters = parametersConverter.convert( + parameters, + camera.getParameters() + ); + + camera.setParameters(cameraParameters); + } + +} diff --git a/fotoapparat/src/main/java/io/fotoapparat/parameter/Parameters.java b/fotoapparat/src/main/java/io/fotoapparat/parameter/Parameters.java index c68ebe0d..93868885 100644 --- a/fotoapparat/src/main/java/io/fotoapparat/parameter/Parameters.java +++ b/fotoapparat/src/main/java/io/fotoapparat/parameter/Parameters.java @@ -64,6 +64,21 @@ public Set storedTypes() { return result; } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Parameters that = (Parameters) o; + + return values.equals(that.values); + } + + @Override + public int hashCode() { + return values.hashCode(); + } + @Override public String toString() { return "Parameters{" + diff --git a/fotoapparat/src/test/java/io/fotoapparat/hardware/v1/parameters/SplitParametersOperatorTest.java b/fotoapparat/src/test/java/io/fotoapparat/hardware/v1/parameters/SplitParametersOperatorTest.java new file mode 100644 index 00000000..89697bba --- /dev/null +++ b/fotoapparat/src/test/java/io/fotoapparat/hardware/v1/parameters/SplitParametersOperatorTest.java @@ -0,0 +1,55 @@ +package io.fotoapparat.hardware.v1.parameters; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import io.fotoapparat.hardware.operators.ParametersOperator; +import io.fotoapparat.parameter.Parameters; +import io.fotoapparat.parameter.Size; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +@RunWith(MockitoJUnitRunner.class) +public class SplitParametersOperatorTest { + + static final Size PICTURE_SIZE = new Size(100, 100); + static final Size PREVIEW_SIZE = new Size(50, 50); + + @Mock + ParametersOperator wrapped; + + @InjectMocks + SplitParametersOperator testee; + + @Test + public void updateParameters() throws Exception { + // Given + Parameters parameters = new Parameters(); + parameters.putValue(Parameters.Type.PICTURE_SIZE, PICTURE_SIZE); + parameters.putValue(Parameters.Type.PREVIEW_SIZE, PREVIEW_SIZE); + + // When + testee.updateParameters(parameters); + + // Then + verify(wrapped).updateParameters( + parametersWithJust(Parameters.Type.PICTURE_SIZE, PICTURE_SIZE) + ); + verify(wrapped).updateParameters( + parametersWithJust(Parameters.Type.PREVIEW_SIZE, PREVIEW_SIZE) + ); + + verifyNoMoreInteractions(wrapped); + } + + private Parameters parametersWithJust(Parameters.Type type, Object value) { + Parameters parameters = new Parameters(); + parameters.putValue(type, value); + + return parameters; + } +} \ No newline at end of file diff --git a/fotoapparat/src/test/java/io/fotoapparat/hardware/v1/parameters/SupressExceptionsParametersOperatorTest.java b/fotoapparat/src/test/java/io/fotoapparat/hardware/v1/parameters/SupressExceptionsParametersOperatorTest.java new file mode 100644 index 00000000..1cd5c265 --- /dev/null +++ b/fotoapparat/src/test/java/io/fotoapparat/hardware/v1/parameters/SupressExceptionsParametersOperatorTest.java @@ -0,0 +1,43 @@ +package io.fotoapparat.hardware.v1.parameters; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import io.fotoapparat.hardware.operators.ParametersOperator; +import io.fotoapparat.log.Logger; +import io.fotoapparat.parameter.Parameters; + +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.verify; + +@RunWith(MockitoJUnitRunner.class) +public class SupressExceptionsParametersOperatorTest { + + @Mock + ParametersOperator wrapped; + @Mock + Logger logger; + @Mock + Parameters parameters; + + @InjectMocks + SupressExceptionsParametersOperator testee; + + @Test + public void updateParameters() throws Exception { + // Given + doThrow(new RuntimeException()) + .when(wrapped) + .updateParameters(parameters); + + // When + testee.updateParameters(parameters); + + // Then + verify(wrapped).updateParameters(parameters); + } + +} \ No newline at end of file diff --git a/fotoapparat/src/test/java/io/fotoapparat/hardware/v1/parameters/SwitchOnFailureParametersOperatorTest.java b/fotoapparat/src/test/java/io/fotoapparat/hardware/v1/parameters/SwitchOnFailureParametersOperatorTest.java new file mode 100644 index 00000000..826709b2 --- /dev/null +++ b/fotoapparat/src/test/java/io/fotoapparat/hardware/v1/parameters/SwitchOnFailureParametersOperatorTest.java @@ -0,0 +1,60 @@ +package io.fotoapparat.hardware.v1.parameters; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import io.fotoapparat.hardware.operators.ParametersOperator; +import io.fotoapparat.parameter.Parameters; + +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; + +@RunWith(MockitoJUnitRunner.class) +public class SwitchOnFailureParametersOperatorTest { + + @Mock + ParametersOperator first; + @Mock + ParametersOperator second; + @Mock + Parameters parameters; + + SwitchOnFailureParametersOperator testee; + + @Before + public void setUp() throws Exception { + testee = new SwitchOnFailureParametersOperator( + first, second + ); + } + + @Test + public void firstSucceeds() throws Exception { + // When + testee.updateParameters(parameters); + + // Then + verify(first).updateParameters(parameters); + + verifyZeroInteractions(second); + } + + @Test + public void firstFails() throws Exception { + // Given + doThrow(new RuntimeException()) + .when(first) + .updateParameters(parameters); + + // When + testee.updateParameters(parameters); + + // Then + verify(second).updateParameters(parameters); + } + +} \ No newline at end of file