Created
September 7, 2022 10:59
-
-
Save kyodgorbek/0185994f5691fb73f8297106750df54f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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