diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/.idea/.name b/.idea/.name
new file mode 100644
index 0000000..5af6101
--- /dev/null
+++ b/.idea/.name
@@ -0,0 +1 @@
+OTUS Location+Maps HW
\ No newline at end of file
diff --git a/.idea/AndroidProjectSystem.xml b/.idea/AndroidProjectSystem.xml
new file mode 100644
index 0000000..4a53bee
--- /dev/null
+++ b/.idea/AndroidProjectSystem.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000..b86273d
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml
new file mode 100644
index 0000000..a74f29f
--- /dev/null
+++ b/.idea/deploymentTargetSelector.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/deviceManager.xml b/.idea/deviceManager.xml
new file mode 100644
index 0000000..91f9558
--- /dev/null
+++ b/.idea/deviceManager.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
new file mode 100644
index 0000000..639c779
--- /dev/null
+++ b/.idea/gradle.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/markdown.xml b/.idea/markdown.xml
new file mode 100644
index 0000000..c61ea33
--- /dev/null
+++ b/.idea/markdown.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/migrations.xml b/.idea/migrations.xml
new file mode 100644
index 0000000..f8051a6
--- /dev/null
+++ b/.idea/migrations.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..b2c751a
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml
new file mode 100644
index 0000000..16660f1
--- /dev/null
+++ b/.idea/runConfigurations.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index 3e76c3d..83fbd65 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -4,6 +4,13 @@ plugins {
id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin'
}
+// Настройка secrets-gradle-plugin для использования API ключа из local.properties
+secrets {
+ propertiesFileName = 'local.properties'
+ defaultPropertiesFileName = 'local.properties'
+ ignoreList.add("keyToIgnore")
+}
+
android {
namespace 'com.sample.otuslocationmapshw'
compileSdk 35
@@ -15,6 +22,7 @@ android {
versionCode 1
versionName "1.0"
+ buildConfigField "String", "MAPS_API_KEY", "\"${project.findProperty('MAPS_API_KEY') ?: ''}\""
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
@@ -33,6 +41,7 @@ android {
}
buildFeatures {
viewBinding true
+ buildConfig true
}
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 7a1883f..4cfc455 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -2,6 +2,20 @@
+
+
+
+
+
+
+
+
+
+
+ android:value="${MAPS_API_KEY}" />
+ val exifInterface = ExifInterface(lastFile)
+ val location = locationDataUtils.getLocationFromExif(exifInterface)
+ val lastPoint = LatLng(location.latitude, location.longitude)
+ // Анимация перемещения камеры к точке с зумом 15
+ map.animateCamera(CameraUpdateFactory.newLatLngZoom(lastPoint, 15f))
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/sample/otuslocationmapshw/camera/CameraActivity.kt b/app/src/main/java/com/sample/otuslocationmapshw/camera/CameraActivity.kt
index 076ade5..0886eae 100644
--- a/app/src/main/java/com/sample/otuslocationmapshw/camera/CameraActivity.kt
+++ b/app/src/main/java/com/sample/otuslocationmapshw/camera/CameraActivity.kt
@@ -14,6 +14,7 @@ import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageCapture
+import androidx.camera.core.ImageCaptureException
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.app.ActivityCompat
@@ -58,21 +59,24 @@ class CameraActivity : AppCompatActivity() {
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
cameraProviderFuture = ProcessCameraProvider.getInstance(this)
- // TODO("Получить экземпляр SensorManager")
- // TODO("Добавить проверку на наличие датчика акселерометра и присвоить значение tiltSensor")
- tiltSensor = TODO("Get tilt sensor")
+ // Получение экземпляра SensorManager для работы с датчиками
+ sensorManager = getSystemService(SENSOR_SERVICE) as SensorManager
+ // Получение датчика акселерометра
+ tiltSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
cameraProviderFuture.addListener({
cameraProvider = cameraProviderFuture.get()
}, ContextCompat.getMainExecutor(this))
sensorEventListener = object : SensorEventListener {
override fun onSensorChanged(event: SensorEvent) {
- val tilt = event.values[2]
- binding.errorTextView.visibility = if (abs(tilt) > 2) View.VISIBLE else View.GONE
+ // Отслеживание угла наклона по оси X (values[0])
+ val tiltX = event.values[0]
+ // Если телефон наклонен слишком сильно (больше 2 единиц), показываем предупреждение
+ binding.errorTextView.visibility = if (abs(tiltX) > 2) View.VISIBLE else View.GONE
}
override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {
- //nothing to do
+ // Ничего не делаем при изменении точности датчика
}
}
@@ -81,9 +85,23 @@ class CameraActivity : AppCompatActivity() {
}
}
- // TODO("Подписаться на получение событий обновления датчика")
+ override fun onResume() {
+ super.onResume()
+ // Подписываемся на получение событий обновления датчика акселерометра
+ tiltSensor?.let {
+ sensorManager.registerListener(
+ sensorEventListener,
+ it,
+ SensorManager.SENSOR_DELAY_NORMAL
+ )
+ }
+ }
- // TODO("Остановить получение событий от датчика")
+ override fun onPause() {
+ super.onPause()
+ // Отписываемся от получения событий от датчика для экономии ресурсов
+ sensorManager.unregisterListener(sensorEventListener)
+ }
override fun onRequestPermissionsResult(
requestCode: Int,
@@ -117,19 +135,68 @@ class CameraActivity : AppCompatActivity() {
}
val filePath = folderPath + SimpleDateFormat(FILENAME_FORMAT, Locale.getDefault()).format(Date())
- // TODO("4. Добавить установку местоположения в метаданные фото")
+ // Создание метаданных с местоположением для фото
+ val metadata = ImageCapture.Metadata().apply {
+ // Установка местоположения в метаданные фото
+ location?.let {
+ this.location = it
+ }
+ }
+
+ // Создание настроек для сохранения фото с метаданными
val outputFileOptions = ImageCapture.OutputFileOptions.Builder(File(filePath))
+ .setMetadata(metadata) // Установка метаданных с местоположением
.build()
- // TODO("Добавить вызов CameraX для фото")
- // TODO("Вывести Toast о том, что фото успешно сохранено и закрыть текущее активити c указанием кода результата SUCCESS_RESULT_CODE")
- // imageCapture...
+ // Получение главного executor для выполнения callback-ов
+ val executor = ContextCompat.getMainExecutor(this)
+
+ // Вызов CameraX для создания фото
+ imageCapture.takePicture(
+ outputFileOptions,
+ executor,
+ object : ImageCapture.OnImageSavedCallback {
+ override fun onImageSaved(output: ImageCapture.OutputFileResults) {
+ // Успешное сохранение фото
+ Toast.makeText(
+ this@CameraActivity,
+ "Фото успешно сохранено!",
+ Toast.LENGTH_SHORT
+ ).show()
+ // Установка результата работы активити
+ setResult(SUCCESS_RESULT_CODE)
+ // Закрытие активити
+ finish()
+ }
+
+ override fun onError(exception: ImageCaptureException) {
+ // Обработка ошибки при сохранении фото
+ Log.e(TAG, "Photo capture failed: ${exception.message}", exception)
+ Toast.makeText(
+ this@CameraActivity,
+ "Ошибка при сохранении фото: ${exception.message}",
+ Toast.LENGTH_LONG
+ ).show()
+ }
+ }
+ )
}
}
@SuppressLint("MissingPermission")
private fun getLastLocation(callback: (location: Location?) -> Unit) {
- // TODO("Добавить получение местоположения от fusedLocationClient и передать результат в callback после получения")
+ // Получение текущего местоположения через FusedLocationProviderClient
+ // Используем getLastLocation() для получения последнего известного местоположения
+ fusedLocationClient.lastLocation
+ .addOnSuccessListener { location: Location? ->
+ // Передаем полученное местоположение в callback
+ callback.invoke(location)
+ }
+ .addOnFailureListener { exception ->
+ Log.e(TAG, "Error getting location", exception)
+ // В случае ошибки передаем null
+ callback.invoke(null)
+ }
}
private fun startCamera() {
@@ -168,10 +235,12 @@ class CameraActivity : AppCompatActivity() {
private const val TAG = "CameraXApp"
private const val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS"
private const val REQUEST_CODE_PERMISSIONS = 10
- // TODO("Указать набор требуемых разрешений")
- private val REQUIRED_PERMISSIONS: Array = mutableListOf(
- // TODO("Добавить требуемые разрешения")
- ).toTypedArray()
+ // Набор требуемых разрешений для работы с камерой и местоположением
+ private val REQUIRED_PERMISSIONS: Array = arrayOf(
+ android.Manifest.permission.CAMERA,
+ android.Manifest.permission.ACCESS_FINE_LOCATION,
+ android.Manifest.permission.ACCESS_COARSE_LOCATION
+ )
const val SUCCESS_RESULT_CODE = 15
}