Kotlin - Menentukan Rute Terdekat dari Lokasi menggunakan Google Map

Tutorial ini membahas mengenai bagaimana membuat rute terdekat dari lokasi menggunakan google map, dimana untuk menentukan rute terdekat dari lokasi tersebut membutuhkan API dari Google Developers anda bisa membaca dokumentasi dibagian sini.

Java - Menentukan Rute Terdekat dari Lokasi menggunakan Google Map

Sebelum Anda mulai membuat proyek ini, Anda membutuhkan API keys supaya aplikasi dapat berjalan dengan Google Maps, untuk membuat Google Maps API key disini

API yang digunakan untuk membuat rute terdekat dari lokasi, yang dimana untuk output format menggunakan json.

http://maps.googleapis.com/maps/api/directions/json?parameters

Dari API diatas ada parameter yang harus diprelukan antara lain:

  • origin - Lokasi asal dengan mengisikan berupa alamat, nilai dari garis lintang (Latitude) dan bujur (Longitude) atau ID tempat dimana anda ingin menentukan rute.
    • Jika anda mengisikan berupa alamat anda perlu memasukan pada parameter origin
      origin=Mataram
    • Jika anda mengisikan dengan nilai lintang dan bujur anda harus memasukan bagian parameter origin
      origin=-8.594848, 116.105390
    • Jika anda mengisikan dengan nilai ID anda harus memasukan bagian parameter origin
      origin=place_id:DhIJSS-JXXauEmsRUcIaWtf5MzE
  • destination - Lokasi tujuan dengan mengisikan berupa alamat, nilai dari garis lintang (Latitude) dan bujur (Longitude) atau ID, untuk opsi destination hampirsama pengisian dengan origin.
  • key - Kunci API aplikasi yang harus anda buat untuk ditempatkan di parameter key

Menyiapkan Proyek

Langkah pertama masukan dependencies dibagian file build gradle

implementation 'com.android.support:support-v4:28.0.0'
implementation 'com.android.support:design:28.0.0'
implementation 'com.google.android.gms:play-services-maps:16.1.0'
implementation 'com.google.android.gms:play-services-location:16.0.0'
implementation 'com.google.android.gms:play-services-places:16.0.0'
implementation 'com.google.android.libraries.places:places-compat:1.1.0'
implementation 'com.google.maps.android:android-maps-utils:0.5+'

Download resource disini dan tempatkan dibagain drawable

Langkah Membuat Proyek

Bagian Manifests Masukan Api Keys dibagain value dari metadata.

.....
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

<application
.....
<meta-data
    android:name="com.google.android.geo.API_KEY"
    android:value="YOU HERE KEYS" />

<service
   android:name=".service.FetchAddressIntentService"
   android:exported="false"/>

</application>

Menambahkan Permission GPS, fungsinya saat aplikasi dijalankan akan tampil dialog untuk meminta mengaktifkan GPS.

package com.kodetr.googlemap.utils

import android.app.Activity
import android.content.Context
import android.content.IntentSender
import android.location.LocationManager
import android.os.Bundle
import android.util.Log
import android.widget.Toast

import com.google.android.gms.common.ConnectionResult
import com.google.android.gms.common.api.GoogleApiClient
import com.google.android.gms.common.api.PendingResult
import com.google.android.gms.common.api.Status
import com.google.android.gms.location.LocationRequest
import com.google.android.gms.location.LocationServices
import com.google.android.gms.location.LocationSettingsRequest
import com.google.android.gms.location.LocationSettingsResult
import com.google.android.gms.location.LocationSettingsStatusCodes

/**
 * Created by kodetr on 09/05/19.
 */

class PermissionGPS(private val activity: Activity) {

    private var googleApiClient: GoogleApiClient? = null

    init {
        // Todo Location Already on  ... start
        val manager = activity.getSystemService(Context.LOCATION_SERVICE) as LocationManager
        if (manager.isProviderEnabled(LocationManager.GPS_PROVIDER) && hasGPSDevice(activity)) {
            // Toast.makeText(activity, "Gps already enabled", Toast.LENGTH_SHORT).show();
        }

        // Todo Location Already on  ... end
        if (!hasGPSDevice(activity)) {
            Toast.makeText(activity, "Gps tidak mendukung", Toast.LENGTH_SHORT).show()
        }

        if (!manager.isProviderEnabled(LocationManager.GPS_PROVIDER) && hasGPSDevice(activity)) {
            Log.e("TAG", "Gps already enabled")
            enableLoc()
        } else {
            Log.e("TAG", "Gps already enabled")
        }
    }

    private fun hasGPSDevice(context: Context): Boolean {
        val mgr = context
                .getSystemService(Context.LOCATION_SERVICE) as LocationManager
        val providers = mgr.allProviders ?: return false
        return providers.contains(LocationManager.GPS_PROVIDER)
    }

    private fun enableLoc() {
        if (googleApiClient == null) {
            googleApiClient = GoogleApiClient.Builder(activity)
                    .addApi(LocationServices.API)
                    .addConnectionCallbacks(object : GoogleApiClient.ConnectionCallbacks {
                        override fun onConnected(bundle: Bundle?) {

                        }

                        override fun onConnectionSuspended(i: Int) {
                            googleApiClient!!.connect()
                        }
                    })
                    .addOnConnectionFailedListener { connectionResult -> Log.d("Location error", "Location error " + connectionResult.errorCode) }.build()
            googleApiClient!!.connect()
        }

        val locationRequest = LocationRequest.create()
        locationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
        locationRequest.interval = (30 * 1000).toLong()
        locationRequest.fastestInterval = (5 * 1000).toLong()
        val builder = LocationSettingsRequest.Builder()
                .addLocationRequest(locationRequest)

        builder.setAlwaysShow(true)

        val result = LocationServices.SettingsApi.checkLocationSettings(googleApiClient, builder.build())
        result.setResultCallback { result1 ->
            val status = result1.status
            when (status.statusCode) {
                LocationSettingsStatusCodes.RESOLUTION_REQUIRED -> try {
                    status.startResolutionForResult(activity, REQUEST_LOCATION)
                } catch (e: IntentSender.SendIntentException) {
                }

            }
        }
    }

    companion object {
        internal const val REQUEST_LOCATION = 199
    }
}

Membuat validasi supaya terhubung ke jaringan internet dengan membuat fungsi check koneksi, kasusnya jika terhubung ke jaringan maka aplikasi diteruskan jika tidak maka keluar dari aplikasi dengan popup “Kesalahan jariangn periksa koneksi anda”.

package com.kodetr.googlemap.utils

import android.content.Context
import android.net.ConnectivityManager
import android.net.NetworkInfo

/**
 * Created by kodetr on 18/05/19.
 */

object Connections {
    fun checkConnection(context: Context): Boolean {
        val connMgr = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
        val activeNetworkInfo = connMgr.activeNetworkInfo
        if (activeNetworkInfo != null) {
            if (activeNetworkInfo.type == ConnectivityManager.TYPE_WIFI) {
                // connected to wifi
                return true
            } else if (activeNetworkInfo.type == ConnectivityManager.TYPE_MOBILE) {
                // connected to the mobile provider's data plan
                return true
            }
        }
        return false
    }
}

Selanjutnya membuat penampungan konstan, jadi anda tidak menginginkan anda bisa menerapkan langsung pada aplikasi anda.

package com.kodetr.googlemap.utils

/**
 * Created by kodetr on 19/05/19.
 */

object Constants {
    const val DIRECTION_URL_API = "https://maps.googleapis.com/maps/api/directions/json?"
    private const val PACKAGE_NAME = "com.kodetr.googlemap"
    const val RECEIVER = "$PACKAGE_NAME.RECEIVER"
    const val RESULT_DATA_KEY = "$PACKAGE_NAME.RESULT_DATA_KEY"
    const val LOCATION_DATA_EXTRA = "$PACKAGE_NAME.LOCATION_DATA_EXTRA"
    const val SUCCESS_RESULT = 0
    const val FAILURE_RESULT = 1
}

