diff --git a/CLAUDE.md b/CLAUDE.md index 22a6b5b..d9a4edf 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -10,7 +10,7 @@ Android camera app that applies a real-time tilt-shift (miniature/diorama) blur - Gesture controls: drag to position, pinch to resize, two-finger rotate - Slider panel for precise control of blur, falloff, size, angle, aspect ratio - Multi-lens support on devices with multiple back cameras -- EXIF GPS tagging with user-toggleable opt-out (persisted across restarts) +- EXIF GPS tagging from device location - Saves processed images to MediaStore (scoped storage) ## Architecture @@ -97,5 +97,6 @@ Bitmaps emitted to `StateFlow`s are **never eagerly recycled** immediately after - `minSdk = 35` (Android 15) — intentional for personal use. Lower to 26-29 if distributing. - Accompanist Permissions (`0.36.0`) is deprecated; should migrate to first-party `activity-compose` API. +- No user-facing toggle to disable GPS tagging — location is embedded whenever permission is granted. - Dependencies are pinned to late-2024 versions; periodic bumps recommended. - Fragment shader uses `int` uniform branching in GLSL ES 1.00 — works but could be cleaner with ES 3.00. diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 18cfc0d..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2026 Ole-Morten Duesund - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. 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 80ed80b..6cb7705 100644 --- a/app/src/main/java/no/naiv/tiltshift/ui/CameraScreen.kt +++ b/app/src/main/java/no/naiv/tiltshift/ui/CameraScreen.kt @@ -39,8 +39,6 @@ import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.FlipCameraAndroid import androidx.compose.material.icons.filled.PhotoLibrary import androidx.compose.material.icons.filled.RestartAlt -import androidx.compose.material.icons.filled.LocationOff -import androidx.compose.material.icons.filled.LocationOn import androidx.compose.material.icons.filled.Tune import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Icon @@ -109,7 +107,6 @@ fun CameraScreen( val lastSavedUri by viewModel.lastSavedUri.collectAsState() val lastThumbnailBitmap by viewModel.lastThumbnailBitmap.collectAsState() val galleryPreviewBitmap by viewModel.galleryPreviewBitmap.collectAsState() - val geotagEnabled by viewModel.geotagEnabled.collectAsState() val isGalleryPreview = galleryPreviewBitmap != null val zoomRatio by viewModel.cameraManager.zoomRatio.collectAsState() @@ -257,20 +254,6 @@ fun CameraScreen( } Row(verticalAlignment = Alignment.CenterVertically) { - // GPS geotagging toggle - IconButton( - onClick = { - viewModel.toggleGeotag() - viewModel.haptics.tick() - } - ) { - Icon( - imageVector = if (geotagEnabled) Icons.Default.LocationOn else Icons.Default.LocationOff, - contentDescription = if (geotagEnabled) "Disable GPS geotagging" else "Enable GPS geotagging", - tint = if (geotagEnabled) Color.White else Color.White.copy(alpha = 0.5f) - ) - } - if (!isGalleryPreview) { // Camera flip button IconButton( diff --git a/app/src/main/java/no/naiv/tiltshift/ui/CameraViewModel.kt b/app/src/main/java/no/naiv/tiltshift/ui/CameraViewModel.kt index cb4d9da..9595e21 100644 --- a/app/src/main/java/no/naiv/tiltshift/ui/CameraViewModel.kt +++ b/app/src/main/java/no/naiv/tiltshift/ui/CameraViewModel.kt @@ -1,7 +1,6 @@ package no.naiv.tiltshift.ui import android.app.Application -import android.content.Context import android.graphics.Bitmap import android.location.Location import android.net.Uri @@ -41,16 +40,12 @@ class CameraViewModel(application: Application) : AndroidViewModel(application) companion object { private const val TAG = "CameraViewModel" - private const val PREFS_NAME = "tiltshift_prefs" - private const val KEY_GEOTAG_ENABLED = "geotag_enabled" /** Max dimension for the preview source bitmap to keep effect computation fast. */ private const val PREVIEW_MAX_DIMENSION = 1024 /** Debounce delay before recomputing preview to reduce GC pressure during slider drags. */ private const val PREVIEW_DEBOUNCE_MS = 80L } - private val prefs = application.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE) - val cameraManager = CameraManager(application) val photoSaver = PhotoSaver(application) val captureHandler = ImageCaptureHandler(application, photoSaver) @@ -103,20 +98,6 @@ class CameraViewModel(application: Application) : AndroidViewModel(application) val isGalleryPreview: Boolean get() = _galleryBitmap.value != null - // GPS geotagging toggle (persisted) - private val _geotagEnabled = MutableStateFlow(prefs.getBoolean(KEY_GEOTAG_ENABLED, true)) - val geotagEnabled: StateFlow = _geotagEnabled.asStateFlow() - - fun toggleGeotag() { - val newValue = !_geotagEnabled.value - _geotagEnabled.value = newValue - prefs.edit().putBoolean(KEY_GEOTAG_ENABLED, newValue).apply() - } - - /** Returns current location only if geotagging is enabled. */ - private val effectiveLocation: Location? - get() = if (_geotagEnabled.value) _currentLocation.value else null - // Device state private val _currentRotation = MutableStateFlow(Surface.ROTATION_0) val currentRotation: StateFlow = _currentRotation.asStateFlow() @@ -260,7 +241,7 @@ class CameraViewModel(application: Application) : AndroidViewModel(application) val result = captureHandler.processExistingImage( imageUri = uri, blurParams = _blurParams.value, - location = effectiveLocation + location = _currentLocation.value ) handleSaveResult(result) cancelGalleryPreview() @@ -282,7 +263,7 @@ class CameraViewModel(application: Application) : AndroidViewModel(application) executor = cameraManager.getExecutor(), blurParams = _blurParams.value, deviceRotation = _currentRotation.value, - location = effectiveLocation, + location = _currentLocation.value, isFrontCamera = cameraManager.isFrontCamera.value ) handleSaveResult(result)