Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<uses-feature android:name="android.hardware.camera.any" />

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
Expand All @@ -25,21 +31,20 @@
-->
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="YOUR_API_KEY" />
android:value="${MAPS_API_KEY}" />

<activity
android:name=".MapsActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<activity
android:name=".camera.CameraActivity"
android:exported="false"/>
android:exported="false" />
</application>

</manifest>
21 changes: 11 additions & 10 deletions app/src/main/java/com/sample/otuslocationmapshw/MapsActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class MapsActivity : AppCompatActivity(), OnMapReadyCallback {
ActivityResultContracts.StartActivityForResult()
) {
if (it.resultCode == CameraActivity.SUCCESS_RESULT_CODE) {
// TODO("Обновить точки на карте при получении результата от камеры")
showPreviewsOnMap()
}
}

Expand All @@ -41,9 +41,8 @@ class MapsActivity : AppCompatActivity(), OnMapReadyCallback {
binding = ActivityMapsBinding.inflate(layoutInflater)
setContentView(binding.root)

val mapFragment = supportFragmentManager
.findFragmentById(R.id.map) as SupportMapFragment
// TODO("Вызвать инициализацию карты")
val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment
mapFragment.getMapAsync(this)
}

override fun onCreateOptionsMenu(menu: Menu?): Boolean {
Expand All @@ -57,6 +56,7 @@ class MapsActivity : AppCompatActivity(), OnMapReadyCallback {
cameraForResultLauncher.launch(Intent(this, CameraActivity::class.java))
true
}

else -> {
super.onOptionsItemSelected(item)
}
Expand All @@ -72,23 +72,24 @@ class MapsActivity : AppCompatActivity(), OnMapReadyCallback {
private fun showPreviewsOnMap() {
map.clear()
val folder = File("${filesDir.absolutePath}/photos/")
var lastPoint: LatLng? = null
folder.listFiles()?.forEach {
val exifInterface = ExifInterface(it)
val location = locationDataUtils.getLocationFromExif(exifInterface)
val point = LatLng(location.latitude, location.longitude)
lastPoint = point
val pinBitmap = Bitmap.createScaledBitmap(
BitmapFactory.decodeFile(
it.path,
BitmapFactory.Options().apply {
it.path, BitmapFactory.Options().apply {
inPreferredConfig = Bitmap.Config.ARGB_8888
}), 64, 64, false
)
// TODO("Указать pinBitmap как иконку для маркера")
map.addMarker(
MarkerOptions()
.position(point)
MarkerOptions().position(point).icon(BitmapDescriptorFactory.fromBitmap(pinBitmap))
)
// TODO("Передвинуть карту к местоположению последнего фото")
lastPoint?.let {
map.animateCamera(CameraUpdateFactory.newLatLngZoom(it, 10f))
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import java.io.File
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import kotlin.invoke
import kotlin.math.abs

class CameraActivity : AppCompatActivity() {
Expand Down Expand Up @@ -58,9 +59,8 @@ class CameraActivity : AppCompatActivity() {
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
cameraProviderFuture = ProcessCameraProvider.getInstance(this)

// TODO("Получить экземпляр SensorManager")
// TODO("Добавить проверку на наличие датчика акселерометра и присвоить значение tiltSensor")
tiltSensor = TODO("Get tilt sensor")
sensorManager = getSystemService(SENSOR_SERVICE) as SensorManager
tiltSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
cameraProviderFuture.addListener({
cameraProvider = cameraProviderFuture.get()
}, ContextCompat.getMainExecutor(this))
Expand All @@ -81,24 +81,32 @@ class CameraActivity : AppCompatActivity() {
}
}

// TODO("Подписаться на получение событий обновления датчика")
override fun onResume() {
super.onResume()
tiltSensor?.let {
sensorManager.registerListener(
sensorEventListener,
it,
SensorManager.SENSOR_DELAY_UI
)
}
}

// TODO("Остановить получение событий от датчика")
override fun onPause() {
super.onPause()
sensorManager.unregisterListener(sensorEventListener)
}

override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
requestCode: Int, permissions: Array<out String>, grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == REQUEST_CODE_PERMISSIONS) {
if (allPermissionsGranted()) {
startCamera()
} else {
Toast.makeText(
this,
"Permissions not granted by the user.",
Toast.LENGTH_SHORT
this, "Permissions not granted by the user.", Toast.LENGTH_SHORT
).show()
finish()
}
Expand All @@ -115,33 +123,52 @@ class CameraActivity : AppCompatActivity() {
if (!folder.exists()) {
folder.mkdirs()
}
val filePath = folderPath + SimpleDateFormat(FILENAME_FORMAT, Locale.getDefault()).format(Date())
val filePath =
folderPath + SimpleDateFormat(FILENAME_FORMAT, Locale.getDefault()).format(Date())

// TODO("4. Добавить установку местоположения в метаданные фото")
val outputFileOptions = ImageCapture.OutputFileOptions.Builder(File(filePath))
.build()

// TODO("Добавить вызов CameraX для фото")
// TODO("Вывести Toast о том, что фото успешно сохранено и закрыть текущее активити c указанием кода результата SUCCESS_RESULT_CODE")
// imageCapture...
val metadata = ImageCapture.Metadata().apply {
this.location = location
}
val outputFileOptions =
ImageCapture.OutputFileOptions.Builder(File(filePath)).setMetadata(metadata).build()

imageCapture.takePicture(
outputFileOptions,
ContextCompat.getMainExecutor(this),
object : ImageCapture.OnImageSavedCallback {
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
Toast.makeText(baseContext, "Фото успешно сохранено", Toast.LENGTH_SHORT)
.show()
setResult(SUCCESS_RESULT_CODE)
finish()
}

override fun onError(exception: androidx.camera.core.ImageCaptureException) {
Log.e("CameraXApp", "Ошибка при съемке: ${exception.message}", exception)
Toast.makeText(baseContext, "Ошибка сохранения фото", Toast.LENGTH_SHORT)
.show()
}
})
}
}

@SuppressLint("MissingPermission")
private fun getLastLocation(callback: (location: Location?) -> Unit) {
// TODO("Добавить получение местоположения от fusedLocationClient и передать результат в callback после получения")
fusedLocationClient.lastLocation.addOnSuccessListener { location: Location? ->
callback.invoke(location)
}.addOnFailureListener {
callback.invoke(null)
}
}

private fun startCamera() {
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener({
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()

val preview = Preview.Builder()
.build()
.also {
it.setSurfaceProvider(binding.cameraPreview.surfaceProvider)
}
val preview = Preview.Builder().build().also {
it.setSurfaceProvider(binding.cameraPreview.surfaceProvider)
}

imageCapture = ImageCapture.Builder().build()

Expand All @@ -168,9 +195,10 @@ 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<String> = mutableListOf<String>(
// TODO("Добавить требуемые разрешения")
android.Manifest.permission.CAMERA,
android.Manifest.permission.ACCESS_FINE_LOCATION,
android.Manifest.permission.ACCESS_COARSE_LOCATION
).toTypedArray()

const val SUCCESS_RESULT_CODE = 15
Expand Down
6 changes: 6 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
dependencies {
classpath("com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:2.0.1")
}
}

plugins {
id 'com.android.application' version '8.7.3' apply false
id 'com.android.library' version '8.7.3' apply false
Expand Down