Skip to content

Instantly share code, notes, and snippets.

@kyodgorbek
Created September 7, 2022 10:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kyodgorbek/0185994f5691fb73f8297106750df54f to your computer and use it in GitHub Desktop.
Save kyodgorbek/0185994f5691fb73f8297106750df54f to your computer and use it in GitHub Desktop.
package com.chargeatfriends.android.ui.map
import android.Manifest
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
import android.graphics.Color
import android.location.Location
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.ImageButton
import android.widget.TextView
import android.widget.Toast
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.navigation.fragment.findNavController
import com.chargeatfriends.android.R
import com.chargeatfriends.android.model.ChargePoint
import com.chargeatfriends.android.model.ChargePointType
import com.chargeatfriends.android.model.appCommon
import com.chargeatfriends.android.model.map.BitmapHelper
import com.chargeatfriends.android.model.map.CafMarker
import com.chargeatfriends.android.model.stripe.BackendApiFactory
import com.chargeatfriends.android.network.SET_MAP_PERMISSIONS
import com.chargeatfriends.android.network.baseUrl
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationServices
import com.google.android.gms.maps.CameraUpdateFactory
import com.google.android.gms.maps.GoogleMap
import com.google.android.gms.maps.MapView
import com.google.android.gms.maps.OnMapReadyCallback
import com.google.android.gms.maps.model.*
import com.google.android.libraries.places.api.model.Place
import com.google.android.libraries.places.api.net.PlacesClient
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import com.google.maps.android.clustering.ClusterManager
//import kotlinx.android.synthetic.main.map_fragment.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.*
class MapFragment : Fragment(), OnMapReadyCallback, GoogleMap.OnMarkerClickListener, GoogleMap.OnInfoWindowClickListener {
private var locationPermissionGranted: Boolean = false
private var mMap: MapView? = null
private lateinit var map: GoogleMap
private val cafMarker:MutableList<CafMarker> = mutableListOf()
private var isRegistered: Boolean = false
// The entry point to the Places API.
private lateinit var placesClient: PlacesClient
private var lastKnownLocation: Location? = null
private var cameraPosition: CameraPosition? = null
private lateinit var qrButton: ImageButton
private lateinit var textViewAvailability: TextView
// The entry point to the Fused Location Provider.
private lateinit var fusedLocationProviderClient: FusedLocationProviderClient
private val defaultLocation = LatLng(51.1633611, 10.4476833)
private val defaultZoom = 10.0
private lateinit var StationInfo: ConstraintLayout
lateinit var CurrentChargePoint: ChargePoint
private var ChargePoints: MutableList<ChargePoint> = mutableListOf<ChargePoint>()
private val TAG = "MapFragment"
private lateinit var activityContext: Context
companion object {
fun newInstance() = MapFragment()
private const val COUNTRY_ZOOM = 5.85
private const val DEFAULT_ZOOM = 15
private const val PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION = 1
// Keys for storing activity state.
// [START maps_current_place_state_keys]
private const val KEY_CAMERA_POSITION = "camera_position"
private const val KEY_LOCATION = "location"
// [END maps_current_place_state_keys]
// Used for selecting the current place.
private const val M_MAX_ENTRIES = 5
}
private lateinit var viewModel: MapViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater?.inflate(R.layout.map_fragment, container, false)
activityContext = requireContext()
// if (mMap == null) {
mMap = view?.findViewById(R.id.mapView) as MapView
mMap?.onCreate(savedInstanceState)
mMap?.getMapAsync(this)
mMap?.setOnClickListener(clickListener)
// }
StationInfo = view?.findViewById(R.id.StationInfo) as ConstraintLayout
textViewAvailability = view?.findViewById(R.id.textViewAvailability) as TextView
val NextButton = StationInfo?.findViewById(R.id.gotoBookingButton) as Button
NextButton.setOnClickListener(clickListener);
StationInfo.isVisible = false;
createChannel("caf", "alarm")
val permissions = arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION)
// Construct a FusedLocationProviderClient.
fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(requireActivity())
if (savedInstanceState != null) {
lastKnownLocation = savedInstanceState.getParcelable(KEY_LOCATION)
cameraPosition = savedInstanceState.getParcelable(KEY_CAMERA_POSITION)
}
//activity?.let { ActivityCompat.requestPermissions(it, permissions, REQUEST_CODE_LOCATION) }
qrButton = view?.findViewById(R.id.qrButton) as ImageButton
qrButton.setOnClickListener(clickListener)
return view
}
val clickListener = View.OnClickListener { view ->
when (view.getId()) {
R.id.gotoBookingButton -> {
appCommon.CurrentChargePoint = CurrentChargePoint
if (appCommon.getLoginToken().length > 0) {
appCommon.isAdhocBooking = false
findNavController().navigate(R.id.navigation_reserve)
} else {
findNavController().navigate(R.id.navigation_settings_login)
}
}
R.id.mapView -> {
StationInfo.isVisible = false
}
R.id.qrButton -> {
findNavController().navigate(R.id.activity_qr)
}
}
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
// viewModel = ViewModelProviders.of(this).get(MapViewModel::class.java)
viewModel = MapViewModel()
// TODO: Use the ViewModel
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
mMap?.onSaveInstanceState(outState)
}
private val chargingIcon: BitmapDescriptor by lazy {
val color = ContextCompat.getColor(activityContext, R.color.colorPrimary)
BitmapHelper.vectorToBitmap(activityContext, R.drawable.ic_baseline_charging_station_24, color)
}
private fun addMarkers(googleMap: GoogleMap) {
cafMarker.forEach { place ->
val marker = googleMap.addMarker(
MarkerOptions()
.title(place?.title)
.position(place.position)
.icon(chargingIcon)
)
// Set place as the tag on the marker object so it can be referenced within
// MarkerInfoWindowAdapter
marker?.tag = place
}
}
override fun onMapReady(googleMap: GoogleMap) {
map = googleMap
gotoDefaultLocation()
//val devDefault = LatLng(48.07, 11.656)
if (ActivityCompat.checkSelfPermission(requireContext(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
requireContext(),
Manifest.permission.ACCESS_COARSE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
ActivityCompat.requestPermissions(
requireActivity(), arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
REQUEST_CODE_LOCATION
)
return
}
locationPermissionGranted = true
map.isMyLocationEnabled = true;
//gMap.addMarker(MarkerOptions().position(devDefault).title("Hier bin ich"))
updateLocationUI()
getDeviceLocation()
map.setOnMarkerClickListener(this)
getChargePoints()
// FetchChargePoints()
}
val REQUEST_CODE_LOCATION = 32
/* override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == REQUEST_CODE_LOCATION && grantResults.isNotEmpty()
&& grantResults[0] != PackageManager.PERMISSION_GRANTED
) {
MakeToast("Standort Zugriff erlaubt")
locationPermissionGranted = true
updateLocationUI()
getDeviceLocation()
}
}*/
val broadCastReceiver = object : BroadcastReceiver() {
override fun onReceive(contxt: Context?, intent: Intent?) {
when (intent?.action) {
SET_MAP_PERMISSIONS -> {
if (intent?.extras?.get("result")?.toString()!!.toInt() == PackageManager.PERMISSION_GRANTED) {
locationPermissionGranted = true
updateLocationUI()
getDeviceLocation()
getChargePoints()
}
}
// BROADCAST_CHANGE_TYPE_CHANGED -> handleChangeTypeChanged()
}
}
}
private fun updateLocationUI() {
if (map == null) {
return
}
try {
if (locationPermissionGranted) {
map?.isMyLocationEnabled = true
map?.uiSettings?.isMyLocationButtonEnabled = true
} else {
map?.isMyLocationEnabled = false
map?.uiSettings?.isMyLocationButtonEnabled = false
lastKnownLocation = null
getLocationPermission()
}
} catch (e: SecurityException) {
Log.e("Exception: %s", e.message, e)
}
}
// [START maps_current_place_location_permission]
private fun getLocationPermission() {
/*
* Request location permission, so that we can get the location of the
* device. The result of the permission request is handled by a callback,
* onRequestPermissionsResult.
*/
if (ContextCompat.checkSelfPermission(
requireContext(),
Manifest.permission.ACCESS_FINE_LOCATION
)
== PackageManager.PERMISSION_GRANTED
) {
locationPermissionGranted = true
updateLocationUI()
} else {
ActivityCompat.requestPermissions(
requireActivity(), arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION
)
}
}
// [END maps_current_place_location_permission]
// [START maps_current_place_get_device_location]
private fun getDeviceLocation() {
/*
* Get the best and most recent location of the device, which may be null in rare
* cases when a location is not available.
*/
try {
if (locationPermissionGranted) {
val locationResult = fusedLocationProviderClient.lastLocation
locationResult.addOnCompleteListener(requireActivity()) { task ->
if (task.isSuccessful) {
// Set the map's camera position to the current location of the device.
lastKnownLocation = task.result
if (lastKnownLocation != null) {
map?.moveCamera(
CameraUpdateFactory.newLatLngZoom(
LatLng(
lastKnownLocation!!.latitude,
lastKnownLocation!!.longitude
), DEFAULT_ZOOM.toFloat()
)
)
} else {
gotoDefaultLocation()
}
} else {
Log.d(TAG, "Current location is null. Using defaults.")
Log.e(TAG, "Exception: %s", task.exception)
gotoDefaultLocation()
}
}
}
} catch (e: SecurityException) {
MakeToast("Standort Zugriff leider nicht erlaubt")
Log.e("Exception: %s", e.message, e)
}
}
private fun gotoDefaultLocation() {
map?.moveCamera(
CameraUpdateFactory
.newLatLngZoom(defaultLocation, COUNTRY_ZOOM.toFloat())
)
map?.uiSettings?.isMyLocationButtonEnabled = false
}
private fun MakeToast(text: String) {
var toast = text
Toast.makeText(
activity?.applicationContext,
toast,
Toast.LENGTH_SHORT
).show()
}
override fun onResume() {
super.onResume()
registerReceiver()
mMap?.onResume()
}
override fun onPause() {
super.onPause()
mMap?.onPause()
}
override fun onStart() {
super.onStart()
registerReceiver()
mMap?.onStart()
}
override fun onStop() {
super.onStop()
LocalBroadcastManager.getInstance(requireContext()).unregisterReceiver(broadCastReceiver)
mMap?.onStop()
}
override fun onDestroy() {
super.onDestroy()
mMap?.onDestroy()
}
private fun registerReceiver() {
if (!isRegistered) {
var filter = IntentFilter()
filter.addAction(SET_MAP_PERMISSIONS)
// filter.addAction(END_PAYMENT)
LocalBroadcastManager.getInstance(requireContext())
.registerReceiver(broadCastReceiver, filter)
isRegistered = true
}
}
lateinit var cp: ChargePoint
override fun onMarkerClick(p0: Marker): Boolean {
getCPs(p0)
UseCpInfo()
return true
}
private fun UseCpInfo() {
if (this::cp.isInitialized) {
CurrentChargePoint = cp
StationInfo.isVisible = true
val streetName = StationInfo.findViewById(R.id.textViewStreet) as TextView
val stationCity = StationInfo.findViewById(R.id.textViewCity) as TextView
val stationName = StationInfo.findViewById(R.id.textViewStationName) as TextView
stationName.text = cp.getCpLocName()
streetName.text = cp.getStreetName()
stationCity.text = cp.getCityName()
if (cp?.hasType(ChargePointType.AdHoc)) {
textViewAvailability.text = "nur Direktladen, " + cp.Tarif + " Ct/kWh"
} else if (cp?.Available) {
textViewAvailability.text = String.format(Locale.GERMAN, "%.0f", cp.Tarif) + " Ct/kWh - "+String.format(Locale.GERMAN, "%.1f", cp.Power) + " kW"
} else {
textViewAvailability.text = "belegt, "+String.format(Locale.GERMAN, "%.0f", cp.Tarif) + " Ct/kWh - "+String.format(Locale.GERMAN, "%.1f", cp.Power) + " kW"
}
} else {
MakeToast("Station leider nicht verfügbar")
}
}
private fun getCPs(p0: Marker) {
for (i in 0 until ChargePoints.size) {
if (ChargePoints[i].id == p0.id) {
cp = ChargePoints[i]
}
}
}
private fun getCPsByCafMarker(p0: CafMarker) {
for (i in 0 until ChargePoints.size) {
if (ChargePoints[i].Guid == p0.GetId()) {
cp = ChargePoints[i]
}
}
}
private val backendApi = BackendApiFactory(baseUrl).create()
private val listOfChargePoints = object : TypeToken<ArrayList<ChargePoint?>?>() {}.type
private fun getChargePoints() {
CoroutineScope(Dispatchers.IO).launch {
val response =
kotlin.runCatching {
backendApi
.getChargePoints(
appCommon.getLoginToken()
)
.string()
}
withContext(Dispatchers.Main) {
response.fold(
onSuccess = {
ChargePoints = mutableListOf<ChargePoint>()
Log.d("getChargePoints-Success", it)
var gSon = GsonBuilder().create()
var chargePointsJson =
gSon.fromJson<List<ChargePoint>>(it, listOfChargePoints)
if (chargePointsJson != null) {
appCommon.setChargePoints(chargePointsJson)
if (true) {
setUpClusterer()
} else {
for (i in 0 until chargePointsJson.size) {
val cp: ChargePoint = chargePointsJson[i]
val marker = map.addMarker(
MarkerOptions()
.position(LatLng(cp.Lat, cp.Lon))
.title(cp.getDisplayName())
.icon(chargingIcon)
)
if (marker != null) {
cp.id = marker.id
}
ChargePoints.add(i, cp)
}
appCommon.setChargePoints(ChargePoints)
}
}
},
onFailure = {
Log.d("getChargePoints-Fail", it.message.orEmpty())
}
)
}
}
}
// Declare a variable for the cluster manager.
private lateinit var clusterManager: ClusterManager<CafMarker>
private fun setUpClusterer() {
// Position the map.
//gMap?.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(51.503186, -0.126446), 10f))
// Initialize the manager with the context and the map.
// (Activity extends context, so we can pass 'this' in the constructor.)
if (activityContext != null) {
clusterManager = ClusterManager<CafMarker>(activityContext, map)
// Point the map's listeners at the listeners implemented by the cluster
addMarkers(map)
// manager.
map.setOnCameraIdleListener(clusterManager)
map.setOnMarkerClickListener(clusterManager)
map.setOnInfoWindowClickListener(clusterManager)
clusterManager.setOnClusterItemClickListener {
getCPsByCafMarker(it)
UseCpInfo()
// renderer.getMarker(clusterItem).showInfoWindow();
false
} // Add cluster items (markers) to the cluster manager.
addItems()
}
getDeviceLocation()
}
override fun onInfoWindowClick(marker: Marker) {
onMarkerClick(marker);
}
private fun addItems() {
// Add ten cluster items in close proximity, for purposes of this example.
val items: List<ChargePoint> = appCommon.getChargePoints()
var isGood: Boolean = true
for (i in 0 until items.size) {
val cp: ChargePoint = items[i]
if (cp.Guid != null) {
val offsetItem =
CafMarker(cp.Lat, cp.Lon, cp.Guid, cp.CpName, cp.Name)
clusterManager.addItem(offsetItem)
/*
val marker = gMap.addMarker(
MarkerOptions()
.position(LatLng(cp.Lat, cp.Lon))
.title(cp.getDisplayName())
)*/
cp.uuid = offsetItem.GetId()
ChargePoints.add(i, cp)
} else {
isGood = false
}
}
if (!isGood) {
}
// for (i in 0..9) {
// val offset = i / 60.0
// lat += offset
// lng += offset
// val offsetItem =
// CafMarker(lat, lng, "Title $i", "Snippet $i")
// clusterManager.addItem(offsetItem)
// }
}
private fun createChannel(channelId: String, channelName: String) {
// TODO: Step 1.6 START create a channel
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Create channel to show notifications.
val notificationChannel = NotificationChannel(
channelId,
channelName,
// TODO: Step 2.4 change importance
NotificationManager.IMPORTANCE_HIGH
)
// TODO: Step 2.6 disable badges for this channel
.apply {
setShowBadge(false)
}
notificationChannel.enableLights(true)
notificationChannel.lightColor = Color.RED
notificationChannel.enableVibration(true)
notificationChannel.description = getString(R.string.notification_channel_description)
val notificationManager = requireActivity().getSystemService(
NotificationManager::class.java
)
notificationManager.createNotificationChannel(notificationChannel)
}
// TODO: Step 1.6 END create channel
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment