diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 5e3c123..b7bbc0a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -14,15 +14,6 @@ val keystoreProperties = Properties().apply { } } -// Load version from version.properties -val versionProperties = Properties().apply { - load(rootProject.file("version.properties").inputStream()) -} -val vMajor = versionProperties["versionMajor"].toString().toInt() -val vMinor = versionProperties["versionMinor"].toString().toInt() -val vPatch = versionProperties["versionPatch"].toString().toInt() -val vCode = versionProperties["versionCode"].toString().toInt() - android { namespace = "no.naiv.tiltshift" compileSdk = 35 @@ -43,8 +34,8 @@ android { applicationId = "no.naiv.tiltshift" minSdk = 35 targetSdk = 35 - versionCode = vCode - versionName = "$vMajor.$vMinor.$vPatch" + versionCode = 1 + versionName = "1.0.0" vectorDrawables { useSupportLibrary = true diff --git a/app/src/main/java/no/naiv/tiltshift/camera/CameraManager.kt b/app/src/main/java/no/naiv/tiltshift/camera/CameraManager.kt index 00f9107..9c440ac 100644 --- a/app/src/main/java/no/naiv/tiltshift/camera/CameraManager.kt +++ b/app/src/main/java/no/naiv/tiltshift/camera/CameraManager.kt @@ -59,9 +59,6 @@ class CameraManager(private val context: Context) { private val _isFrontCamera = MutableStateFlow(false) val isFrontCamera: StateFlow = _isFrontCamera.asStateFlow() - private val _previewResolution = MutableStateFlow(Size(0, 0)) - val previewResolution: StateFlow = _previewResolution.asStateFlow() - /** Background executor for image capture callbacks to avoid blocking the main thread. */ private val captureExecutor: ExecutorService = Executors.newSingleThreadExecutor() @@ -164,7 +161,6 @@ class CameraManager(private val context: Context) { } surfaceSize = request.resolution - _previewResolution.value = surfaceSize surfaceTexture.setDefaultBufferSize(surfaceSize.width, surfaceSize.height) val surface = Surface(surfaceTexture) diff --git a/app/src/main/java/no/naiv/tiltshift/effect/TiltShiftRenderer.kt b/app/src/main/java/no/naiv/tiltshift/effect/TiltShiftRenderer.kt index 3258fa8..b3bf963 100644 --- a/app/src/main/java/no/naiv/tiltshift/effect/TiltShiftRenderer.kt +++ b/app/src/main/java/no/naiv/tiltshift/effect/TiltShiftRenderer.kt @@ -19,8 +19,7 @@ import javax.microedition.khronos.opengles.GL10 */ class TiltShiftRenderer( private val context: Context, - private val onSurfaceTextureAvailable: (SurfaceTexture) -> Unit, - private val onFrameAvailable: () -> Unit + private val onSurfaceTextureAvailable: (SurfaceTexture) -> Unit ) : GLSurfaceView.Renderer { private lateinit var shader: TiltShiftShader @@ -40,13 +39,13 @@ class TiltShiftRenderer( @Volatile private var isFrontCamera: Boolean = false - // Camera resolution for aspect ratio correction (set from UI thread) - @Volatile - private var cameraWidth: Int = 0 - @Volatile - private var cameraHeight: Int = 0 - @Volatile - private var vertexBufferDirty: Boolean = false + // Quad vertices (full screen) + private val vertices = floatArrayOf( + -1f, -1f, // Bottom left + 1f, -1f, // Bottom right + -1f, 1f, // Top left + 1f, 1f // Top right + ) // Texture coordinates rotated 90° for portrait mode (back camera) // (Camera sensors are landscape-oriented, we rotate to portrait) @@ -76,12 +75,11 @@ class TiltShiftRenderer( shader = TiltShiftShader(context) shader.initialize() - // Allocate vertex buffer (8 floats = 4 vertices × 2 components) - vertexBuffer = ByteBuffer.allocateDirect(8 * 4) + // Create vertex buffer + vertexBuffer = ByteBuffer.allocateDirect(vertices.size * 4) .order(ByteOrder.nativeOrder()) .asFloatBuffer() - // Fill with default full-screen quad; will be recomputed when camera resolution is known - vertexBuffer.put(floatArrayOf(-1f, -1f, 1f, -1f, -1f, 1f, 1f, 1f)) + .put(vertices) vertexBuffer.position(0) // Create texture coordinate buffer @@ -104,7 +102,6 @@ class TiltShiftRenderer( // Create SurfaceTexture for camera frames surfaceTexture = SurfaceTexture(cameraTextureId).also { - it.setOnFrameAvailableListener { onFrameAvailable() } onSurfaceTextureAvailable(it) } } @@ -113,19 +110,12 @@ class TiltShiftRenderer( GLES20.glViewport(0, 0, width, height) surfaceWidth = width surfaceHeight = height - vertexBufferDirty = true } override fun onDrawFrame(gl: GL10?) { // Update texture with latest camera frame surfaceTexture?.updateTexImage() - // Recompute vertex buffer for crop-to-fill when camera or surface dimensions change - if (vertexBufferDirty) { - recomputeVertices() - vertexBufferDirty = false - } - // Update texture coordinate buffer if camera changed if (updateTexCoordBuffer) { texCoordBuffer.clear() @@ -192,53 +182,6 @@ class TiltShiftRenderer( @Volatile private var updateTexCoordBuffer = false - /** - * Sets the camera preview resolution for crop-to-fill aspect ratio correction. - * Thread-safe — vertex buffer is recomputed on the next frame. - */ - fun setCameraResolution(width: Int, height: Int) { - if (cameraWidth != width || cameraHeight != height) { - cameraWidth = width - cameraHeight = height - vertexBufferDirty = true - } - } - - /** - * Recomputes vertex positions to achieve crop-to-fill. - * - * The camera sensor is landscape; after the 90° rotation applied via texture coordinates, - * the effective portrait dimensions are (cameraHeight × cameraWidth). We scale the vertex - * quad so the camera frame fills the screen without stretching — the GPU clips the overflow. - */ - private fun recomputeVertices() { - var scaleX = 1f - var scaleY = 1f - - if (cameraWidth > 0 && cameraHeight > 0 && surfaceWidth > 0 && surfaceHeight > 0) { - // After 90° rotation: portrait width = cameraHeight, portrait height = cameraWidth - val cameraRatio = cameraHeight.toFloat() / cameraWidth - val screenRatio = surfaceWidth.toFloat() / surfaceHeight - - if (cameraRatio > screenRatio) { - // Camera wider than screen → crop sides - scaleX = cameraRatio / screenRatio - } else { - // Camera taller than screen → crop top/bottom - scaleY = screenRatio / cameraRatio - } - } - - vertexBuffer.clear() - vertexBuffer.put(floatArrayOf( - -scaleX, -scaleY, - scaleX, -scaleY, - -scaleX, scaleY, - scaleX, scaleY - )) - vertexBuffer.position(0) - } - /** * Releases OpenGL resources. * Must be called from GL thread. diff --git a/app/src/main/java/no/naiv/tiltshift/ui/CameraScreen.kt b/app/src/main/java/no/naiv/tiltshift/ui/CameraScreen.kt index 8012961..80ed80b 100644 --- a/app/src/main/java/no/naiv/tiltshift/ui/CameraScreen.kt +++ b/app/src/main/java/no/naiv/tiltshift/ui/CameraScreen.kt @@ -74,8 +74,6 @@ import androidx.compose.ui.semantics.stateDescription import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.viewinterop.AndroidView -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.LifecycleEventObserver import androidx.lifecycle.compose.LocalLifecycleOwner import androidx.lifecycle.viewmodel.compose.viewModel import kotlinx.coroutines.flow.collectLatest @@ -118,7 +116,6 @@ fun CameraScreen( val minZoom by viewModel.cameraManager.minZoomRatio.collectAsState() val maxZoom by viewModel.cameraManager.maxZoomRatio.collectAsState() val isFrontCamera by viewModel.cameraManager.isFrontCamera.collectAsState() - val previewResolution by viewModel.cameraManager.previewResolution.collectAsState() val cameraError by viewModel.cameraManager.error.collectAsState() // Gallery picker @@ -164,14 +161,6 @@ fun CameraScreen( glSurfaceView?.requestRender() } - // Update renderer with camera preview resolution for crop-to-fill - LaunchedEffect(previewResolution) { - if (previewResolution.width > 0) { - renderer?.setCameraResolution(previewResolution.width, previewResolution.height) - glSurfaceView?.requestRender() - } - } - // Start camera when surface texture is available LaunchedEffect(surfaceTexture) { surfaceTexture?.let { @@ -183,28 +172,14 @@ fun CameraScreen( LaunchedEffect(isGalleryPreview) { if (isGalleryPreview) { glSurfaceView?.onPause() - } else if (lifecycleOwner.lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) { + } else { glSurfaceView?.onResume() } } - // Tie GLSurfaceView lifecycle to Activity lifecycle to prevent background rendering - val currentIsGalleryPreview by rememberUpdatedState(isGalleryPreview) - DisposableEffect(lifecycleOwner) { - val observer = LifecycleEventObserver { _, event -> - when (event) { - Lifecycle.Event.ON_RESUME -> { - if (!currentIsGalleryPreview) { - glSurfaceView?.onResume() - } - } - Lifecycle.Event.ON_PAUSE -> glSurfaceView?.onPause() - else -> {} - } - } - lifecycleOwner.lifecycle.addObserver(observer) + // Cleanup GL resources on GL thread (ViewModel handles its own cleanup in onCleared) + DisposableEffect(Unit) { onDispose { - lifecycleOwner.lifecycle.removeObserver(observer) glSurfaceView?.queueEvent { renderer?.release() } } } @@ -233,16 +208,13 @@ fun CameraScreen( GLSurfaceView(ctx).apply { setEGLContextClientVersion(2) - val view = this - val newRenderer = TiltShiftRenderer( - context = ctx, - onSurfaceTextureAvailable = { st -> surfaceTexture = st }, - onFrameAvailable = { view.requestRender() } - ) + val newRenderer = TiltShiftRenderer(ctx) { st -> + surfaceTexture = st + } renderer = newRenderer setRenderer(newRenderer) - renderMode = GLSurfaceView.RENDERMODE_WHEN_DIRTY + renderMode = GLSurfaceView.RENDERMODE_CONTINUOUSLY glSurfaceView = this } diff --git a/bump-version.sh b/bump-version.sh deleted file mode 100755 index 63a1e72..0000000 --- a/bump-version.sh +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env bash -# Bumps the version in version.properties before a release build. -# Usage: ./bump-version.sh [major|minor|patch] -# Default: patch - -set -euo pipefail - -PROPS_FILE="$(dirname "$0")/version.properties" - -if [[ ! -f "$PROPS_FILE" ]]; then - echo "Error: $PROPS_FILE not found" >&2 - exit 1 -fi - -# Read current values -major=$(grep '^versionMajor=' "$PROPS_FILE" | cut -d= -f2) -minor=$(grep '^versionMinor=' "$PROPS_FILE" | cut -d= -f2) -patch=$(grep '^versionPatch=' "$PROPS_FILE" | cut -d= -f2) -code=$(grep '^versionCode=' "$PROPS_FILE" | cut -d= -f2) - -bump_type="${1:-patch}" - -case "$bump_type" in - major) - major=$((major + 1)) - minor=0 - patch=0 - ;; - minor) - minor=$((minor + 1)) - patch=0 - ;; - patch) - patch=$((patch + 1)) - ;; - *) - echo "Usage: $0 [major|minor|patch]" >&2 - exit 1 - ;; -esac - -code=$((code + 1)) - -# Write updated values -cat > "$PROPS_FILE" << EOF -versionMajor=$major -versionMinor=$minor -versionPatch=$patch -versionCode=$code -EOF - -echo "$major.$minor.$patch (versionCode=$code)" diff --git a/version.properties b/version.properties deleted file mode 100644 index a074999..0000000 --- a/version.properties +++ /dev/null @@ -1,4 +0,0 @@ -versionMajor=1 -versionMinor=1 -versionPatch=1 -versionCode=3