My program is skipping frames. I am quite new to kotlin and google maps API. Also if you spot some other problems feel free to point out as I try to figure out the other bugs as well(Unexpected response code 400 for https://clients4.google.com/glm/mmap/api,and my autocomplete fragment pops up and the collapses instantly)
This is for an application that measures distances between the markers placed on the map by the user. I have tried to implement AsyncTask by transforming some of my functions into async task extensions, but the problem I have encountered is that most of my code interacts with the UI, therefore they can't run on background threads.
This is my MapsActivity code
:
package com.leusoft.decorio
import android.app.Activity
import android.content.Intent
import android.content.IntentSender
import android.content.pm.PackageManager
import android.graphics.Color
import android.location.Address
import android.location.Geocoder
import android.location.Location
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import com.google.android.gms.common.api.ResolvableApiException
import com.google.android.gms.common.api.Status
import com.google.android.gms.location.*
import com.google.android.gms.maps.CameraUpdateFactory
import com.google.android.gms.maps.GoogleMap
import com.google.android.gms.maps.OnMapReadyCallback
import com.google.android.gms.maps.SupportMapFragment
import com.google.android.gms.maps.model.*
import com.google.android.libraries.places.api.Places
import com.google.android.libraries.places.api.model.Place
import com.google.android.libraries.places.widget.Autocomplete
import com.google.android.libraries.places.widget.AutocompleteActivity
import com.google.android.libraries.places.widget.AutocompleteSupportFragment
import com.google.android.libraries.places.widget.listener.PlaceSelectionListener
import com.google.android.libraries.places.widget.model.AutocompleteActivityMode
import com.google.android.material.floatingactionbutton.FloatingActionButton
import java.io.IOException
import java.util.*
import kotlin.collections.ArrayList
class MapsActivity : AppCompatActivity(), OnMapReadyCallback,
GoogleMap.OnMarkerClickListener, GoogleMap.OnPolylineClickListener {
private lateinit var lastLocation: Location
private lateinit var mMap: GoogleMap
private lateinit var fusedLocationClient: FusedLocationProviderClient
private lateinit var locationCallback: LocationCallback
private lateinit var locationRequest: LocationRequest
private var locationUpdateState = false
private var markers: ArrayList<LatLng?> = ArrayList(10)
private var latestMarker: LatLng? = LatLng(0.0, 0.0)
private lateinit var removeMarker: FloatingActionButton
private lateinit var removeAll: FloatingActionButton
private var polylineOptionsArray: ArrayList<PolylineOptions?> = ArrayList(10)
companion object {
private const val LOCATION_PERMISSION_REQUEST_CODE = 1
private const val REQUEST_CHECK_SETTINGS = 2
private const val AUTOCOMPLETE_REQUEST_CODE = 3
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_maps)
// Получить SupportMapFragment и получить уведомление, когда карта будет готова к использованию
val mapFragment = supportFragmentManager
.findFragmentById(R.id.map) as SupportMapFragment
mapFragment.getMapAsync(this)
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
locationCallback = object : LocationCallback() {
override fun onLocationResult(p0: LocationResult) {
super.onLocationResult(p0)
lastLocation = p0.lastLocation
}
}
createLocationRequest()
removeMarker = findViewById(R.id.removeMarker)
removeAll = findViewById(R.id.removeAll)
// Инициализировать места
Places.initialize(getApplicationContext(), R.string.google_maps_key.toString())
// Создать новый экземпляр клиента Places
val placesClient = Places.createClient(this)
val autocompleteFragment =
getSupportFragmentManager().findFragmentById(R.id.autocomplete_fragment) as AutocompleteSupportFragment
autocompleteFragment.setPlaceFields(Arrays.asList(Place.Field.ID, Place.Field.NAME))
autocompleteFragment.setOnPlaceSelectedListener(PlaceSelectionListener())
}
fun PlaceSelectionListener():PlaceSelectionListener? {
fun onPlaceSelected(place: Place) {
val fields: List<Place.Field> = Arrays.asList(Place.Field.ID, Place.Field.NAME)
// Начать намерение автозаполнения
val intent: Intent = Autocomplete.IntentBuilder(
AutocompleteActivityMode.FULLSCREEN, fields
)
.build(this)
startActivityForResult(intent, AUTOCOMPLETE_REQUEST_CODE)
}
fun onError(status: Status) {
Log.i("Search bar", "An error occurred: " + status)
}
return null
}
// Управляет картой, когда она станет доступной.
// Этот обратный вызов срабатывает, когда карта готова к использованию.
// Здесь мы можем добавить маркеры или линии, добавить слушателей или переместить камеру. В этом случае
// мы просто добавляем маркер возле Сиднея, Австралия.
// Если сервисы Google Play не установлены на устройстве, // пользователю будет предложено установить
// это внутри SupportMapFragment. Этот метод будет запущен только после того, как пользователь
// установил сервисы Google Play и вернулся в приложение.
override fun onMapReady(googleMap: GoogleMap) {
mMap = googleMap
mMap.uiSettings.isZoomControlsEnabled = true
mMap.setOnMarkerClickListener(this)
setUpMap()
}
private fun startLocationUpdates() {
if (ActivityCompat.checkSelfPermission(
this,
android.Manifest.permission.ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(
this,
arrayOf(android.Manifest.permission.ACCESS_FINE_LOCATION),
LOCATION_PERMISSION_REQUEST_CODE
)
return
}
fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, null /* Looper */)
}
private fun clearMap() {
mMap.clear()
polylineOptionsArray.clear()
markers.clear()
}
private fun createLocationRequest() {
locationRequest = LocationRequest()
locationRequest.interval = 10000
locationRequest.fastestInterval = 5000
locationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
val builder = LocationSettingsRequest.`enter code here`Builder()
.addLocationRequest(locationRequest)
val client = LocationServices.getSettingsClient(this)
val task = client.checkLocationSettings(builder.build())
task.addOnSuccessListener {
locationUpdateState = true
startLocationUpdates()
}
task.addOnFailureListener { e ->
if (e is ResolvableApiException) {
// Настройки местоположения не удовлетворены, но это можно исправить
// показывая пользователю диалог.
try {
// Показать диалог, вызвав startResolutionForResult (),
// и проверяем результат в onActivityResult ().
e.startResolutionForResult(
this@MapsActivity,
REQUEST_CHECK_SETTINGS
)
} catch (sendEx: IntentSender.SendIntentException) {
// Игнорировать ошибку
}
}
}
}
private fun placeMarkerOnMap(location: LatLng) {
val markerOptions = MarkerOptions().position(location).draggable(true)
val titleStr = getAddress(location)
markerOptions.title(titleStr)
markers.add(location)
mMap.addMarker(markerOptions)
latestMarker = location
}
override fun onPolylineClick(p0: Polyline?) {
val start = LatLng(p0!!.points.first().latitude,p0!!.points.first().longitude)
val end = LatLng(p0!!.points.last().latitude,p0!!.points.last().longitude)
val polylineOptions:PolylineOptions = PolylineOptions().add(start,end).width(15f).color(Color.RED).clickable(true)
polylineOptionsArray.remove(polylineOptions)
p0!!.remove()
}
private fun getAddress(latLng: LatLng): String {
val geocoder = Geocoder(this)
val addresses: List<Address>?
val address: Address?
var addressText = ""
try {
addresses = geocoder.getFromLocation(latLng.latitude, latLng.longitude, 1)
if (null != addresses && !addresses.isEmpty()) {
address = addresses[0]
for (i in 0 until address.maxAddressLineIndex) {
addressText += if (i == 0) address.getAddressLine(i) else "\n" + address.getAddressLine(i)
}
}
} catch (e: IOException) {
Log.e("MapsActivity", e.localizedMessage)
}
return addressText
}
private fun setUpMap() {
if (ActivityCompat.checkSelfPermission(
this,
android.Manifest.permission.ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(
this,
arrayOf(android.Manifest.permission.ACCESS_FINE_LOCATION), LOCATION_PERMISSION_REQUEST_CODE
)
return
}
mMap.isMyLocationEnabled = true
mMap.mapType = GoogleMap.MAP_TYPE_TERRAIN
fusedLocationClient.lastLocation.addOnSuccessListener(this) { location ->
// Получил последнее известное местоположение. В некоторых редких ситуациях это может быть нулевым
if (location != null) {
lastLocation = location
val currentLatLng = LatLng(location.latitude, location.longitude)
mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(currentLatLng, 12f))
}
}
mMap.setOnMapClickListener {
placeMarkerOnMap(it)
if (markers.size > 1) {
val num = markers.size - 2
val newLinePoint = LatLng(markers[num]!!.latitude, markers[num]!!.longitude)
polylineOptionsArray.add(
PolylineOptions().add(newLinePoint, it).width(15f).color(Color.RED).clickable(true)
)
mMap.addPolyline(polylineOptionsArray.last())
}
markers.add(it)
}
mMap.setOnMarkerClickListener { onMarkerClick(it) }
mMap.setOnPolylineClickListener { onPolylineClick(it) }
removeMarker.setOnClickListener {
mMap.setOnMarkerClickListener { onMarkerClickRemove(it) }
}
removeAll.setOnClickListener { clearMap() }
}
private fun onMarkerClickRemove(p0: Marker?): Boolean {
val num = markers.size - 1
for (i in 0..num) {
if ((p0?.position?.latitude == markers[i]?.latitude) && (p0?.position?.longitude == markers[i]?.longitude)) {
markers[i] = null
break
}
}
for (i in num downTo 0) {
if (markers[i] != null) {
latestMarker = LatLng(markers[i]!!.latitude, markers[i]!!.longitude)
}
}
p0?.remove()
val marker: Marker? = null
mMap.setOnMarkerClickListener {
onMarkerClick(marker)
}
return true
}
override fun onMarkerClick(p0: Marker?): Boolean {
polylineOptionsArray.add(
PolylineOptions().add(latestMarker, p0!!.position).width(15f).color(Color.RED).clickable(true)
)
mMap.addPolyline(polylineOptionsArray.last())
return true
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_CHECK_SETTINGS) {
if (resultCode == Activity.RESULT_OK) {
locationUpdateState = true
startLocationUpdates()
}
}
if (requestCode == AUTOCOMPLETE_REQUEST_CODE) {
if (resultCode == RESULT_OK) {
val place:Place = Autocomplete . getPlaceFromIntent (data as Intent)
Log.i("Search bar", "Place: " + place.getName() + ", " + place.getId())
} else if (resultCode == AutocompleteActivity.RESULT_ERROR) {
// TODO: обработать ошибку.
val status:Status = Autocomplete . getStatusFromIntent (data as Intent)
Log.i("Search bar", status.getStatusMessage())
} else if (resultCode == RESULT_CANCELED) {
// Пользователь отменил операцию
}
}
}
override fun onPause() {
super.onPause()
fusedLocationClient.removeLocationUpdates(locationCallback)
}
public override fun onResume() {
super.onResume()
if (!locationUpdateState) {
startLocationUpdates()
}
}
}