Membuat aksi dimana digunakan untuk mengambil alamat menggunakan intent dengan menerima berupa result dari lokasi yang ditentukan oleh kordinat blue point pada GPS, untuk menerapkannya dengan membuat Intent Service dan menjalankan dengan memanggil service pada manifest yang sudah diterapkan pada langkah sebelumnya.

package com.kodetr.googlemap.service

import android.app.IntentService
import android.content.Intent
import android.location.Address
import android.location.Geocoder
import android.location.Location
import android.os.Bundle
import android.os.ResultReceiver
import android.text.TextUtils
import android.util.Log

import com.kodetr.googlemap.R
import com.kodetr.googlemap.utils.Constants

import java.io.IOException
import java.util.ArrayList
import java.util.Locale

/**
 * Asynchronously handles an intent using a worker thread. Receives a ResultReceiver object and a
 * location through an intent. Tries to fetch the address for the location using a Geocoder, and
 * sends the result to the ResultReceiver.
 */
/**
 * This constructor is required, and calls the super IntentService(String)
 * constructor with the name for a worker thread.
 */
class FetchAddressIntentService : IntentService(TAG) {

    /**
     * The receiver where results are forwarded from this service.
     */
    private var receiver: ResultReceiver? = null

    /**
     * Tries to get the location address using a Geocoder. If successful, sends an address to a
     * result receiver. If unsuccessful, sends an error message instead.
     * Note: [ResultReceiver] in * MainActivity is defined to
     * process the content sent from this service.
     *
     *
     * This service calls this method from the default worker thread with the intent that started
     * the service. When this method returns, the service automatically stops.
     */
    override fun onHandleIntent(intent: Intent?) {
        var errorMessage = ""

        receiver = intent!!.getParcelableExtra(Constants.RECEIVER)

        if (receiver == null) {
            Log.wtf(TAG, "No receiver received. There is nowhere to send the results.")
            return
        }

        val location = intent.getParcelableExtra<Location>(Constants.LOCATION_DATA_EXTRA)

        if (location == null) {
            errorMessage = getString(R.string.no_location_data_provided)
            Log.wtf(TAG, errorMessage)
            deliverResultToReceiver(Constants.FAILURE_RESULT, errorMessage)
            return
        }

        val geocoder = Geocoder(this, Locale.getDefault())
        var addresses: List<Address>? = null

        try {
            addresses = geocoder.getFromLocation(
                    location.latitude,
                    location.longitude,
                    1)
        } catch (ioException: IOException) {
            errorMessage = getString(R.string.service_not_available)
            Log.e(TAG, errorMessage, ioException)
        } catch (illegalArgumentException: IllegalArgumentException) {
            errorMessage = getString(R.string.invalid_lat_long_used)
            Log.e(TAG, errorMessage + ". " +
                    "Latitude = " + location.latitude +
                    ", Longitude = " + location.longitude, illegalArgumentException)
        }

        if (addresses == null || addresses.isEmpty()) {
            deliverResultToReceiver(Constants.FAILURE_RESULT, errorMessage)
        } else {
            val address = addresses[0]
            val addressFragments = ArrayList<String>()

            for (i in 0..address.maxAddressLineIndex) {
                addressFragments.add(address.getAddressLine(i))
            }

            Log.i(TAG, getString(R.string.address_found))
            deliverResultToReceiver(Constants.SUCCESS_RESULT,
                    TextUtils.join(System.getProperty("line.separator")!!, addressFragments))
        }
    }

    /**
     * Sends a resultCode and message to the receiver.
     */
    private fun deliverResultToReceiver(resultCode: Int, message: String) {
        val bundle = Bundle()
        bundle.putString(Constants.RESULT_DATA_KEY, message)
        receiver!!.send(resultCode, bundle)
    }

    companion object {
        private const val TAG = "FetchAddressIS"
    }
}

Selanjutnya adalah membuat Parsing data dari url API dengan menggunakan lib json, Membuat model berupa interfece finder listener, class model Distance, class model Duration dan class model Route.

class Distance(var text: String, var value: Int)

class Duration(var text: String, var value: Int)

