From e957ce607a45457b6435527168b900d9f1e9b97a Mon Sep 17 00:00:00 2001 From: fatih ergin Date: Mon, 24 Jul 2023 01:54:34 +0300 Subject: [PATCH] save medias with location data if it's allowed --- app/build.gradle | 2 +- .../camera/helpers/SimpleLocationManager.kt | 81 +++++++++++++++++++ .../implementations/CameraXInitializer.kt | 4 +- .../camera/implementations/CameraXPreview.kt | 46 ++++++++--- 4 files changed, 121 insertions(+), 12 deletions(-) create mode 100644 app/src/main/kotlin/com/simplemobiletools/camera/helpers/SimpleLocationManager.kt diff --git a/app/build.gradle b/app/build.gradle index eec54d04..37248298 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -63,7 +63,7 @@ android { } dependencies { - implementation 'com.github.fatihergin:Simple-Commons:49f496ea16' + implementation 'com.github.fatihergin:Simple-Commons:23b8cf8be1' // TODO: replace it with SimpleMobileTools after merging the Commons PR implementation 'androidx.documentfile:documentfile:1.0.1' implementation "androidx.exifinterface:exifinterface:1.3.5" implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.5.1" diff --git a/app/src/main/kotlin/com/simplemobiletools/camera/helpers/SimpleLocationManager.kt b/app/src/main/kotlin/com/simplemobiletools/camera/helpers/SimpleLocationManager.kt new file mode 100644 index 00000000..c3b3df03 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/camera/helpers/SimpleLocationManager.kt @@ -0,0 +1,81 @@ +package com.simplemobiletools.camera.helpers + +import android.Manifest +import android.location.Location +import android.location.LocationListener +import android.location.LocationManager +import androidx.annotation.RequiresPermission +import com.simplemobiletools.camera.extensions.checkLocationPermission +import com.simplemobiletools.camera.extensions.config +import com.simplemobiletools.commons.activities.BaseSimpleActivity +import com.simplemobiletools.commons.helpers.PERMISSION_ACCESS_FINE_LOCATION + +class SimpleLocationManager(private val activity: BaseSimpleActivity) { + + companion object { + private const val LOCATION_UPDATE_MIN_TIME_INTERVAL_MS = 5_000L + private const val LOCATION_UPDATE_MIN_DISTANCE_M = 10F + } + + private val locationManager = activity.getSystemService(LocationManager::class.java)!! + private val locationListener = LocationListener { location -> + this@SimpleLocationManager.location = location + } + + private var location: Location? = null + + fun getLocation(): Location? { + if (location == null) { + location = getLastKnownLocation() + } + + return location + } + + private fun getLastKnownLocation(): Location? { + return if (activity.checkLocationPermission()) { + var accurateLocation: Location? = null + for (provider in locationManager.allProviders) { + val location = locationManager.getLastKnownLocation(provider) ?: continue + if (accurateLocation == null || location.accuracy < accurateLocation.accuracy) { + accurateLocation = location + } + } + accurateLocation + } else { + null + } + } + + fun requestLocationUpdates() { + activity.apply { + if (checkLocationPermission()) { + registerLocationUpdateListener() + } else { + handlePermission(PERMISSION_ACCESS_FINE_LOCATION) { _ -> + if (checkLocationPermission()) { + registerLocationUpdateListener() + } else { + config.saveMediaLocation = false + } + } + } + } + } + + @RequiresPermission(anyOf = [Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION]) + private fun registerLocationUpdateListener() { + locationManager.allProviders.forEach { provider -> + locationManager.requestLocationUpdates( + provider, + LOCATION_UPDATE_MIN_TIME_INTERVAL_MS, + LOCATION_UPDATE_MIN_DISTANCE_M, + locationListener + ) + } + } + + fun dropLocationUpdates() { + locationManager.removeUpdates(locationListener) + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/camera/implementations/CameraXInitializer.kt b/app/src/main/kotlin/com/simplemobiletools/camera/implementations/CameraXInitializer.kt index 88b01e69..4ce3c40f 100644 --- a/app/src/main/kotlin/com/simplemobiletools/camera/implementations/CameraXInitializer.kt +++ b/app/src/main/kotlin/com/simplemobiletools/camera/implementations/CameraXInitializer.kt @@ -2,12 +2,12 @@ package com.simplemobiletools.camera.implementations import android.net.Uri import androidx.camera.view.PreviewView +import com.simplemobiletools.camera.activities.MainActivity import com.simplemobiletools.camera.helpers.CameraErrorHandler import com.simplemobiletools.camera.helpers.MediaOutputHelper import com.simplemobiletools.camera.helpers.MediaSoundHelper -import com.simplemobiletools.commons.activities.BaseSimpleActivity -class CameraXInitializer(private val activity: BaseSimpleActivity) { +class CameraXInitializer(private val activity: MainActivity) { fun createCameraXPreview( previewView: PreviewView, diff --git a/app/src/main/kotlin/com/simplemobiletools/camera/implementations/CameraXPreview.kt b/app/src/main/kotlin/com/simplemobiletools/camera/implementations/CameraXPreview.kt index b4ad662a..a4baac68 100644 --- a/app/src/main/kotlin/com/simplemobiletools/camera/implementations/CameraXPreview.kt +++ b/app/src/main/kotlin/com/simplemobiletools/camera/implementations/CameraXPreview.kt @@ -10,8 +10,6 @@ import android.util.Rational import android.util.Size import android.view.* import android.view.GestureDetector.SimpleOnGestureListener -import android.widget.Toast -import androidx.appcompat.app.AppCompatActivity import androidx.camera.core.* import androidx.camera.core.ImageCapture.* import androidx.camera.lifecycle.ProcessCameraProvider @@ -34,11 +32,12 @@ import com.simplemobiletools.camera.models.CaptureMode import com.simplemobiletools.camera.models.MediaOutput import com.simplemobiletools.camera.models.MySize import com.simplemobiletools.camera.models.ResolutionOption +import com.simplemobiletools.commons.activities.BaseSimpleActivity import com.simplemobiletools.commons.extensions.toast import com.simplemobiletools.commons.helpers.ensureBackgroundThread class CameraXPreview( - private val activity: AppCompatActivity, + private val activity: BaseSimpleActivity, private val previewView: PreviewView, private val mediaSoundHelper: MediaSoundHelper, private val mediaOutputHelper: MediaOutputHelper, @@ -122,6 +121,7 @@ class CameraXPreview( private var isPhotoCapture = initInPhotoMode private var lastRotation = 0 private var lastCameraStartTime = 0L + private var simpleLocationManager: SimpleLocationManager? = null init { bindToLifeCycle() @@ -351,6 +351,21 @@ class CameraXPreview( } } + override fun onResume(owner: LifecycleOwner) { + super.onResume(owner) + if (config.saveMediaLocation) { + if (simpleLocationManager == null) { + simpleLocationManager = SimpleLocationManager(activity) + } + simpleLocationManager?.requestLocationUpdates() + } + } + + override fun onPause(owner: LifecycleOwner) { + super.onPause(owner) + simpleLocationManager?.dropLocationUpdates() + } + override fun onStop(owner: LifecycleOwner) { orientationEventListener.disable() } @@ -473,6 +488,9 @@ class CameraXPreview( val metadata = Metadata().apply { isReversedHorizontal = isFrontCameraInUse() && config.flipPhotos + if (config.saveMediaLocation) { + location = simpleLocationManager?.getLocation() + } } val mediaOutput = mediaOutputHelper.getImageMediaOutput() @@ -572,16 +590,26 @@ class CameraXPreview( val recording = when (val mediaOutput = mediaOutputHelper.getVideoMediaOutput()) { is MediaOutput.FileDescriptorMediaOutput -> { - FileDescriptorOutputOptions.Builder(mediaOutput.fileDescriptor).build() - .let { videoCapture!!.output.prepareRecording(activity, it) } + FileDescriptorOutputOptions.Builder(mediaOutput.fileDescriptor).apply { + if (config.saveMediaLocation) { + setLocation(simpleLocationManager?.getLocation()) + } + }.build().let { videoCapture!!.output.prepareRecording(activity, it) } } is MediaOutput.FileMediaOutput -> { - FileOutputOptions.Builder(mediaOutput.file).build() - .let { videoCapture!!.output.prepareRecording(activity, it) } + FileOutputOptions.Builder(mediaOutput.file).apply { + if (config.saveMediaLocation) { + setLocation(simpleLocationManager?.getLocation()) + } + }.build().let { videoCapture!!.output.prepareRecording(activity, it) } } is MediaOutput.MediaStoreOutput -> { - MediaStoreOutputOptions.Builder(contentResolver, mediaOutput.contentUri).setContentValues(mediaOutput.contentValues).build() - .let { videoCapture!!.output.prepareRecording(activity, it) } + MediaStoreOutputOptions.Builder(contentResolver, mediaOutput.contentUri).apply { + setContentValues(mediaOutput.contentValues) + if (config.saveMediaLocation) { + setLocation(simpleLocationManager?.getLocation()) + } + }.build().let { videoCapture!!.output.prepareRecording(activity, it) } } }