Skip to content

Instantly share code, notes, and snippets.

@wangerekaharun
Created September 3, 2020 16:11
Show Gist options
  • Save wangerekaharun/1d664bf8b4efa1a3091e2622f72da4e1 to your computer and use it in GitHub Desktop.
Save wangerekaharun/1d664bf8b4efa1a3091e2622f72da4e1 to your computer and use it in GitHub Desktop.
package ke.co.appslab.bring.ui.views.fragments
import android.Manifest
import android.annotation.SuppressLint
import android.app.Activity.RESULT_CANCELED
import android.app.Activity.RESULT_OK
import android.content.Intent
import android.content.IntentSender
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Bundle
import android.os.Looper
import android.provider.Settings
import android.util.Log
import android.view.View
import androidx.core.app.ActivityCompat
import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.OnLifecycleEvent
import androidx.navigation.fragment.findNavController
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.LatLng
import ke.co.appslab.bring.BuildConfig
import ke.co.appslab.bring.R
import ke.co.appslab.bring.data.models.geoaddress.AddressResponse
import ke.co.appslab.bring.ui.viewmodels.AddressViewModel
import ke.co.appslab.bring.utils.NetworkResult
import ke.co.appslab.bring.utils.createLocationRequest
import ke.co.appslab.bring.utils.toast
import org.koin.androidx.viewmodel.ext.android.viewModel
class MapFragment : Fragment(R.layout.fragment_map), OnMapReadyCallback {
private val locationRequest: LocationRequest by lazy {
createLocationRequest()
}
private val fusedLocationProviderClient: FusedLocationProviderClient by lazy {
LocationServices.getFusedLocationProviderClient(requireContext())
}
private val settingsClient: SettingsClient by lazy {
LocationServices.getSettingsClient(requireActivity())
}
private lateinit var locationSettingsRequest: LocationSettingsRequest
private lateinit var locationCallback: LocationCallback
private lateinit var googleMap: GoogleMap
private val addressViewModel: AddressViewModel by viewModel()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val mapFragment = childFragmentManager
.findFragmentById(R.id.mapFragment) as SupportMapFragment
mapFragment.getMapAsync(this)
createLocationCallback()
createLocationRequest()
buildLocationSettingsRequest()
observeAddressResponse()
receiveLocationUpdates()
}
private fun observeAddressResponse() {
addressViewModel.addressResponse.observe(viewLifecycleOwner) { addressReponse ->
when (addressReponse) {
is NetworkResult.Success -> showBottomSheet(addressReponse.data)
is NetworkResult.Error -> requireActivity().toast(addressReponse.exception.localizedMessage)
}
}
}
private fun showBottomSheet(addressResponse: AddressResponse?) {
addressResponse?.let {
val locationDetailsAction =
MapFragmentDirections.actionMapFragmentToLocationDetailsFragment(it)
findNavController().navigate(locationDetailsAction)
}
}
override fun onMapReady(map: GoogleMap) {
googleMap = map
googleMap.uiSettings.isCompassEnabled = false
googleMap.apply {
uiSettings.isCompassEnabled = false
uiSettings.isMyLocationButtonEnabled = true
}
receiveLocationUpdates()
}
private fun buildLocationSettingsRequest() {
val builder = LocationSettingsRequest.Builder()
builder.addLocationRequest(locationRequest)
locationSettingsRequest = builder.build()
}
private fun receiveLocationUpdates() {
when {
checkPermissions() -> startLocationUpdates()
!checkPermissions() -> requestPermissions()
}
}
private fun checkPermissions(): Boolean {
val permissionState = ActivityCompat.checkSelfPermission(
requireActivity(),
Manifest.permission.ACCESS_FINE_LOCATION
);
return permissionState == PackageManager.PERMISSION_GRANTED;
}
private fun requestPermissions() {
val shouldProvideRationale = ActivityCompat.shouldShowRequestPermissionRationale(
requireActivity(),
Manifest.permission.ACCESS_FINE_LOCATION
)
// Provide an additional rationale to the user. This would happen if the user denied the
// request previously, but didn't check the "Don't ask again" checkbox.
if (!shouldProvideRationale) {
ActivityCompat.requestPermissions(
requireActivity(),
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
REQUEST_PERMISSIONS_REQUEST_CODE
)
}
}
@SuppressLint("MissingPermission")
private fun startLocationUpdates() {
settingsClient.checkLocationSettings(locationSettingsRequest)
.addOnSuccessListener(requireActivity()) {
fusedLocationProviderClient.requestLocationUpdates(
locationRequest,
locationCallback, Looper.myLooper()
)
}
.addOnFailureListener(requireActivity()) { e ->
when ((e as ApiException).statusCode) {
LocationSettingsStatusCodes.RESOLUTION_REQUIRED -> {
try {
// Show the dialog by calling startResolutionForResult(), and check the
// result in onActivityResult().
val rae = e as ResolvableApiException
rae.startResolutionForResult(requireActivity(), REQUEST_CHECK_SETTINGS)
} catch (sie: IntentSender.SendIntentException) {
Log.i("MapFragment", "PendingIntent unable to execute request.")
}
}
LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE -> {
val errorMessage =
"Location settings are inadequate, and cannot be " + "fixed here. Fix in Settings."
requireContext().toast(errorMessage)
}
}
}
}
private fun createLocationCallback() {
locationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult) {
locationResult.locations.forEach {
val currentLatLng = LatLng(it.latitude, it.longitude)
googleMap.animateCamera(
CameraUpdateFactory.newLatLngZoom(
currentLatLng,
DEFAULT_ZOOM.toFloat()
)
)
googleMap.setOnCameraIdleListener {
//enable google map scroll
googleMap.uiSettings.isScrollGesturesEnabled = true
val positionLatLng = googleMap.cameraPosition.target
val addressLatLng =
positionLatLng.latitude.toString() + "," + positionLatLng.longitude.toString()
// get address
getAddress(addressLatLng)
}
}
}
}
}
private fun getAddress(addressLatLng: String) {
addressViewModel.fetchAddress(addressLatLng, getString(R.string.google_maps_key))
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when (requestCode) {
REQUEST_CHECK_SETTINGS -> {
when (resultCode) {
RESULT_OK -> {
requireActivity().toast("Permissions!")
receiveLocationUpdates()
}
RESULT_CANCELED -> {
requireActivity().toast("Permission Denied!")
}
}
}
}
super.onActivityResult(requestCode, resultCode, data)
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
if (requestCode == REQUEST_PERMISSIONS_REQUEST_CODE) {
when {
grantResults.isEmpty() -> // If user interaction was interrupted, the permission request is cancelled and you
// receive empty arrays.
Log.i("MapFragment", "User interaction was cancelled.")
grantResults[0] == PackageManager.PERMISSION_GRANTED -> {
Log.i(
"MapFragment",
"Permission granted, updates requested, starting location updates"
)
startLocationUpdates()
requireActivity().toast("Permissions!")
}
else -> {
// Permission denied.
val intent = Intent()
intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
val uri = Uri.fromParts(
"package",
BuildConfig.APPLICATION_ID, null
)
intent.data = uri
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
startActivity(intent)
}
}
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun stopLocationUpdates() {
fusedLocationProviderClient.removeLocationUpdates(locationCallback)
.addOnCompleteListener(requireActivity()) {
}
}
companion object {
const val REQUEST_PERMISSIONS_REQUEST_CODE = 30
const val REQUEST_CHECK_SETTINGS = 0x1
const val DEFAULT_ZOOM = 17
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment