Многие приложения используют местоположение для предоставления услуг своим пользователям.
Примеры включают в себя множество приложений такси, таких как Uber.
Если вы хотите создать приложение, которое использует местоположение клиента, то вам повезло.
Я собираюсь показать вам, как узнать текущее местоположение пользователя и отобразить его на карте.
Найдите исходный код AndroidCurrentUserLocation на GitHub -> https://github.com/CodeGenes/AndroidCurrentUserLocation/tree/master
Сначала получите ключ API карт Google, следуя инструкциям на их странице.
Получив ключ и включив биллинг, вы должны активировать API карт с любыми дополнительными API, которые вам могут понадобиться, такими как Places API.
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
ApiKeyMap="Insert Google Maps Key Here"
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 29
buildToolsVersion "29.0.2"
defaultConfig {
applicationId "com.computingforgeeks.trucksend"
minSdkVersion 16
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
debug {
buildConfigField 'String', "ApiKeyMap", ApiKeyMap
resValue 'string', "api_key_map", ApiKeyMap
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
buildConfigField 'String', "ApiKeyMap", ApiKeyMap
resValue 'string', "api_key_map", ApiKeyMap
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'com.google.android.material:material:1.0.0'
implementation 'com.google.android.gms:play-services-location:17.0.0'
implementation 'com.google.android.gms:play-services-maps:17.0.0'
implementation "com.karumi:dexter:5.0.0"
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
Нажмите «Sync Now» в правом верхнем углу, чтобы синхронизировать зависимости.
Откройте манифест и добавьте следующее:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.computingforgeeks.trucksend">
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="@string/api_key_map" />
</application>
</manifest>
private lateinit var googleMap: GoogleMap
private lateinit var fusedLocationProviderClient: FusedLocationProviderClient
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment?
mapFragment!!.getMapAsync(this)
fusedLocationProviderClient = FusedLocationProviderClient(this)
}
override fun onMapReady(map: GoogleMap?) {
googleMap = map?: return
}
private fun isPermissionGiven(): Boolean{
return ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
}
Если разрешение не предоставлено, мы вызываем метод givePermission, чтобы запросить разрешение.
private fun givePermission() {
Dexter.withActivity(this)
.withPermission(Manifest.permission.ACCESS_FINE_LOCATION)
.withListener(this)
.check()
}
private fun getCurrentLocation() {
val locationRequest = LocationRequest()
locationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
locationRequest.interval = (10 * 1000).toLong()
locationRequest.fastestInterval = 2000
val builder = LocationSettingsRequest.Builder()
builder.addLocationRequest(locationRequest)
val locationSettingsRequest = builder.build()
val result = LocationServices.getSettingsClient(this).checkLocationSettings(locationSettingsRequest)
result.addOnCompleteListener { task ->
try {
val response = task.getResult(ApiException::class.java)
if (response!!.locationSettingsStates.isLocationPresent){
getLastLocation()
}
} catch (exception: ApiException) {
when (exception.statusCode) {
LocationSettingsStatusCodes.RESOLUTION_REQUIRED -> try {
val resolvable = exception as ResolvableApiException
resolvable.startResolutionForResult(this, REQUEST_CHECK_SETTINGS)
} catch (e: IntentSender.SendIntentException) {
} catch (e: ClassCastException) {
}
LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE -> { }
}
}
}
}
Бонус Geocoder
private fun getLastLocation() {
fusedLocationProviderClient.lastLocation
.addOnCompleteListener(this) { task ->
if (task.isSuccessful && task.result != null) {
val mLastLocation = task.result
var address = "No known address"
val gcd = Geocoder(this, Locale.getDefault())
val addresses: List<Address>
try {
addresses = gcd.getFromLocation(mLastLocation!!.latitude, mLastLocation.longitude, 1)
if (addresses.isNotEmpty()) {
address = addresses[0].getAddressLine(0)
}
} catch (e: IOException) {
e.printStackTrace()
}
val icon = BitmapDescriptorFactory.fromBitmap(BitmapFactory.decodeResource(this.resources, R.drawable.ic_pickup))
googleMap.addMarker(
MarkerOptions()
.position(LatLng(mLastLocation!!.latitude, mLastLocation.longitude))
.title("Current Location")
.snippet(address)
.icon(icon)
)
val cameraPosition = CameraPosition.Builder()
.target(LatLng(mLastLocation.latitude, mLastLocation.longitude))
.zoom(17f)
.build()
googleMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition))
} else {
Toast.makeText(this, "No current location found", Toast.LENGTH_LONG).show()
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when (requestCode) {
REQUEST_CHECK_SETTINGS -> {
if (resultCode == Activity.RESULT_OK) {
getCurrentLocation()
}
}
}
super.onActivityResult(requestCode, resultCode, data)
}
Финальное активити выглядит так:
package com.computingforgeeks.trucksend
import android.Manifest
import android.app.Activity
import android.content.Intent
import android.content.IntentSender
import android.content.pm.PackageManager
import android.graphics.BitmapFactory
import android.location.Address
import android.location.Geocoder
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import android.view.Menu
import android.view.MenuItem
import android.widget.Toast
import androidx.core.app.ActivityCompat
import com.google.android.gms.common.api.ApiException
import com.google.android.gms.common.api.ResolvableApiException
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.BitmapDescriptorFactory
import com.google.android.gms.maps.model.CameraPosition
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.MarkerOptions
import com.karumi.dexter.Dexter
import com.karumi.dexter.PermissionToken
import com.karumi.dexter.listener.PermissionDeniedResponse
import com.karumi.dexter.listener.PermissionGrantedResponse
import com.karumi.dexter.listener.PermissionRequest
import com.karumi.dexter.listener.single.PermissionListener
import kotlinx.android.synthetic.main.activity_main.*
import java.io.IOException
import java.util.*
class MainActivity : AppCompatActivity(), OnMapReadyCallback, PermissionListener {
companion object {
const val REQUEST_CHECK_SETTINGS = 43
}
private lateinit var googleMap: GoogleMap
private lateinit var fusedLocationProviderClient: FusedLocationProviderClient
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment?
mapFragment!!.getMapAsync(this)
fusedLocationProviderClient = FusedLocationProviderClient(this)
}
override fun onMapReady(map: GoogleMap?) {
googleMap = map?: return
if (isPermissionGiven()){
googleMap.isMyLocationEnabled = true
googleMap.uiSettings.isMyLocationButtonEnabled = true
googleMap.uiSettings.isZoomControlsEnabled = true
getCurrentLocation()
} else {
givePermission()
}
}
private fun isPermissionGiven(): Boolean{
return ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
}
private fun givePermission() {
Dexter.withActivity(this)
.withPermission(Manifest.permission.ACCESS_FINE_LOCATION)
.withListener(this)
.check()
}
override fun onPermissionGranted(response: PermissionGrantedResponse?) {
getCurrentLocation()
}
override fun onPermissionRationaleShouldBeShown(
permission: PermissionRequest?,
token: PermissionToken?
) {
token!!.continuePermissionRequest()
}
override fun onPermissionDenied(response: PermissionDeniedResponse?) {
Toast.makeText(this, "Permission required for showing location", Toast.LENGTH_LONG).show()
finish()
}
private fun getCurrentLocation() {
val locationRequest = LocationRequest()
locationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
locationRequest.interval = (10 * 1000).toLong()
locationRequest.fastestInterval = 2000
val builder = LocationSettingsRequest.Builder()
builder.addLocationRequest(locationRequest)
val locationSettingsRequest = builder.build()
val result = LocationServices.getSettingsClient(this).checkLocationSettings(locationSettingsRequest)
result.addOnCompleteListener { task ->
try {
val response = task.getResult(ApiException::class.java)
if (response!!.locationSettingsStates.isLocationPresent){
getLastLocation()
}
} catch (exception: ApiException) {
when (exception.statusCode) {
LocationSettingsStatusCodes.RESOLUTION_REQUIRED -> try {
val resolvable = exception as ResolvableApiException
resolvable.startResolutionForResult(this, REQUEST_CHECK_SETTINGS)
} catch (e: IntentSender.SendIntentException) {
} catch (e: ClassCastException) {
}
LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE -> { }
}
}
}
}
private fun getLastLocation() {
fusedLocationProviderClient.lastLocation
.addOnCompleteListener(this) { task ->
if (task.isSuccessful && task.result != null) {
val mLastLocation = task.result
var address = "No known address"
val gcd = Geocoder(this, Locale.getDefault())
val addresses: List<Address>
try {
addresses = gcd.getFromLocation(mLastLocation!!.latitude, mLastLocation.longitude, 1)
if (addresses.isNotEmpty()) {
address = addresses[0].getAddressLine(0)
}
} catch (e: IOException) {
e.printStackTrace()
}
val icon = BitmapDescriptorFactory.fromBitmap(BitmapFactory.decodeResource(this.resources, R.drawable.ic_pickup))
googleMap.addMarker(
MarkerOptions()
.position(LatLng(mLastLocation!!.latitude, mLastLocation.longitude))
.title("Current Location")
.snippet(address)
.icon(icon)
)
val cameraPosition = CameraPosition.Builder()
.target(LatLng(mLastLocation.latitude, mLastLocation.longitude))
.zoom(17f)
.build()
googleMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition))
} else {
Toast.makeText(this, "No current location found", Toast.LENGTH_LONG).show()
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when (requestCode) {
REQUEST_CHECK_SETTINGS -> {
if (resultCode == Activity.RESULT_OK) {
getCurrentLocation()
}
}
}
super.onActivityResult(requestCode, resultCode, data)
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_main, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.action_settings -> true
else -> super.onOptionsItemSelected(item)
}
}
}
Теперь запустите ваше приложение, чтобы увидеть ваше текущее местоположение.
Если вы уже открывали доступ к своему местоположению другому пользователю (или он открывал доступ вам), вы можете снова узнать, где находится этот пользователь, с помощью Google Карт.
спасибо Капитан