class Route {
    var distance: Distance? = null
    var duration: Duration? = null
    var endAddress: String? = null
    var endLocation: LatLng? = null
    var startAddress: String? = null
    var startLocation: LatLng? = null
    var points: List<LatLng>? = null
}

interface DirectionFinderListener {
    fun onDirectionFinderStart()
    fun onDirectionFinderSuccess(route: List<Route>)
}

Menerapkan Parsing data dari url API dengan menggunakan lib json.

package com.kodetr.googlemap.models.direction

import android.annotation.SuppressLint
import android.os.AsyncTask

import com.google.android.gms.maps.model.LatLng
import com.kodetr.googlemap.utils.Constants

import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject

import java.io.BufferedReader
import java.io.IOException
import java.io.InputStream
import java.io.InputStreamReader
import java.io.UnsupportedEncodingException
import java.net.MalformedURLException
import java.net.URL
import java.net.URLEncoder
import java.util.ArrayList

/**
 * Created by kodetr on 09/05/19.
 */

class DirectionFinder(private val listener: DirectionFinderListener, private val origin: String, private val destination: String) {

    fun execute(google_api_key: String) {
        listener.onDirectionFinderStart()
        DownloadRawData().execute(createUrl(google_api_key))
    }

    private fun createUrl(google_api_key: String): String {
        val urlOrigin = URLEncoder.encode(origin, "utf-8")
        val urlDestination = URLEncoder.encode(destination, "utf-8")

        return Constants.DIRECTION_URL_API + "origin=" + urlOrigin + "&destination=" + urlDestination + "&key=" + google_api_key
    }

    @SuppressLint("StaticFieldLeak")
    private inner class DownloadRawData : AsyncTask<String, Void, String>() {

        override fun doInBackground(vararg params: String): String? {
            val link = params[0]
            try {
                val url = URL(link)
                val `is` = url.openConnection().getInputStream()
                val buffer = StringBuilder()
                val reader = BufferedReader(InputStreamReader(`is`))

                while (reader.readLine() != null) {
                    buffer.append(reader.readLine()).append("\n")
                }

                return buffer.toString()

            } catch (e: MalformedURLException) {
                e.printStackTrace()
            } catch (e: IOException) {
                e.printStackTrace()
            }

            return null
        }

        override fun onPostExecute(res: String) {
            try {
                parseJSon(res)
            } catch (e: JSONException) {
                e.printStackTrace()
            }

        }
    }

    private fun parseJSon(data: String?) {
        if (data == null)
            return

        val routes = ArrayList<Route>()
        val jsonData = JSONObject(data)
        val jsonRoutes = jsonData.getJSONArray("routes")
        for (i in 0 until jsonRoutes.length()) {
            val jsonRoute = jsonRoutes.getJSONObject(i)
            val route = Route()

            val overview_polylineJson = jsonRoute.getJSONObject("overview_polyline")
            val jsonLegs = jsonRoute.getJSONArray("legs")
            val jsonLeg = jsonLegs.getJSONObject(0)
            val jsonDistance = jsonLeg.getJSONObject("distance")
            val jsonDuration = jsonLeg.getJSONObject("duration")
            val jsonEndLocation = jsonLeg.getJSONObject("end_location")
            val jsonStartLocation = jsonLeg.getJSONObject("start_location")

            route.distance = Distance(jsonDistance.getString("text"), jsonDistance.getInt("value"))
            route.duration = Duration(jsonDuration.getString("text"), jsonDuration.getInt("value"))
            route.endAddress = jsonLeg.getString("end_address")
            route.startAddress = jsonLeg.getString("start_address")
            route.startLocation = LatLng(jsonStartLocation.getDouble("lat"), jsonStartLocation.getDouble("lng"))
            route.endLocation = LatLng(jsonEndLocation.getDouble("lat"), jsonEndLocation.getDouble("lng"))
            route.points = decodePolyLine(overview_polylineJson.getString("points"))
            routes.add(route)
        }
        listener.onDirectionFinderSuccess(routes)
    }
}

Menambahkan fungsi decode untuk setiap poin yang ditampung dalam list berupa langitude dan longitude untuk mencari rute paling dominan dengan menampilkan berupa garis pada map.

