mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-02 16:32:57 +01:00
Preserve user zoom level when starting video recording.
Remove the unconditional zoom reset to 1x at the start of video recording so that any pinch-to-zoom the user applied before recording is maintained.
This commit is contained in:
committed by
Cody Henthorne
parent
177ef8a555
commit
4ed0056d2a
@@ -257,14 +257,20 @@ public class CameraButtonView extends View {
|
||||
startAnimation(shrinkAnimation);
|
||||
}
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
if (isRecordingVideo && eventIsNotInsideDeadzone(event)) {
|
||||
if (isRecordingVideo) {
|
||||
float maxRange = getHeight() * DRAG_DISTANCE_MULTIPLIER;
|
||||
|
||||
float maxRange = getHeight() * DRAG_DISTANCE_MULTIPLIER;
|
||||
float deltaY = Math.abs(event.getY() - deadzoneRect.top);
|
||||
float increment = Math.min(1f, deltaY / maxRange);
|
||||
|
||||
notifyZoomPercent(ZOOM_INTERPOLATOR.getInterpolation(increment));
|
||||
invalidate();
|
||||
if (eventIsAboveDeadzone(event)) {
|
||||
float deltaY = Math.abs(event.getY() - deadzoneRect.top);
|
||||
float increment = Math.min(1f, deltaY / maxRange);
|
||||
notifyZoomPercent(ZOOM_INTERPOLATOR.getInterpolation(increment));
|
||||
invalidate();
|
||||
} else if (eventIsBelowDeadzone(event)) {
|
||||
float deltaY = Math.abs(event.getY() - deadzoneRect.bottom);
|
||||
float increment = Math.min(1f, deltaY / maxRange);
|
||||
notifyZoomPercent(-ZOOM_INTERPOLATOR.getInterpolation(increment));
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
@@ -279,10 +285,14 @@ public class CameraButtonView extends View {
|
||||
return super.onTouchEvent(event);
|
||||
}
|
||||
|
||||
private boolean eventIsNotInsideDeadzone(MotionEvent event) {
|
||||
private boolean eventIsAboveDeadzone(MotionEvent event) {
|
||||
return Math.round(event.getY()) < deadzoneRect.top;
|
||||
}
|
||||
|
||||
private boolean eventIsBelowDeadzone(MotionEvent event) {
|
||||
return Math.round(event.getY()) > deadzoneRect.bottom;
|
||||
}
|
||||
|
||||
private void notifyVideoCaptureStarted() {
|
||||
if (!isRecordingVideo && videoCaptureListener != null) {
|
||||
videoCaptureListener.onVideoCaptureStarted();
|
||||
|
||||
@@ -238,8 +238,15 @@ class CameraXVideoCaptureHelper implements CameraButtonView.VideoCaptureListener
|
||||
@Override
|
||||
public void onZoomIncremented(float increment) {
|
||||
ZoomState zoomState = Objects.requireNonNull(cameraController.getZoomState().getValue());
|
||||
float range = zoomState.getMaxZoomRatio() - getDefaultVideoZoomRatio();
|
||||
cameraController.setZoomRatio((range * increment) + getDefaultVideoZoomRatio());
|
||||
float base = getDefaultVideoZoomRatio();
|
||||
|
||||
if (increment >= 0f) {
|
||||
float range = zoomState.getMaxZoomRatio() - base;
|
||||
cameraController.setZoomRatio(base + range * increment);
|
||||
} else {
|
||||
float range = base - zoomState.getMinZoomRatio();
|
||||
cameraController.setZoomRatio(base + range * increment);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -29,8 +29,8 @@ sealed interface CameraScreenEvents {
|
||||
/** Zoom that happens when you pinch your fingers. */
|
||||
data class PinchZoom(val zoomFactor: Float) : CameraScreenEvents
|
||||
|
||||
/** Zoom that happens when you move your finger up and down during recording. */
|
||||
data class LinearZoom(@param:FloatRange(from = 0.0, to = 1.0) val linearZoom: Float) : CameraScreenEvents
|
||||
/** Zoom that happens when you move your finger up and down during recording. Positive values zoom in, negative values zoom out. */
|
||||
data class LinearZoom(@param:FloatRange(from = -1.0, to = 1.0) val linearZoom: Float) : CameraScreenEvents
|
||||
|
||||
/** Switches between available cameras (i.e. front and rear cameras). */
|
||||
data class SwitchCamera(val context: Context) : CameraScreenEvents
|
||||
|
||||
@@ -79,6 +79,7 @@ class CameraScreenViewModel : ViewModel() {
|
||||
private var brightnessBeforeFlash: Float = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE
|
||||
private var brightnessWindow: WeakReference<Window>? = null
|
||||
private var orientationListener: OrientationEventListener? = null
|
||||
private var recordingStartZoomRatio: Float = 1f
|
||||
|
||||
private val _qrCodeDetected = MutableSharedFlow<String>(extraBufferCapacity = 1)
|
||||
|
||||
@@ -229,6 +230,8 @@ class CameraScreenViewModel : ViewModel() {
|
||||
) {
|
||||
val capture = videoCapture ?: return
|
||||
|
||||
recordingStartZoomRatio = _state.value.zoomRatio
|
||||
|
||||
val enableTorch = _state.value.flashMode == FlashMode.On &&
|
||||
_state.value.lensFacing == CameraSelector.LENS_FACING_BACK
|
||||
|
||||
@@ -236,9 +239,6 @@ class CameraScreenViewModel : ViewModel() {
|
||||
camera?.cameraControl?.enableTorch(true)
|
||||
}
|
||||
|
||||
camera?.cameraControl?.setZoomRatio(1f)
|
||||
_state.value = _state.value.copy(zoomRatio = 1f)
|
||||
|
||||
// Prepare recording based on configuration
|
||||
val pendingRecording = when (output) {
|
||||
is VideoOutput.FileOutput -> {
|
||||
@@ -494,14 +494,21 @@ class CameraScreenViewModel : ViewModel() {
|
||||
) {
|
||||
val currentCamera = camera ?: return
|
||||
|
||||
// Clamp linear zoom to valid range
|
||||
val clampedLinearZoom = linearZoom.coerceIn(0f, 1f)
|
||||
// Clamp linear zoom to valid range (-1 to 1)
|
||||
val clampedLinearZoom = linearZoom.coerceIn(-1f, 1f)
|
||||
|
||||
// Map 0.0-1.0 to the range from 1x to maxZoomRatio.
|
||||
// We use 1x as the base instead of minZoomRatio because minZoomRatio may be less than 1x on devices with an ultrawide lens (e.g. 0.5x).
|
||||
val baseZoom = 1f
|
||||
// Use the zoom ratio from when recording started as the base, so that the
|
||||
// drag gesture is relative to the user's current zoom level rather than jumping.
|
||||
// Positive values (0 to 1) zoom in from base toward maxZoomRatio.
|
||||
// Negative values (-1 to 0) zoom out from base toward minZoomRatio.
|
||||
val baseZoom = recordingStartZoomRatio
|
||||
val minZoom = currentCamera.cameraInfo.zoomState.value?.minZoomRatio ?: 1f
|
||||
val maxZoom = currentCamera.cameraInfo.zoomState.value?.maxZoomRatio ?: 1f
|
||||
val newZoomRatio = baseZoom + (maxZoom - baseZoom) * clampedLinearZoom
|
||||
val newZoomRatio = if (clampedLinearZoom >= 0f) {
|
||||
baseZoom + (maxZoom - baseZoom) * clampedLinearZoom
|
||||
} else {
|
||||
baseZoom + (baseZoom - minZoom) * clampedLinearZoom
|
||||
}
|
||||
|
||||
currentCamera.cameraControl.setZoomRatio(newZoomRatio)
|
||||
|
||||
|
||||
@@ -202,13 +202,19 @@ fun CaptureButton(
|
||||
|
||||
// Handle zoom during recording
|
||||
if (longPressTriggered) {
|
||||
val deadzoneBottom = size.height * (1f - DEADZONE_REDUCTION_PERCENT / 2f)
|
||||
val isAboveDeadzone = currentPointer.position.y < deadzoneTop
|
||||
val isBelowDeadzone = currentPointer.position.y > deadzoneBottom
|
||||
if (isAboveDeadzone) {
|
||||
val deltaY = (deadzoneTop - currentPointer.position.y).coerceAtLeast(0f)
|
||||
val zoomPercent = (deltaY / maxRange).coerceIn(0f, 1f)
|
||||
// Apply decelerate interpolation like CameraButtonView
|
||||
val interpolatedZoom = decelerateInterpolation(zoomPercent)
|
||||
onZoomChange(interpolatedZoom)
|
||||
} else if (isBelowDeadzone) {
|
||||
val deltaY = (currentPointer.position.y - deadzoneBottom).coerceAtLeast(0f)
|
||||
val zoomPercent = (deltaY / maxRange).coerceIn(0f, 1f)
|
||||
val interpolatedZoom = decelerateInterpolation(zoomPercent)
|
||||
onZoomChange(-interpolatedZoom)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ sealed interface StandardCameraHudEvents {
|
||||
|
||||
data object SwitchCamera : StandardCameraHudEvents
|
||||
|
||||
data class SetZoomLevel(@param:FloatRange(from = 0.0, to = 1.0) val zoomLevel: Float) : StandardCameraHudEvents
|
||||
data class SetZoomLevel(@param:FloatRange(from = -1.0, to = 1.0) val zoomLevel: Float) : StandardCameraHudEvents
|
||||
|
||||
/**
|
||||
* Emitted when the gallery button is clicked.
|
||||
|
||||
Reference in New Issue
Block a user