parent
e1139e4ecb
commit
ef879c2bbb
@ -0,0 +1,154 @@ |
|||||||
|
package com.otaliastudios.cameraview.engine; |
||||||
|
|
||||||
|
import android.hardware.camera2.CameraCharacteristics; |
||||||
|
import android.hardware.camera2.CaptureRequest; |
||||||
|
import android.hardware.camera2.CaptureResult; |
||||||
|
import android.hardware.camera2.TotalCaptureResult; |
||||||
|
import android.os.Build; |
||||||
|
|
||||||
|
import androidx.annotation.NonNull; |
||||||
|
import androidx.annotation.RequiresApi; |
||||||
|
|
||||||
|
import com.otaliastudios.cameraview.CameraLogger; |
||||||
|
import com.otaliastudios.cameraview.engine.locking.AutoExposure; |
||||||
|
import com.otaliastudios.cameraview.engine.locking.AutoFocus; |
||||||
|
import com.otaliastudios.cameraview.engine.locking.AutoWhiteBalance; |
||||||
|
import com.otaliastudios.cameraview.engine.locking.Parameter; |
||||||
|
|
||||||
|
/** |
||||||
|
* Helps Camera2-based engines to perform 3A locking and unlocking. |
||||||
|
* Users are required to: |
||||||
|
* |
||||||
|
* - Call {@link #lock(CaptureResult)} to start |
||||||
|
* - Call {@link #onCapture(CaptureResult)} when they have partial or total results, as long as the |
||||||
|
* locker is still in a locking operation, which can be checked through {@link #isLocking()} ()} |
||||||
|
* - Call {@link #unlock()} to reset the locked parameters if needed. |
||||||
|
*/ |
||||||
|
@RequiresApi(Build.VERSION_CODES.LOLLIPOP) |
||||||
|
public class Locker { |
||||||
|
|
||||||
|
/** |
||||||
|
* The locker callback. |
||||||
|
*/ |
||||||
|
public interface Callback extends Parameter.LockingChangeCallback { |
||||||
|
|
||||||
|
/** |
||||||
|
* Notifies that locking has ended. No action is required for implementors. |
||||||
|
* From now on, {@link #isLocking()} will return false. |
||||||
|
* @param success success |
||||||
|
*/ |
||||||
|
void onLocked(boolean success); |
||||||
|
|
||||||
|
/** |
||||||
|
* Notifies that locking has been undone. From now on, this locker instance |
||||||
|
* is done, although in theory it could be reused by calling |
||||||
|
* {@link #lock(CaptureResult)} again. |
||||||
|
*/ |
||||||
|
void onUnlocked(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the currently used builder. This can change while a locking |
||||||
|
* operation happens, so the locker will never cache this value. |
||||||
|
* It is the engine responsibility to copy over values to the new builder |
||||||
|
* when it changes. |
||||||
|
* @return a builder |
||||||
|
*/ |
||||||
|
@NonNull |
||||||
|
CaptureRequest.Builder getLockingBuilder(); |
||||||
|
} |
||||||
|
|
||||||
|
private static final String TAG = Locker.class.getSimpleName(); |
||||||
|
private static final CameraLogger LOG = CameraLogger.create(TAG); |
||||||
|
private static final int FORCED_END_DELAY = 2500; |
||||||
|
|
||||||
|
private final CameraCharacteristics mCharacteristics; |
||||||
|
private final Callback mCallback; |
||||||
|
|
||||||
|
private boolean mIsLocking; |
||||||
|
private Parameter mAutoFocus; |
||||||
|
private Parameter mAutoWhiteBalance; |
||||||
|
private Parameter mAutoExposure; |
||||||
|
private long mLockingStartTime; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new locker. |
||||||
|
* @param characteristics the camera characteristics |
||||||
|
* @param callback the callback |
||||||
|
*/ |
||||||
|
@SuppressWarnings("WeakerAccess") |
||||||
|
public Locker(@NonNull CameraCharacteristics characteristics, |
||||||
|
@NonNull Callback callback) { |
||||||
|
mCharacteristics = characteristics; |
||||||
|
mCallback = callback; |
||||||
|
mAutoFocus = new AutoFocus(callback); |
||||||
|
mAutoExposure = new AutoExposure(callback); |
||||||
|
mAutoWhiteBalance = new AutoWhiteBalance(callback); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Locks 3A values. |
||||||
|
* @param lastResult the last result |
||||||
|
*/ |
||||||
|
@SuppressWarnings("WeakerAccess") |
||||||
|
public void lock(@NonNull CaptureResult lastResult) { |
||||||
|
mIsLocking = true; |
||||||
|
mLockingStartTime = System.currentTimeMillis(); |
||||||
|
mAutoFocus.lock(mCharacteristics, mCallback.getLockingBuilder(), lastResult); |
||||||
|
mAutoWhiteBalance.lock(mCharacteristics, mCallback.getLockingBuilder(), lastResult); |
||||||
|
mAutoExposure.lock(mCharacteristics, mCallback.getLockingBuilder(), lastResult); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* True if we're locking. False if we're not, for example |
||||||
|
* if {@link #lock(CaptureResult)} was never called. |
||||||
|
* @return true if locking |
||||||
|
*/ |
||||||
|
@SuppressWarnings("WeakerAccess") |
||||||
|
public boolean isLocking() { |
||||||
|
return mIsLocking; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Should be called when we have partial or total CaptureResults, |
||||||
|
* but only while {@link #isLocking()} returns true. |
||||||
|
* @param result result |
||||||
|
*/ |
||||||
|
@SuppressWarnings("WeakerAccess") |
||||||
|
public void onCapture(@NonNull CaptureResult result) { |
||||||
|
if (!mIsLocking) return; // We're not interested in results anymore
|
||||||
|
if (!(result instanceof TotalCaptureResult)) return; // Let's ignore these, contents are missing/wrong
|
||||||
|
|
||||||
|
if (!mAutoFocus.isLocked()) mAutoFocus.onCapture(mCallback.getLockingBuilder(), result); |
||||||
|
if (!mAutoExposure.isLocked()) mAutoExposure.onCapture(mCallback.getLockingBuilder(), result); |
||||||
|
if (!mAutoWhiteBalance.isLocked()) mAutoWhiteBalance.onCapture(mCallback.getLockingBuilder(), result); |
||||||
|
if (mAutoFocus.isLocked() && mAutoExposure.isLocked() && mAutoWhiteBalance.isLocked()) { |
||||||
|
LOG.i("onCapture:", "all Parameters have converged. Dispatching onMeteringEnd"); |
||||||
|
boolean success = mAutoFocus.isSuccessful() |
||||||
|
&& mAutoExposure.isSuccessful() |
||||||
|
&& mAutoWhiteBalance.isSuccessful(); |
||||||
|
dispatchEnd(success); |
||||||
|
} else if (System.currentTimeMillis() - mLockingStartTime >= FORCED_END_DELAY) { |
||||||
|
LOG.i("onCapture:", "FORCED_END_DELAY was reached. Some Parameter is stuck. Forcing end."); |
||||||
|
dispatchEnd(false); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void dispatchEnd(boolean success) { |
||||||
|
mCallback.onLocked(success); |
||||||
|
mIsLocking = false; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Should be called to unlock. |
||||||
|
* Note that {@link Callback#onUnlocked()} will be called immediately, |
||||||
|
* we're not waiting for the results. |
||||||
|
*/ |
||||||
|
@SuppressWarnings("WeakerAccess") |
||||||
|
public void unlock() { |
||||||
|
LOG.i("Unlocking."); |
||||||
|
mAutoFocus.unlock(mCharacteristics, mCallback.getLockingBuilder()); |
||||||
|
mAutoExposure.unlock(mCharacteristics, mCallback.getLockingBuilder()); |
||||||
|
mAutoWhiteBalance.unlock(mCharacteristics, mCallback.getLockingBuilder()); |
||||||
|
mCallback.onUnlocked(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,91 @@ |
|||||||
|
package com.otaliastudios.cameraview.engine.locking; |
||||||
|
|
||||||
|
import android.hardware.camera2.CameraCharacteristics; |
||||||
|
import android.hardware.camera2.CaptureRequest; |
||||||
|
import android.hardware.camera2.CaptureResult; |
||||||
|
import android.os.Build; |
||||||
|
|
||||||
|
import androidx.annotation.NonNull; |
||||||
|
import androidx.annotation.RequiresApi; |
||||||
|
|
||||||
|
import com.otaliastudios.cameraview.CameraLogger; |
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.LOLLIPOP) |
||||||
|
public class AutoExposure extends Parameter { |
||||||
|
|
||||||
|
private static final String TAG = AutoExposure.class.getSimpleName(); |
||||||
|
private static final CameraLogger LOG = CameraLogger.create(TAG); |
||||||
|
|
||||||
|
public AutoExposure(@NonNull LockingChangeCallback callback) { |
||||||
|
super(callback); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected boolean checkSupportsLocking(@NonNull CameraCharacteristics characteristics, |
||||||
|
@NonNull CaptureRequest.Builder builder) { |
||||||
|
boolean isNotLegacy = readCharacteristic(characteristics, |
||||||
|
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL, -1) != |
||||||
|
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY; |
||||||
|
// Not sure we should check aeMode as well, probably all aeModes support locking,
|
||||||
|
// but this should not be a big issue since we're not even using different AE modes.
|
||||||
|
Integer aeMode = builder.get(CaptureRequest.CONTROL_AE_MODE); |
||||||
|
boolean isAEOn = aeMode != null && |
||||||
|
(aeMode == CameraCharacteristics.CONTROL_AE_MODE_ON |
||||||
|
|| aeMode == CameraCharacteristics.CONTROL_AE_MODE_ON_ALWAYS_FLASH |
||||||
|
|| aeMode == CameraCharacteristics.CONTROL_AE_MODE_ON_AUTO_FLASH |
||||||
|
|| aeMode == CameraCharacteristics.CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE |
||||||
|
|| aeMode == 5 /* CameraCharacteristics.CONTROL_AE_MODE_ON_EXTERNAL_FLASH, API 28 */); |
||||||
|
boolean result = isNotLegacy && isAEOn; |
||||||
|
LOG.i("checkSupportsProcessing:", result); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected boolean checkShouldSkip(@NonNull CaptureResult lastResult) { |
||||||
|
Integer aeState = lastResult.get(CaptureResult.CONTROL_AE_STATE); |
||||||
|
boolean result = aeState != null && aeState == CaptureResult.CONTROL_AE_STATE_LOCKED; |
||||||
|
LOG.i("checkShouldSkip:", result); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void onLock(@NonNull CameraCharacteristics characteristics, |
||||||
|
@NonNull CaptureRequest.Builder builder) { |
||||||
|
builder.set(CaptureRequest.CONTROL_AE_LOCK, true); |
||||||
|
notifyBuilderChanged(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void processCapture(@NonNull CaptureResult result) { |
||||||
|
Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); |
||||||
|
LOG.i("processCapture:", "aeState:", aeState); |
||||||
|
if (aeState == null) return; |
||||||
|
switch (aeState) { |
||||||
|
case CaptureRequest.CONTROL_AE_STATE_LOCKED: { |
||||||
|
isLocked = true; |
||||||
|
isSuccessful = false; |
||||||
|
break; |
||||||
|
} |
||||||
|
case CaptureRequest.CONTROL_AE_STATE_PRECAPTURE: |
||||||
|
case CaptureRequest.CONTROL_AE_STATE_CONVERGED: |
||||||
|
case CaptureRequest.CONTROL_AE_STATE_INACTIVE: |
||||||
|
case CaptureRequest.CONTROL_AE_STATE_SEARCHING: |
||||||
|
case CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED: { |
||||||
|
// Wait...
|
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void onLocked(@NonNull CaptureRequest.Builder builder) { |
||||||
|
// Do nothing
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void onUnlock(@NonNull CameraCharacteristics characteristics, |
||||||
|
@NonNull CaptureRequest.Builder builder) { |
||||||
|
builder.set(CaptureRequest.CONTROL_AE_LOCK, false); |
||||||
|
notifyBuilderChanged(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,101 @@ |
|||||||
|
package com.otaliastudios.cameraview.engine.locking; |
||||||
|
|
||||||
|
import android.hardware.camera2.CameraCharacteristics; |
||||||
|
import android.hardware.camera2.CaptureRequest; |
||||||
|
import android.hardware.camera2.CaptureResult; |
||||||
|
import android.os.Build; |
||||||
|
|
||||||
|
import androidx.annotation.NonNull; |
||||||
|
import androidx.annotation.Nullable; |
||||||
|
import androidx.annotation.RequiresApi; |
||||||
|
|
||||||
|
import com.otaliastudios.cameraview.CameraLogger; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.LOLLIPOP) |
||||||
|
public class AutoFocus extends Parameter { |
||||||
|
|
||||||
|
private static final String TAG = AutoFocus.class.getSimpleName(); |
||||||
|
private static final CameraLogger LOG = CameraLogger.create(TAG); |
||||||
|
|
||||||
|
private Integer oldAfMode; |
||||||
|
|
||||||
|
public AutoFocus(@NonNull LockingChangeCallback callback) { |
||||||
|
super(callback); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected boolean checkSupportsLocking(@NonNull CameraCharacteristics characteristics, |
||||||
|
@NonNull CaptureRequest.Builder builder) { |
||||||
|
// We'll lock by changing the AF mode to AUTO.
|
||||||
|
// In that mode, AF won't change unless someone starts a trigger operation.
|
||||||
|
int[] modes = readCharacteristic(characteristics, |
||||||
|
CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES, new int[]{}); |
||||||
|
for (int mode : modes) { |
||||||
|
if (mode == CameraCharacteristics.CONTROL_AF_MODE_AUTO) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected boolean checkShouldSkip(@NonNull CaptureResult lastResult) { |
||||||
|
Integer afState = lastResult.get(CaptureResult.CONTROL_AF_STATE); |
||||||
|
boolean afStateOk = afState != null && |
||||||
|
(afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED || |
||||||
|
afState == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED); |
||||||
|
oldAfMode = lastResult.get(CaptureResult.CONTROL_AF_MODE); |
||||||
|
boolean afModeOk = oldAfMode != null && oldAfMode == CaptureResult.CONTROL_AF_MODE_AUTO; |
||||||
|
boolean result = afStateOk && afModeOk; |
||||||
|
LOG.i("checkShouldSkip:", result); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void onLock(@NonNull CameraCharacteristics characteristics, |
||||||
|
@NonNull CaptureRequest.Builder builder) { |
||||||
|
builder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO); |
||||||
|
builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_CANCEL); |
||||||
|
notifyBuilderChanged(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void processCapture(@NonNull CaptureResult result) { |
||||||
|
Integer afState = result.get(CaptureResult.CONTROL_AF_STATE); |
||||||
|
Integer afMode = result.get(CaptureResult.CONTROL_AF_MODE); |
||||||
|
LOG.i("onCapture:", "afState:", afState, "afMode:", afMode); |
||||||
|
if (afState == null || afMode == null) return; |
||||||
|
if (afMode != CaptureResult.CONTROL_AF_MODE_AUTO) return; |
||||||
|
switch (afState) { |
||||||
|
case CaptureRequest.CONTROL_AF_STATE_FOCUSED_LOCKED: |
||||||
|
case CaptureRequest.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED: |
||||||
|
case CaptureRequest.CONTROL_AF_STATE_INACTIVE: |
||||||
|
case CaptureRequest.CONTROL_AF_STATE_PASSIVE_FOCUSED: |
||||||
|
case CaptureRequest.CONTROL_AF_STATE_PASSIVE_UNFOCUSED: { |
||||||
|
isLocked = true; |
||||||
|
isSuccessful = true; |
||||||
|
break; |
||||||
|
} |
||||||
|
case CaptureRequest.CONTROL_AF_STATE_ACTIVE_SCAN: |
||||||
|
case CaptureRequest.CONTROL_AF_STATE_PASSIVE_SCAN: { |
||||||
|
// Wait...
|
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void onLocked(@NonNull CaptureRequest.Builder builder) { |
||||||
|
// Do nothing.
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void onUnlock(@NonNull CameraCharacteristics characteristics, |
||||||
|
@NonNull CaptureRequest.Builder builder) { |
||||||
|
builder.set(CaptureRequest.CONTROL_AF_MODE, oldAfMode); |
||||||
|
notifyBuilderChanged(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,80 @@ |
|||||||
|
package com.otaliastudios.cameraview.engine.locking; |
||||||
|
|
||||||
|
import android.hardware.camera2.CameraCharacteristics; |
||||||
|
import android.hardware.camera2.CaptureRequest; |
||||||
|
import android.hardware.camera2.CaptureResult; |
||||||
|
import android.os.Build; |
||||||
|
|
||||||
|
import androidx.annotation.NonNull; |
||||||
|
import androidx.annotation.RequiresApi; |
||||||
|
|
||||||
|
import com.otaliastudios.cameraview.CameraLogger; |
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.LOLLIPOP) |
||||||
|
public class AutoWhiteBalance extends Parameter { |
||||||
|
|
||||||
|
private static final String TAG = AutoWhiteBalance.class.getSimpleName(); |
||||||
|
private static final CameraLogger LOG = CameraLogger.create(TAG); |
||||||
|
|
||||||
|
public AutoWhiteBalance(@NonNull LockingChangeCallback callback) { |
||||||
|
super(callback); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected boolean checkSupportsLocking(@NonNull CameraCharacteristics characteristics, |
||||||
|
@NonNull CaptureRequest.Builder builder) { |
||||||
|
boolean isNotLegacy = readCharacteristic(characteristics, |
||||||
|
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL, -1) != |
||||||
|
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY; |
||||||
|
Integer awbMode = builder.get(CaptureRequest.CONTROL_AWB_MODE); |
||||||
|
boolean result = isNotLegacy && awbMode != null && awbMode == CaptureRequest.CONTROL_AWB_MODE_AUTO; |
||||||
|
LOG.i("checkSupportsLocking:", result); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected boolean checkShouldSkip(@NonNull CaptureResult lastResult) { |
||||||
|
Integer awbState = lastResult.get(CaptureResult.CONTROL_AWB_STATE); |
||||||
|
boolean result = awbState != null && awbState == CaptureRequest.CONTROL_AWB_STATE_LOCKED; |
||||||
|
LOG.i("checkShouldSkip:", result); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void onLock(@NonNull CameraCharacteristics characteristics, |
||||||
|
@NonNull CaptureRequest.Builder builder) { |
||||||
|
builder.set(CaptureRequest.CONTROL_AWB_LOCK, true); |
||||||
|
notifyBuilderChanged(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void processCapture(@NonNull CaptureResult result) { |
||||||
|
Integer awbState = result.get(CaptureResult.CONTROL_AWB_STATE); |
||||||
|
LOG.i("processCapture:", "awbState:", awbState); |
||||||
|
if (awbState == null) return; |
||||||
|
switch (awbState) { |
||||||
|
case CaptureRequest.CONTROL_AWB_STATE_LOCKED: { |
||||||
|
isLocked = true; |
||||||
|
isSuccessful = false; |
||||||
|
break; |
||||||
|
} |
||||||
|
case CaptureRequest.CONTROL_AWB_STATE_CONVERGED: |
||||||
|
case CaptureRequest.CONTROL_AWB_STATE_INACTIVE: |
||||||
|
case CaptureRequest.CONTROL_AWB_STATE_SEARCHING: { |
||||||
|
// Wait...
|
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void onLocked(@NonNull CaptureRequest.Builder builder) { |
||||||
|
// Do nothing
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void onUnlock(@NonNull CameraCharacteristics characteristics, @NonNull CaptureRequest.Builder builder) { |
||||||
|
builder.set(CaptureRequest.CONTROL_AWB_LOCK, false); |
||||||
|
notifyBuilderChanged(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,103 @@ |
|||||||
|
package com.otaliastudios.cameraview.engine.locking; |
||||||
|
|
||||||
|
import android.hardware.camera2.CameraCharacteristics; |
||||||
|
import android.hardware.camera2.CaptureRequest; |
||||||
|
import android.hardware.camera2.CaptureResult; |
||||||
|
import android.hardware.camera2.params.MeteringRectangle; |
||||||
|
import android.os.Build; |
||||||
|
|
||||||
|
import androidx.annotation.NonNull; |
||||||
|
import androidx.annotation.Nullable; |
||||||
|
import androidx.annotation.RequiresApi; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.LOLLIPOP) |
||||||
|
public abstract class Parameter { |
||||||
|
|
||||||
|
public interface LockingChangeCallback { |
||||||
|
void onLockingChange(); |
||||||
|
} |
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess") |
||||||
|
protected boolean isSuccessful; |
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess") |
||||||
|
protected boolean isLocked; |
||||||
|
|
||||||
|
private LockingChangeCallback callback; |
||||||
|
private boolean shouldSkip; |
||||||
|
private boolean supportsLocking; |
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess") |
||||||
|
protected Parameter(@NonNull LockingChangeCallback callback) { |
||||||
|
this.callback = callback; |
||||||
|
} |
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess") |
||||||
|
@NonNull |
||||||
|
protected <T> T readCharacteristic(@NonNull CameraCharacteristics characteristics, |
||||||
|
@NonNull CameraCharacteristics.Key<T> key, |
||||||
|
@NonNull T fallback) { |
||||||
|
T value = characteristics.get(key); |
||||||
|
return value == null ? fallback : value; |
||||||
|
} |
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess") |
||||||
|
protected void notifyBuilderChanged() { |
||||||
|
callback.onLockingChange(); |
||||||
|
} |
||||||
|
|
||||||
|
public final boolean isLocked() { |
||||||
|
// A non supported parameter should always appear as metered
|
||||||
|
return isLocked || !supportsLocking || shouldSkip; |
||||||
|
} |
||||||
|
|
||||||
|
public final boolean isSuccessful() { |
||||||
|
// A non supported parameter should always appear as successful
|
||||||
|
return isSuccessful || !supportsLocking || shouldSkip; |
||||||
|
} |
||||||
|
|
||||||
|
public final void lock(@NonNull CameraCharacteristics characteristics, |
||||||
|
@NonNull CaptureRequest.Builder builder, |
||||||
|
@NonNull CaptureResult lastResult) { |
||||||
|
isSuccessful = false; |
||||||
|
isLocked = false; |
||||||
|
shouldSkip = checkShouldSkip(lastResult); |
||||||
|
supportsLocking = checkSupportsLocking(characteristics, builder); |
||||||
|
if (!shouldSkip && supportsLocking) { |
||||||
|
onLock(characteristics, builder); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public final void onCapture(@NonNull CaptureRequest.Builder builder, |
||||||
|
@NonNull CaptureResult result) { |
||||||
|
if (isLocked()) return; |
||||||
|
processCapture(result); |
||||||
|
if (isLocked()) onLocked(builder); |
||||||
|
} |
||||||
|
|
||||||
|
public final void unlock(@NonNull CameraCharacteristics characteristics, |
||||||
|
@NonNull CaptureRequest.Builder builder) { |
||||||
|
if (supportsLocking) { |
||||||
|
// Not checking shouldSkip. Though we skipped locking, we're
|
||||||
|
// now asked to unlock, so do it.
|
||||||
|
onUnlock(characteristics, builder); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected abstract boolean checkSupportsLocking(@NonNull CameraCharacteristics characteristics, |
||||||
|
@NonNull CaptureRequest.Builder builder); |
||||||
|
|
||||||
|
protected abstract boolean checkShouldSkip(@NonNull CaptureResult lastResult); |
||||||
|
|
||||||
|
protected abstract void onLock(@NonNull CameraCharacteristics characteristics, |
||||||
|
@NonNull CaptureRequest.Builder builder); |
||||||
|
|
||||||
|
protected abstract void processCapture(@NonNull CaptureResult result); |
||||||
|
|
||||||
|
protected abstract void onLocked(@NonNull CaptureRequest.Builder builder); |
||||||
|
|
||||||
|
protected abstract void onUnlock(@NonNull CameraCharacteristics characteristics, |
||||||
|
@NonNull CaptureRequest.Builder builder); |
||||||
|
} |
Loading…
Reference in new issue