private fun decodePolyLine(poly: String): List<LatLng> {
    val len = poly.length
    var index = 0
    val decoded = ArrayList<LatLng>()
    var lat = 0
    var lng = 0

    while (index < len) {
        var b: Int
        var shift = 0
        var result = 0
        do {
            b = poly[index++].toInt() - 63
            result = result or (b and 0x1f shl shift)
            shift += 5
        } while (b >= 0x20)
            val dlat = if (result and 1 != 0) (result shr 1).inv() else result shr 1
            lat += dlat
            shift = 0
            result = 0
        do {
            b = poly[index++].toInt() - 63
            result = result or (b and 0x1f shl shift)
            shift += 5
        } while (b >= 0x20)
            val dlng = if (result and 1 != 0) (result shr 1).inv() else result shr 1
            lng += dlng
            decoded.add(LatLng(lat / 100000.0, lng / 100000.0))
        }
    return decoded
}

Jika anda sudah mengikuti langkah dengan benar maka struktur akan seperti gambar berikut.

Bagian activity utama yaitu MapsActivity yang harus anda terapkan terlebih dahulu, berupa Permission dan cek Connection.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    checkLocationPermission()
}

if (!Connections.checkConnection(this)) {
    Toast.makeText(this, "Kesalahan jaringan periksa koneksi anda", Toast.LENGTH_SHORT).show()
    finish()
}

Buat request permission dari fungsi checkLocationPermission beserta dengan fungsi popup.

private fun checkLocationPermission(): Boolean {
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
        if (ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.ACCESS_FINE_LOCATION)) {
            ActivityCompat.requestPermissions(this,
                        arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
                        MY_PERMISSIONS_REQUEST_LOCATION)
        } else {
            ActivityCompat.requestPermissions(this,
                        arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
                        MY_PERMISSIONS_REQUEST_LOCATION)
            }
            return false
    } else {
        return true
    }
}

Membuat fungsi untuk pencarian menggunakan Auto Complete dengan penerapan pada parameter url API sebagai destination.

private fun setupAutoCompleteFragment() {
    val autocompleteFragment = fragmentManager.findFragmentById(R.id.place_autocomplete_fragment) as PlaceAutocompleteFragment
    autocompleteFragment.setOnPlaceSelectedListener(object : PlaceSelectionListener {
        override fun onPlaceSelected(place: Place) {
            searchLocation = place.latLng
        }

        override fun onError(status: Status) {
           Log.e("Error", status.statusMessage)
        }
    })
}

Mencari rute terdekat dengan melakukan aksi pada fungsi DirectionFinder dengan memasukan parameter origin, destination dan api key kedalam fungsi tersebut.

DirectionFinder(MapsActivity.this, origin, destination , api_key);

Menerapkan proses Direction Finder untuk menampilkan distance dan duration dengan origin dari blue poin ke destination yang ditandai dengan marker merah dengan menampilkan berupa garis dengan warna merah.

override fun onDirectionFinderSuccess(routes: List<Route>) {
    progressDialog!!.dismiss()
    polyLinePaths = ArrayList()
    originMarkers = ArrayList()
    destinationMarker = ArrayList()

    for (route in routes) {
        map!!.moveCamera(CameraUpdateFactory.newLatLngZoom(route.startLocation, 15.5f))
        (findViewById<View>(R.id.tvDistance) as TextView).text = route.distance!!.text
        (findViewById<View>(R.id.tvTime) as TextView).text = route.duration!!.text

        destinationMarker!!.add(map!!.addMarker(MarkerOptions()
                .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_RED))
                .title(route.endAddress)
                .position(route.endLocation!!)))

        val polylineOptions = PolylineOptions()
                .geodesic(true)
                .color(resources.getColor(R.color.colorPrimary))
                .width(10f)

        for (i in route.points!!.indices) {
            polylineOptions.add(route.points!![i])
        }

        polyLinePaths!!.add(map!!.addPolyline(polylineOptions))
    }
}

