Files
eqn.ios/Sources/Earthquake Network/Controllers/Simulator/AlertSimulatorViewController.swift
T
2024-06-20 16:26:19 +02:00

266 lines
12 KiB
Swift

//
// AlertSimulatorViewController.swift
// Earthquake Network
//
// Created by Busi Andrea on 14/11/2020.
// Copyright © 2020 Earthquake Network. All rights reserved.
//
import UIKit
import MapKit
import Shogun
class AlertSimulatorViewController: UIViewController, MKMapViewDelegate {
struct MapCircle {
let identifier: String
let distance: CLLocationDistance
let color: UIColor
}
// MARK: - Internal
private let mapCircles = [
MapCircle(identifier: "circle_internal", distance: 50_000, color: AppTheme.Colors.red),
MapCircle(identifier: "circle_external", distance: 750_000, color: AppTheme.Colors.green)
]
private lazy var viewfinderView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .clear
view.isUserInteractionEnabled = false
// add central vertiacl and horizontal lines
let viewfinderVertical = UIView()
view.addSubview(viewfinderVertical)
viewfinderVertical.translatesAutoresizingMaskIntoConstraints = false
viewfinderVertical.backgroundColor = AppTheme.Colors.lightBlue
viewfinderVertical.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
viewfinderVertical.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
viewfinderVertical.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
viewfinderVertical.widthAnchor.constraint(equalToConstant: 2.0).isActive = true
let viewfinderHorizontal = UIView()
view.addSubview(viewfinderHorizontal)
viewfinderHorizontal.translatesAutoresizingMaskIntoConstraints = false
viewfinderHorizontal.backgroundColor = AppTheme.Colors.lightBlue
viewfinderHorizontal.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
viewfinderHorizontal.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
viewfinderHorizontal.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
viewfinderHorizontal.heightAnchor.constraint(equalToConstant: 2.0).isActive = true
return view
}()
private lazy var closeButton: EQNBlurredCloseButton = {
let button = EQNBlurredCloseButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.addTarget(self, action: #selector(closeTapped(_:)), for: .touchUpInside)
button.setTitleColor(.darkGray, for: .normal)
return button
}()
private lazy var mapView: MKMapView = {
let map = MKMapView()
map.translatesAutoresizingMaskIntoConstraints = false
map.showsUserLocation = true
map.delegate = self
return map
}()
private lazy var calculateButton: UIButton = {
let button = EQNRoundedButton(type: .custom)
button.backgroundColor = .lightGray
button.setTitleColor(AppTheme.Colors.darkGray, for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle(NSLocalizedString("globe_simulation_button", comment: "").uppercased(), for: .normal)
button.addTarget(self, action: #selector(calculateTapped(_:)), for: .touchUpInside)
return button
}()
// MARK: - View Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
let closeButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(closeTapped(_:)))
navigationItem.rightBarButtonItem = closeButton
setupUI()
configureMap()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// show an alert view functionality details
let alert = UIAlertController(title: NSLocalizedString("main_simulator", comment: ""), message: NSLocalizedString("globe_simulation", comment: ""), preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("main_understood", comment: ""), style: .default))
present(alert, animated: true)
}
// MARK: - Private
private func setupUI() {
view.addSubview(mapView)
view.addSubview(closeButton)
view.addSubview(viewfinderView)
view.addSubview(calculateButton)
mapView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
mapView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
mapView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
mapView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
closeButton.addDefaultConstraint(to: view)
viewfinderView.topAnchor.constraint(equalTo: mapView.topAnchor).isActive = true
viewfinderView.bottomAnchor.constraint(equalTo: mapView.bottomAnchor).isActive = true
viewfinderView.leftAnchor.constraint(equalTo: mapView.leftAnchor).isActive = true
viewfinderView.rightAnchor.constraint(equalTo: mapView.rightAnchor).isActive = true
calculateButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20.0).isActive = true
calculateButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20.0).isActive = true
calculateButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -10.0).isActive = true
calculateButton.heightAnchor.constraint(equalToConstant: 40.0).isActive = true
}
private func configureMap() {
guard let lastPosition = EQNUser.default().lastPosition else { return }
let span = MKCoordinateSpan(latitudeDelta: 2.0, longitudeDelta: 2.0)
let region = MKCoordinateRegion(center: lastPosition.coordinate, span: span)
mapView.setCenter(lastPosition.coordinate, animated: false)
mapView.setRegion(region, animated: true)
mapCircles.forEach { (mapCircle) in
addCircle(location: lastPosition, radius: mapCircle.distance, id: mapCircle.identifier)
}
}
private func addCircle(location: CLLocation, radius: CLLocationDistance, id: String) {
let circle = MKCircle(center: location.coordinate, radius: radius)
circle.title = id
mapView.addOverlay(circle)
}
// MARK: - Actions
@objc func closeTapped(_ sender: Any) {
dismiss(animated: true)
}
@objc func calculateTapped(_ sender: Any) {
guard let lastPosition = EQNUser.default().lastPosition,
let circleMin = mapCircles.sorted(by: { $0.distance < $1.distance }).first,
let circleMax = mapCircles.sorted(by: { $0.distance > $1.distance }).first else { return }
let location = CLLocation(latitude: mapView.centerCoordinate.latitude, longitude: mapView.centerCoordinate.longitude)
let distance = lastPosition.distance(from: location)
// check for a valid point selected by the user
if distance < circleMin.distance || distance > circleMax.distance {
let message = distance < circleMin.distance ? "globe_simulation_outside" : "globe_simulation_inside"
let alert = UIAlertController(title: NSLocalizedString("attention", comment: ""), message: NSLocalizedString(message, comment: ""), preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("ok", comment: ""), style: .cancel, handler: nil))
present(alert, animated: true)
return
}
let url = EQNGeneratoreURLServer.urlAlertSimulator()
ServerRequest.default().inviaInformazioniAlServer(with: url, richiesta: .alertSimulator) { (result) in
if let result = result as? String {
DispatchQueue.main.async {
self.elaborateServerResponse(result, distance: distance)
}
}
} failure: { (error) in
DispatchQueue.main.async {
let alert = UIAlertController(title: NSLocalizedString("attention", comment: ""), message: error?.localizedDescription, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("ok", comment: ""), style: .cancel, handler: nil))
self.present(alert, animated: true)
return
}
}
}
private func elaborateServerResponse(_ response: String, distance: CLLocationDistance) {
let isTop10k = EQNPurchaseUtility.isTop10kEnabled()
let isTop100k = EQNPurchaseUtility.isTop100kEnabled()
// server response (like '3411#12')
let split = response.split(separator: "#")
let people: Int
let popleTop10k: Int
if split.count > 1 {
//caso top10k=0 e top100k=0
people = Int(split[0]) ?? 0
popleTop10k = Int(split[1]) ?? 0
} else {
//caso top10k=1 oppure top100k=1
people = Int(split[0]) ?? 0
popleTop10k = 0
}
let distanceKm = distance / 1000
let delay = round(Double(people) / 1000.0 * 0.15) //calcolo il ritardo di allerta dovuto alla coda di utenti
let leadTime = round(Double(distanceKm) / 8.0 - 3.0) //calcolo il tempo di allerta teorico nel caso di 0 utenti in coda
let actualTime = leadTime - delay //calcolo il tempo di allerta per l'utente
//il messaggio mostrato all'utente cambia in base al fatto di possedere un abbonamento ed al tempo di allerta calcolato
//il messaggio include tag html perché cambio il colore di parte delle stringa, non so se si fa così anche in iOS
var message = ""
if (isTop10k) {
if (people == 0) {
message = String(format: NSLocalizedString("globe_simulation_message6", comment: ""), "TOP 10K", leadTime)
} else {
message = String(format: NSLocalizedString("globe_simulation_message4", comment: ""), "TOP 10K", leadTime, Double(people))
}
} else if (isTop100k) {
message = String(format: NSLocalizedString("globe_simulation_message5", comment: ""), "TOP 100K", leadTime, Double(people), "TOP 10K")
} else {
if (leadTime / actualTime < 1.4) {
message = String(format: NSLocalizedString("globe_simulation_message3", comment: ""), leadTime, Double(people), Double(popleTop10k))
} else {
if (actualTime > 0) {
message = String(format: NSLocalizedString("globe_simulation_message1", comment: ""), leadTime, Double(people), actualTime, Double(popleTop10k))
} else {
let updatedActualTime = actualTime * -1;
message = String(format: NSLocalizedString("globe_simulation_message2", comment: ""), leadTime, Double(people), updatedActualTime, Double(popleTop10k))
}
}
}
let alert = UIAlertController(title: NSLocalizedString("main_simulator", comment: ""), message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("globe_simulation_priority", comment: ""), style: .default) { (action) in
self.navigateToSubscriptions()
})
alert.addAction(UIAlertAction(title: NSLocalizedString("ok", comment: ""), style: .cancel, handler: nil))
present(alert, animated: true)
}
private func navigateToSubscriptions() {
let controller = SubscriptionsViewController()
let navigationController = UINavigationController(rootViewController: controller)
present(navigationController, animated: true)
}
// MARK: MKMapViewDelegate
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if let overlay = overlay as? MKCircle, let mapCircle = mapCircles.first(where: { $0.identifier == overlay.title }) {
let circle = MKCircleRenderer(overlay: overlay)
circle.strokeColor = mapCircle.color
circle.fillColor = AppTheme.Colors.green.withAlphaComponent(0.1)
circle.lineWidth = 2.0
return circle
}
return MKOverlayRenderer(overlay: overlay)
}
}