Lebih jelasnya ada bisa melihat vidio diatas dan jika langkah-langkah diatas sudah diikuti dengan benar untuk source code lengkapnya seperti dibawah.

package com.kodetr.googlemap

import android.Manifest
import android.annotation.SuppressLint
import android.app.ProgressDialog
import android.content.Intent
import android.content.pm.PackageManager
import android.location.Geocoder
import android.location.Location
import android.os.*
import android.support.design.widget.FloatingActionButton
import android.support.design.widget.Snackbar
import android.support.v4.app.ActivityCompat
import android.support.v4.content.ContextCompat
import android.support.v7.app.AppCompatActivity
import android.util.Log
import android.view.View
import android.widget.TextView
import android.widget.Toast
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.MapFragment
import com.google.android.gms.maps.OnMapReadyCallback
import com.google.android.gms.maps.model.*
import com.google.android.libraries.places.compat.Place
import com.google.android.libraries.places.compat.ui.PlaceAutocompleteFragment
import com.google.android.libraries.places.compat.ui.PlaceSelectionListener
import com.kodetr.googlemap.models.direction.DirectionFinder
import com.kodetr.googlemap.models.direction.DirectionFinderListener
import com.kodetr.googlemap.models.direction.Route
import com.kodetr.googlemap.service.FetchAddressIntentService
import com.kodetr.googlemap.utils.Connections
import com.kodetr.googlemap.utils.Constants
import com.kodetr.googlemap.utils.PermissionGPS
import java.util.*

/**
 * Created by kodetr on 09/05/19.
 */

class MapsActivity : AppCompatActivity(), GoogleMap.OnMarkerClickListener, GoogleMap.OnMapClickListener, OnMapReadyCallback, DirectionFinderListener {

    private val LocationA = LatLng(-8.594848, 116.105390)

    private var map: GoogleMap? = null
    private var fusedLocationProvider: FusedLocationProviderClient? = null
    private var locationRequest: LocationRequest? = null
    private var locationCallback: LocationCallback? = null
    private var locationgps: Location? = null
    private var resultReceiver: ResultReceiver? = null
    private var selectedMarker: Marker? = null
    private var searchLocation: LatLng? = null

    private var originMarkers: List<Marker>? = ArrayList()
    private var destinationMarker: MutableList<Marker>? = ArrayList()
    private var polyLinePaths: MutableList<Polyline>? = ArrayList()

    private var progressDialog: ProgressDialog? = null

    @SuppressLint("SetTextI18n")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_maps)

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            checkLocationPermission()
        }

        if (!Connections.checkConnection(this)) {
            Toast.makeText(this, "Kesalahan jaringan periksa koneksi anda", Toast.LENGTH_SHORT).show()
            finish()
        }

        init()

        fusedLocationProvider = LocationServices.getFusedLocationProviderClient(this)
        resultReceiver = object : ResultReceiver(Handler()) {
            override fun onReceiveResult(resultCode: Int, resultData: Bundle) {
                val addressOutput = resultData.getString(Constants.RESULT_DATA_KEY)
                Toast.makeText(applicationContext, addressOutput, Toast.LENGTH_SHORT).show()
            }
        }

        locationgps = Location("Point A")
    }

    @SuppressLint("SetTextI18n")
    private fun init() {

        setupAutoCompleteFragment()

        val fa = findViewById<FloatingActionButton>(R.id.fblocation)
        fa.setOnClickListener {
            try {
                val origin = locationgps!!.latitude.toString() + "," + locationgps!!.longitude
                DirectionFinder(this@MapsActivity, origin, searchLocation!!.latitude.toString() + "," + searchLocation!!.longitude).execute(getString(R.string.google_maps_key))
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }

        val ft = findViewById<FloatingActionButton>(R.id.fbsatelit)
        ft.setOnClickListener {
            if (map != null) {
                val MapType = map!!.mapType
                if (MapType == 1) {
                    ft.setImageResource(R.drawable.ic_satellite_off)
                    map!!.mapType = GoogleMap.MAP_TYPE_SATELLITE
                } else {
                    ft.setImageResource(R.drawable.ic_satellite_on)
                    map!!.mapType = GoogleMap.MAP_TYPE_NORMAL
                }
            }
        }

        val fm = findViewById<FloatingActionButton>(R.id.fbgps)
        fm.setOnClickListener {
            getDeviceLocation(true)
            if (!Geocoder.isPresent()) {
                Toast.makeText(this, R.string.no_geocoder_available, Toast.LENGTH_SHORT).show()
            } else {
                showAddress()
            }
        }

        locationCallback = object : LocationCallback() {
            override fun onLocationResult(locationResult: LocationResult?) {
                if (locationResult == null)
                    return

                for (locationUpdate in locationResult.locations) {
                    locationgps = locationUpdate
                    if (gpsFirstOn) {
                        gpsFirstOn = false
                        getDeviceLocation(true)
                    }
                }
            }
        }

        locationRequest = LocationRequest()
        locationRequest!!.interval = UPDATE_INTERVAL
        locationRequest!!.fastestInterval = FASTEST_UPDATE_INTERVAL
        locationRequest!!.priority = LocationRequest.PRIORITY_HIGH_ACCURACY

        val mapFragment = fragmentManager.findFragmentById(R.id.map) as MapFragment
        mapFragment.getMapAsync(this)
    }

    private fun setupAutoCompleteFragment() {
        val autocompleteFragment = fragmentManager.findFragmentById(R.id.place_autocomplete_fragment) as PlaceAutocompleteFragment
        autocompleteFragment.setOnPlaceSelectedListener(object : PlaceSelectionListener {
            override fun onPlaceSelected(place: Place) {
                searchLocation = place.latLng
            }

            override fun onError(status: Status) {
                Log.e("Error", status.statusMessage)
            }
        })
    }

    override fun onMapReady(gMap: GoogleMap) {
        map = gMap

        map!!.setOnMapClickListener(this)
        map!!.setOnMarkerClickListener(this)

        map!!.moveCamera(CameraUpdateFactory.newLatLngZoom(LocationA, DEFAULT_ZOOM))

        map!!.uiSettings.isMapToolbarEnabled = false
        map!!.uiSettings.isMyLocationButtonEnabled = false
        //  map.getUiSettings().setCompassEnabled(false);

        // TODO : location
        map!!.projection.visibleRegion

        if (!checkPermission())
            requestPermission()

        getDeviceLocation(false)
    }

    override fun onDirectionFinderStart() {
        progressDialog = ProgressDialog.show(this, "Tunggu sebentar", "Mencari lokasi terdekat..", true)
        if (originMarkers != null) {
            for (marker in originMarkers!!) {
                marker.remove()
            }
        }
        if (destinationMarker != null) {
            for (marker in destinationMarker!!) {
                marker.remove()
            }
        }
        if (polyLinePaths != null) {
            for (polylinePath in polyLinePaths!!) {
                polylinePath.remove()
            }
        }
    }

    override fun onDirectionFinderSuccess(routes: List<Route>) {
        progressDialog!!.dismiss()
        polyLinePaths = ArrayList()
        originMarkers = ArrayList()
        destinationMarker = ArrayList()

        for (route in routes) {
            map!!.moveCamera(CameraUpdateFactory.newLatLngZoom(route.startLocation, 15.5f))
            (findViewById<View>(R.id.tvDistance) as TextView).text = route.distance!!.text
            (findViewById<View>(R.id.tvTime) as TextView).text = route.duration!!.text

            destinationMarker!!.add(map!!.addMarker(MarkerOptions()
                    .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_RED))
                    .title(route.endAddress)
                    .position(route.endLocation!!)))

            val polylineOptions = PolylineOptions()
                    .geodesic(true)
                    .color(resources.getColor(R.color.colorPrimary))
                    .width(10f)

            for (i in route.points!!.indices) {
                polylineOptions.add(route.points!![i])
            }

            polyLinePaths!!.add(map!!.addPolyline(polylineOptions))
        }
    }

    private fun getDeviceLocation(MyLocation: Boolean) {
        if (!MyLocation)

            if (checkPermission()) {
                if (map != null)
                    map!!.isMyLocationEnabled = true

                val locationResult = fusedLocationProvider!!.lastLocation
                locationResult.addOnCompleteListener(this) { task ->
                    if (task.isSuccessful && task.result != null) {
                        // lastKnownLocation = task.getResult();
                    } else {
                        Log.w(TAG, "getLastLocation:exception", task.exception)
                        Toast.makeText(this, R.string.no_location_detected, Toast.LENGTH_SHORT).show()
                    }
                }
            } else
                Log.d(TAG, "Current location is null. Permission Denied.")
    }

    override fun onMapClick(point: LatLng) {
        selectedMarker = null
    }

    override fun onMarkerClick(marker: Marker): Boolean {
        if (marker == selectedMarker) {
            selectedMarker = null
            return true
        }

        Toast.makeText(this, marker.title, Toast.LENGTH_SHORT).show()
        selectedMarker = marker
        return false
    }

    private fun showAddress() {
        val intent = Intent(this, FetchAddressIntentService::class.java)
        intent.putExtra(Constants.RECEIVER, resultReceiver)
        intent.putExtra(Constants.LOCATION_DATA_EXTRA, locationgps)
        startService(intent)
    }

    override fun onStart() {
        super.onStart()
        if (Connections.checkConnection(this)) {
            PermissionGPS(this)
        }
    }

    override fun onRestart() {
        super.onRestart()
        if (Connections.checkConnection(this)) {
            PermissionGPS(this)
        }
    }

    override fun onResume() {
        super.onResume()
        if (Connections.checkConnection(this)) {
            if (checkPermission())
                fusedLocationProvider!!.requestLocationUpdates(locationRequest, locationCallback!!, Looper.myLooper())
        }
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
        Log.i(TAG, "onRequestPermissionResult")
        if (requestCode == LOCATION_PERMISSION_REQUEST_CODE) {
            when {
                grantResults.isEmpty() -> Log.i(TAG, "User interaction was cancelled.") // grantResults.length > 0
                grantResults[0] == PackageManager.PERMISSION_GRANTED -> getDeviceLocation(false)
                else -> showSnackbar(R.string.permission_rationale, Snackbar.LENGTH_INDEFINITE, android.R.string.ok
                ) { requestPermission() }
            }
        }
    }

    private fun showSnackbar(textStringId: Int, length: Int, actionStringId: Int, listener: (Any) -> Unit) {
        val snackbar = Snackbar.make(findViewById(android.R.id.content), textStringId, length)
        snackbar.setAction(actionStringId, listener)
        snackbar.show()
    }

    private fun requestPermission() {
        ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), LOCATION_PERMISSION_REQUEST_CODE)

    }

    private fun checkPermission(): Boolean {
        return ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
    }

    private fun checkLocationPermission(): Boolean {
        if (ContextCompat.checkSelfPermission(this,
                        Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {

            if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                            Manifest.permission.ACCESS_FINE_LOCATION)) {

                ActivityCompat.requestPermissions(this,
                        arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
                        MY_PERMISSIONS_REQUEST_LOCATION)

            } else {
                ActivityCompat.requestPermissions(this,
                        arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
                        MY_PERMISSIONS_REQUEST_LOCATION)
            }
            return false
        } else {
            return true
        }
    }

    companion object {

        private val TAG = MapsActivity::class.java.simpleName

        private const val DEFAULT_ZOOM = 9.5f

        private const val UPDATE_INTERVAL: Long = 500
        private const val FASTEST_UPDATE_INTERVAL = UPDATE_INTERVAL / 5
        private const val LOCATION_PERMISSION_REQUEST_CODE = 1
        private var gpsFirstOn = true

        const val MY_PERMISSIONS_REQUEST_LOCATION = 99
    }
}

Hasil Output

Hasil akhirnya anda bisa lihat gambar dibawah.

Demikian yang dapat saya sampaikan dari artikel ini semoga bermanfaat, jika anda mengalami kesulitan anda bisa melihat vidio yang sudah disediakan atau melalui komentar dibawah, selamat mencoba.

Share Comments
comments powered by Disqus