Compare commits
102 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 92de4c534c | |||
| 3c237c5b18 | |||
| 16dc2410bc | |||
| 3c83cb97cb | |||
| 4796e3d5a7 | |||
| 56f53550da | |||
| d52b980959 | |||
| b9f87c130d | |||
| b7acbc70df | |||
| 093b6471e8 | |||
| 8ca814561b | |||
| 359667b659 | |||
| 468659ee9f | |||
| ff50abd58a | |||
| 4770578ae3 | |||
| f0fe102901 | |||
| 9dacb33736 | |||
| aed78e44cd | |||
| e531088c86 | |||
| 7e0112bf94 | |||
| 5d12a86cfe | |||
| 09e7786e65 | |||
| 8671533e91 | |||
| 9bd94def0f | |||
| 29c325b7e2 | |||
| 8366f2eabb | |||
| 7e26fee45b | |||
| 76d551b847 | |||
| 8f55553759 | |||
| b44a0a2e27 | |||
| 2b98a4e292 | |||
| 44a27536ad | |||
| 76dcabdc5e | |||
| 2405be895c | |||
| 89ca785864 | |||
| 60678d0839 | |||
| 88b36a501d | |||
| c5b4448830 | |||
| 58b8960e21 | |||
| 49d210eca1 | |||
| 068e457297 | |||
| 9796a40e0e | |||
| 01e8996572 | |||
| 19c6b3d642 | |||
| 0c63a59f19 | |||
| f0c56584b8 | |||
| e9961af792 | |||
| 806b4b67bf | |||
| 851ece0a3b | |||
| 0a76768f88 | |||
| b15efe83e0 | |||
| d84dc8657a | |||
| bac5e909bb | |||
| 15088b744f | |||
| ac03a0cccb | |||
| 76a26e3100 | |||
| cac6ed67ac | |||
| 094c682dbd | |||
| c44f46ca46 | |||
| 5e8c3d0796 | |||
| 61ce27ed4b | |||
| 3aea60e560 | |||
| 84b61fd7e2 | |||
| a5a8c6f5c5 | |||
| 61587a0341 | |||
| a56a04a4ad | |||
| 7173dc7031 | |||
| 35dbdbab28 | |||
| 14614267d4 | |||
| 32833010ed | |||
| ecc77e9f2b | |||
| e69747f133 | |||
| 09685fd4a7 | |||
| 3e122240dc | |||
| 95a214403b | |||
| a7db3c6fa1 | |||
| 4805c79ed6 | |||
| c6ec20e180 | |||
| f6dfd4a761 | |||
| 0212d0a15f | |||
| 3176bde5ed | |||
| dfdbbd0ed4 | |||
| e9986e0fe1 | |||
| beb264f95e | |||
| 217b5edbcf | |||
| e13f95aa5d | |||
| fc5bdbcc92 | |||
| d0ee637449 | |||
| 66e9d7035e | |||
| 7db48e381f | |||
| f9bdb84ad9 | |||
| bee18f6407 | |||
| 96f3a44db4 | |||
| e394259f24 | |||
| 3b53350969 | |||
| f3c3c19e39 | |||
| 8404d72d8f | |||
| a3b0499ed3 | |||
| d923b37fbd | |||
| 282803cf98 | |||
| 039e7b82a1 | |||
| b098caf2ef |
@@ -1,3 +1,6 @@
|
||||
# MacOS files
|
||||
.DS_Store
|
||||
|
||||
# Exclude Pods
|
||||
Sources/Pods
|
||||
|
||||
|
||||
@@ -1,5 +1,44 @@
|
||||
# Changelog
|
||||
|
||||
## Versione 5.6
|
||||
- Aggiunta lingua araba
|
||||
|
||||
## Versione 5.5
|
||||
|
||||
- Aggiornata integrazione Firebase
|
||||
- Migrati alcuni endpooint a cache.earthquakenetwork.it
|
||||
- Rimossa funzionalità meteo
|
||||
- Aggiunta rete UOA
|
||||
- Rimosse stringhe non utilizzate
|
||||
- Rimosse sezioni non utilizzate (es PRO)
|
||||
- Aggiunta gestione localizzata plurali
|
||||
- Rimossi filtri intensità da notifiche in tempo reale
|
||||
- Rivista gestione notifiche push allerte
|
||||
- Migrate costanti in nuova struttura Swift
|
||||
|
||||
## Versione 5.4
|
||||
|
||||
- Aggiunto SDK Facebook
|
||||
|
||||
## Versione 5.3.2
|
||||
|
||||
- Corretto problema con notifiche critiche
|
||||
|
||||
## Versione 5.3.1
|
||||
|
||||
- Ricreato progetto NotificationService
|
||||
- Correzioni minori
|
||||
|
||||
## Versione 5.3
|
||||
|
||||
- Rivista gestione registrazione utente
|
||||
- Modifica in segnalazioni utente
|
||||
- Allineata mappa segnalazioni utente ad Android
|
||||
|
||||
## Versione 5.2.1
|
||||
|
||||
- Disattivata schermata di debug
|
||||
|
||||
## Versione 5.2
|
||||
|
||||
- Nuova schermata per allerta in tempo reale
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"magnitude_range" : "0",
|
||||
"provider" : "SGC",
|
||||
"google.c.a.e" : "1",
|
||||
"google.c.fid" : "d3PS1dEvrUA-tmLLpl5E5f",
|
||||
"preliminary" : "0",
|
||||
"longitude" : "-75.5157",
|
||||
"gcm.message_id" : "1668682445010677",
|
||||
"latitude" : "4.35306",
|
||||
"type" : "official",
|
||||
"google.c.sender.id" : "899482329945",
|
||||
"difference" : "6",
|
||||
"data" : "2022-11-17 11:48:00",
|
||||
"depth" : "26",
|
||||
"aps" : {
|
||||
"content-available" : 1,
|
||||
"alert" : {
|
||||
"loc-key" : "Sisma rilevato a",
|
||||
"title-loc-key" : "Segnalazione da rete sismica",
|
||||
"loc-args" : [
|
||||
"Cajamarca - Tolima, Colombia - M2.2"
|
||||
]
|
||||
},
|
||||
"mutable-content" : 1,
|
||||
"sound" : "default"
|
||||
},
|
||||
"magnitude" : "2.2",
|
||||
"magnitude_type" : "M",
|
||||
"place" : "Cajamarca - Tolima, Colombia",
|
||||
"pop100" : "6622"
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"Simulator Target Bundle": "com.finazzi.distquake",
|
||||
"aps": {
|
||||
"alert": {
|
||||
"loc-args": [
|
||||
"2 km da Foligno"
|
||||
],
|
||||
"loc-key": "Rilevato sisma forte a",
|
||||
"title-loc-key": "Allerta sismica in tempo reale"
|
||||
},
|
||||
"category": "notifica_con_mappa",
|
||||
"content-available": 1,
|
||||
"mutable-content": 1,
|
||||
"sound": "alert_star_trek.wav"
|
||||
},
|
||||
"counter": 10,
|
||||
"datetime": "2022-06-22 19:12:00",
|
||||
"delay": 4,
|
||||
"detection_latitude": "45.64664",
|
||||
"detection_longitude": "9.188540",
|
||||
"gcm.message_id": "1614708857742608",
|
||||
"google.c.a.e": 1,
|
||||
"google.c.sender.id": "899482329945",
|
||||
"magnitude": 20,
|
||||
"latitude": "42.958",
|
||||
"location": "2 km da Foligno",
|
||||
"longitude": "12.702",
|
||||
"peak": "0.4",
|
||||
"randcode": 100,
|
||||
"test": 0,
|
||||
"type": "manual",
|
||||
"wave_speed": "4.7",
|
||||
"critical": true
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"Simulator Target Bundle": "com.finazzi.distquake",
|
||||
"aps": {
|
||||
"alert": {
|
||||
"loc-args": [
|
||||
"14 km Al Qunfudhah"
|
||||
],
|
||||
"loc-key": "Rilevato sisma forte a",
|
||||
"title-loc-key": "Allerta sismica in tempo reale"
|
||||
},
|
||||
"category": "notifica_con_mappa",
|
||||
"content-available": 1,
|
||||
"mutable-content": 1,
|
||||
"sound": "alert_star_trek.wav"
|
||||
},
|
||||
"counter": 10,
|
||||
"datetime": "2023-08-10 15:57:00",
|
||||
"delay": 4,
|
||||
"detection_latitude": "18.65",
|
||||
"detection_longitude": "42.76",
|
||||
"gcm.message_id": "1614708857742608",
|
||||
"google.c.a.e": 1,
|
||||
"google.c.sender.id": "899482329945",
|
||||
"intensity": 2,
|
||||
"latitude": "18.65",
|
||||
"location": "14 km Al Qunfudhah",
|
||||
"longitude": "42.76",
|
||||
"peak": "1.2",
|
||||
"randcode": 100,
|
||||
"test": 0,
|
||||
"type": "eqn",
|
||||
"wave_speed": "4.7",
|
||||
"critical": true
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
"aps": {
|
||||
"alert": {
|
||||
"loc-args": [
|
||||
"150 km (Test)"
|
||||
"15 χλμ από το Αίγιο"
|
||||
],
|
||||
"loc-key": "Rilevato sisma forte a",
|
||||
"title-loc-key": "Allerta sismica in tempo reale"
|
||||
@@ -14,7 +14,8 @@
|
||||
"sound": "alert_star_trek.wav"
|
||||
},
|
||||
"counter": 10,
|
||||
"datetime": "2022-06-22 16:15:00",
|
||||
"datetime": "2022-06-23 10:10:00",
|
||||
"delay": 4,
|
||||
"detection_latitude": "37.983810",
|
||||
"detection_longitude": "23.727539",
|
||||
"gcm.message_id": "1614708857742608",
|
||||
@@ -22,12 +23,12 @@
|
||||
"google.c.sender.id": "899482329945",
|
||||
"intensity": 2,
|
||||
"latitude": "38.19",
|
||||
"location": "150 km (Test)",
|
||||
"location": "15 χλμ από το Αίγιο",
|
||||
"longitude": "22.26",
|
||||
"peak": "-1",
|
||||
"randcode": 0,
|
||||
"test": 1,
|
||||
"peak": "0.4",
|
||||
"randcode": 100,
|
||||
"test": 0,
|
||||
"type": "eqn",
|
||||
"wave_speed": "4.7",
|
||||
"critical": true
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
"aps": {
|
||||
"alert": {
|
||||
"loc-args": [
|
||||
"150 km (Test)"
|
||||
"14 km from California City"
|
||||
],
|
||||
"loc-key": "Rilevato sisma forte a",
|
||||
"title-loc-key": "Allerta sismica in tempo reale"
|
||||
@@ -14,7 +14,8 @@
|
||||
"sound": "alert_star_trek.wav"
|
||||
},
|
||||
"counter": 10,
|
||||
"datetime": "2022-06-22 16:15:00",
|
||||
"datetime": "2022-06-23 07:55:00",
|
||||
"delay": 4,
|
||||
"detection_latitude": "37.3229978",
|
||||
"detection_longitude": "-122.0321823",
|
||||
"gcm.message_id": "1614708857742608",
|
||||
@@ -22,11 +23,11 @@
|
||||
"google.c.sender.id": "899482329945",
|
||||
"intensity": 2,
|
||||
"latitude": "35.15",
|
||||
"location": "150 km (Test)",
|
||||
"location": "14 km from California City",
|
||||
"longitude": "-117.78",
|
||||
"peak": "-1",
|
||||
"randcode": 0,
|
||||
"test": 1,
|
||||
"peak": "0.4",
|
||||
"randcode": 100,
|
||||
"test": 0,
|
||||
"type": "eqn",
|
||||
"wave_speed": "4.7",
|
||||
"critical": true
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"aps": {
|
||||
"alert": {
|
||||
"loc-args": [
|
||||
"150 km (Test)"
|
||||
"5 km de Las Cujas"
|
||||
],
|
||||
"loc-key": "Rilevato sisma forte a",
|
||||
"title-loc-key": "Allerta sismica in tempo reale"
|
||||
@@ -14,7 +14,8 @@
|
||||
"sound": "alert_star_trek.wav"
|
||||
},
|
||||
"counter": 10,
|
||||
"datetime": "2022-06-22 16:15:00",
|
||||
"datetime": "2022-06-23 10:19:00",
|
||||
"delay": 4,
|
||||
"detection_latitude": "-34.603722",
|
||||
"detection_longitude": "-58.381592",
|
||||
"gcm.message_id": "1614708857742608",
|
||||
@@ -22,11 +23,11 @@
|
||||
"google.c.sender.id": "899482329945",
|
||||
"intensity": 2,
|
||||
"latitude": "-32.57",
|
||||
"location": "150 km (Test)",
|
||||
"location": "5 km de Las Cujas",
|
||||
"longitude": "-71.46",
|
||||
"peak": "-1",
|
||||
"randcode": 0,
|
||||
"test": 1,
|
||||
"peak": "0.4",
|
||||
"randcode": 100,
|
||||
"test": 0,
|
||||
"type": "eqn",
|
||||
"wave_speed": "4.7",
|
||||
"critical": true
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"aps": {
|
||||
"alert": {
|
||||
"loc-args": [
|
||||
"150 km (Test)"
|
||||
"15 km de Dieppe"
|
||||
],
|
||||
"loc-key": "Rilevato sisma forte a",
|
||||
"title-loc-key": "Allerta sismica in tempo reale"
|
||||
@@ -14,7 +14,8 @@
|
||||
"sound": "alert_star_trek.wav"
|
||||
},
|
||||
"counter": 10,
|
||||
"datetime": "2022-06-22 16:15:00",
|
||||
"datetime": "2022-06-23 10:07:00",
|
||||
"delay": 4,
|
||||
"detection_latitude": "48.856614",
|
||||
"detection_longitude": "2.8522219",
|
||||
"gcm.message_id": "1614708857742608",
|
||||
@@ -22,11 +23,11 @@
|
||||
"google.c.sender.id": "899482329945",
|
||||
"intensity": 2,
|
||||
"latitude": "49.77",
|
||||
"location": "150 km (Test)",
|
||||
"location": "15 km de Dieppe",
|
||||
"longitude": "1.05",
|
||||
"peak": "-1",
|
||||
"randcode": 0,
|
||||
"test": 1,
|
||||
"peak": "0.4",
|
||||
"randcode": 100,
|
||||
"test": 0,
|
||||
"type": "eqn",
|
||||
"wave_speed": "4.7",
|
||||
"critical": true
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"aps": {
|
||||
"alert": {
|
||||
"loc-args": [
|
||||
"150 km (Test)"
|
||||
"5 km od Novog"
|
||||
],
|
||||
"loc-key": "Rilevato sisma forte a",
|
||||
"title-loc-key": "Allerta sismica in tempo reale"
|
||||
@@ -14,7 +14,8 @@
|
||||
"sound": "alert_star_trek.wav"
|
||||
},
|
||||
"counter": 10,
|
||||
"datetime": "2022-06-22 16:15:00",
|
||||
"datetime": "2022-06-23 10:02:00",
|
||||
"delay": 4,
|
||||
"detection_latitude": "45.813177",
|
||||
"detection_longitude": "15.977048",
|
||||
"gcm.message_id": "1614708857742608",
|
||||
@@ -22,11 +23,11 @@
|
||||
"google.c.sender.id": "899482329945",
|
||||
"intensity": 2,
|
||||
"latitude": "45.09",
|
||||
"location": "150 km (Test)",
|
||||
"location": "5 km od Novog",
|
||||
"longitude": "14.87",
|
||||
"peak": "-1",
|
||||
"randcode": 0,
|
||||
"test": 1,
|
||||
"peak": "0.4",
|
||||
"randcode": 100,
|
||||
"test": 0,
|
||||
"type": "eqn",
|
||||
"wave_speed": "4.7",
|
||||
"critical": true
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"aps": {
|
||||
"alert": {
|
||||
"loc-args": [
|
||||
"150 km (Test)"
|
||||
"35 km dari Sindanbarang"
|
||||
],
|
||||
"loc-key": "Rilevato sisma forte a",
|
||||
"title-loc-key": "Allerta sismica in tempo reale"
|
||||
@@ -14,7 +14,8 @@
|
||||
"sound": "alert_star_trek.wav"
|
||||
},
|
||||
"counter": 10,
|
||||
"datetime": "2022-06-22 16:15:00",
|
||||
"datetime": "2022-06-23 10:13:00",
|
||||
"delay": 4,
|
||||
"detection_latitude": "-2.548926",
|
||||
"detection_longitude": "118.0148634",
|
||||
"gcm.message_id": "1614708857742608",
|
||||
@@ -22,11 +23,11 @@
|
||||
"google.c.sender.id": "899482329945",
|
||||
"intensity": 2,
|
||||
"latitude": "-7.38",
|
||||
"location": "150 km (Test)",
|
||||
"location": "35 km dari Sindanbarang",
|
||||
"longitude": "106.83",
|
||||
"peak": "-1",
|
||||
"randcode": 0,
|
||||
"test": 1,
|
||||
"peak": "0.4",
|
||||
"randcode": 100,
|
||||
"test": 0,
|
||||
"type": "eqn",
|
||||
"wave_speed": "4.7",
|
||||
"critical": true
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"aps": {
|
||||
"alert": {
|
||||
"loc-args": [
|
||||
"150 km (Test)"
|
||||
"2 km da Foligno"
|
||||
],
|
||||
"loc-key": "Rilevato sisma forte a",
|
||||
"title-loc-key": "Allerta sismica in tempo reale"
|
||||
@@ -14,7 +14,8 @@
|
||||
"sound": "alert_star_trek.wav"
|
||||
},
|
||||
"counter": 10,
|
||||
"datetime": "2022-06-22 16:15:00",
|
||||
"datetime": "2022-06-22 19:12:00",
|
||||
"delay": 4,
|
||||
"detection_latitude": "45.64664",
|
||||
"detection_longitude": "9.188540",
|
||||
"gcm.message_id": "1614708857742608",
|
||||
@@ -22,11 +23,11 @@
|
||||
"google.c.sender.id": "899482329945",
|
||||
"intensity": 2,
|
||||
"latitude": "42.958",
|
||||
"location": "150 km (Test)",
|
||||
"location": "2 km da Foligno",
|
||||
"longitude": "12.702",
|
||||
"peak": "-1",
|
||||
"randcode": 0,
|
||||
"test": 1,
|
||||
"peak": "0.4",
|
||||
"randcode": 100,
|
||||
"test": 0,
|
||||
"type": "eqn",
|
||||
"wave_speed": "4.7",
|
||||
"critical": true
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"aps": {
|
||||
"alert": {
|
||||
"loc-args": [
|
||||
"150 km (Test)"
|
||||
"45 km de Puebla"
|
||||
],
|
||||
"loc-key": "Rilevato sisma forte a",
|
||||
"title-loc-key": "Allerta sismica in tempo reale"
|
||||
@@ -14,7 +14,8 @@
|
||||
"sound": "alert_star_trek.wav"
|
||||
},
|
||||
"counter": 10,
|
||||
"datetime": "2022-06-22 16:15:00",
|
||||
"datetime": "2022-06-23 10:16:00",
|
||||
"delay": 4,
|
||||
"detection_latitude": "19.432608",
|
||||
"detection_longitude": "-99.133209",
|
||||
"gcm.message_id": "1614708857742608",
|
||||
@@ -22,11 +23,11 @@
|
||||
"google.c.sender.id": "899482329945",
|
||||
"intensity": 2,
|
||||
"latitude": "18.72",
|
||||
"location": "150 km (Test)",
|
||||
"location": "45 km de Puebla",
|
||||
"longitude": "-97.90",
|
||||
"peak": "-1",
|
||||
"randcode": 0,
|
||||
"test": 1,
|
||||
"peak": "0.4",
|
||||
"randcode": 100,
|
||||
"test": 0,
|
||||
"type": "eqn",
|
||||
"wave_speed": "4.7",
|
||||
"critical": true
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"aps": {
|
||||
"alert": {
|
||||
"loc-args": [
|
||||
"150 km (Test)"
|
||||
"Bandırma'ya 25 km"
|
||||
],
|
||||
"loc-key": "Rilevato sisma forte a",
|
||||
"title-loc-key": "Allerta sismica in tempo reale"
|
||||
@@ -14,7 +14,8 @@
|
||||
"sound": "alert_star_trek.wav"
|
||||
},
|
||||
"counter": 10,
|
||||
"datetime": "2022-06-22 16:15:00",
|
||||
"datetime": "2022-06-23 10:22:00",
|
||||
"delay": 4,
|
||||
"detection_latitude": "41.008238",
|
||||
"detection_longitude": "28.978359",
|
||||
"gcm.message_id": "1614708857742608",
|
||||
@@ -22,12 +23,12 @@
|
||||
"google.c.sender.id": "899482329945",
|
||||
"intensity": 2,
|
||||
"latitude": "40.33",
|
||||
"location": "150 km (Test)",
|
||||
"location": "Bandırma'ya 25 km",
|
||||
"longitude": "27.66",
|
||||
"peak": "-1",
|
||||
"randcode": 0,
|
||||
"test": 1,
|
||||
"peak": "0.4",
|
||||
"randcode": 100,
|
||||
"test": 0,
|
||||
"type": "eqn",
|
||||
"wave_speed": "4.7",
|
||||
"critical": true
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,7 @@
|
||||
[super viewDidLoad];
|
||||
|
||||
[self.mapView registerClass:[EQNCustomAnnotationView class] forAnnotationViewWithReuseIdentifier:EQNCustomAnnotationView.SingleLineIdentifier];
|
||||
[self.mapView registerClass:[EQNCustomAnnotationView class] forAnnotationViewWithReuseIdentifier:EQNCustomAnnotationView.SmallIdentifier];
|
||||
}
|
||||
|
||||
- (void)didReceiveNotification:(UNNotification *)notification
|
||||
@@ -67,9 +68,8 @@
|
||||
[self.mapView addAnnotation:annotation];
|
||||
|
||||
} else if ([userInfo[@"type"] isEqualToString:@"manual"]){
|
||||
EQNMapAnnotationUserReport *annotation = [[EQNMapAnnotationUserReport alloc] initWithTitle:@""
|
||||
coordinate:coordinate.coordinate
|
||||
magnitude:[userInfo[@"magnitudo"] intValue]];
|
||||
EQNMapAnnotationUserReport *annotation = [[EQNMapAnnotationUserReport alloc] initWithMagnitude:[userInfo[@"magnitude"] intValue]
|
||||
coordinate:coordinate.coordinate];
|
||||
[self.mapView addAnnotation:annotation];
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
NSTimeInterval difference = MAX([self.userSeismicTimestamp timeIntervalSinceDate:now], 0);
|
||||
NSInteger seconds = (int)lround(difference);
|
||||
|
||||
self.waveLabel.text = [NSString stringWithFormat:NSLocalizedString(@"alert_wave", @""), seconds];
|
||||
self.waveLabel.text = [NSString localizedStringWithFormat:NSLocalizedString(@"alert_wave", @""), seconds];
|
||||
|
||||
if (difference <= 0) {
|
||||
// stop the countdown
|
||||
@@ -118,9 +118,9 @@
|
||||
} else if ([annotation isKindOfClass:[EQNMapAnnotationUserReport class]]) {
|
||||
EQNMapAnnotationUserReport *report = (EQNMapAnnotationUserReport *)annotation;
|
||||
|
||||
EQNCustomAnnotationView *annotationView = (EQNCustomAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:EQNCustomAnnotationView.SingleLineIdentifier];
|
||||
annotationView.image = report.image;
|
||||
annotationView.title = report.title;
|
||||
EQNCustomAnnotationView *annotationView = (EQNCustomAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:EQNCustomAnnotationView.SmallIdentifier];
|
||||
annotationView.image = [report imageWithHeight:EQNCustomAnnotationView.SmallViewImageHeight];
|
||||
annotationView.title = report.timeDifference;
|
||||
return annotationView;
|
||||
}
|
||||
return nil;
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
//
|
||||
// Use this file to import your target's public headers that you would like to expose to Swift.
|
||||
//
|
||||
|
||||
#import "NotificationService.h"
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 8.8 KiB |
@@ -2,35 +2,12 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>EQNNotificationService</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
<string>com.apple.usernotifications.service</string>
|
||||
<key>NSExtensionPrincipalClass</key>
|
||||
<string>NotificationService</string>
|
||||
<string>$(PRODUCT_MODULE_NAME).NotificationService</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
//
|
||||
// NotificationService+Extension.swift
|
||||
// EQNNotificationService
|
||||
//
|
||||
// Created by Andrea Busi on 28/05/22.
|
||||
// Copyright © 2022 Earthquake Network. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UserNotifications
|
||||
|
||||
|
||||
extension NotificationService {
|
||||
|
||||
@objc(removeNotificationsForType:completion:)
|
||||
func removeNotifications(
|
||||
for type: String?,
|
||||
completion: @escaping() -> Void
|
||||
) {
|
||||
guard let type = type else {
|
||||
completion()
|
||||
return
|
||||
}
|
||||
|
||||
let notificationCenter = UNUserNotificationCenter.current()
|
||||
|
||||
notificationCenter.getDeliveredNotifications { notifications in
|
||||
let sameTypeNotifications = notifications.filter { notification in
|
||||
let payload = notification.request.content.userInfo
|
||||
if let notificationType = payload["type"] as? String {
|
||||
return notificationType == type
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
let identifiers = sameTypeNotifications.map { $0.request.identifier }
|
||||
notificationCenter.removeDeliveredNotifications(withIdentifiers: identifiers)
|
||||
|
||||
// !! Note: this is a known issue/bug
|
||||
// we need to add a delay before invoking the completion, otherwise the notification will not be remved
|
||||
// ref: https://stackoverflow.com/questions/53697279/why-are-notifications-not-removed-with-removedeliverednotifications
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||
completion()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
//
|
||||
// NotificationService.h
|
||||
// EQNNotificationService
|
||||
//
|
||||
// Refactored by Andrea Busi
|
||||
// Copyright © 2020 Earthquake Network. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UserNotifications/UserNotifications.h>
|
||||
|
||||
@interface NotificationService : UNNotificationServiceExtension
|
||||
|
||||
@end
|
||||
@@ -1,181 +0,0 @@
|
||||
//
|
||||
// NotificationService.m
|
||||
// EQNNotificationService
|
||||
//
|
||||
// Refactored by Andrea Busi
|
||||
// Copyright © 2020 Earthquake Network. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NotificationService.h"
|
||||
#import "EQNAllertaSismica.h"
|
||||
#import "Costanti.h"
|
||||
#import "EQNNotificationService-Swift.h"
|
||||
|
||||
|
||||
@interface NotificationService ()
|
||||
|
||||
@property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver);
|
||||
@property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent;
|
||||
@end
|
||||
|
||||
@implementation NotificationService
|
||||
|
||||
static NSString * const EQNSoundNotificationEQN = @"alert_sound.wav";
|
||||
|
||||
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent *_Nonnull))contentHandler
|
||||
{
|
||||
self.contentHandler = contentHandler;
|
||||
self.bestAttemptContent = [request.content mutableCopy];
|
||||
|
||||
// Configure the notification's payload.
|
||||
UNMutableNotificationContent* content = [[UNMutableNotificationContent alloc] init];
|
||||
content.title = [NSString localizedUserNotificationStringForKey:request.content.title arguments:nil];
|
||||
content.body = [NSString localizedUserNotificationStringForKey:request.content.body arguments:nil];
|
||||
|
||||
self.bestAttemptContent.title = content.title;
|
||||
self.bestAttemptContent.body = content.body;
|
||||
|
||||
// check for required informations
|
||||
NSDictionary *userInfo = request.content.userInfo;
|
||||
NSString *notificationType = [userInfo objectForKey:@"type"];
|
||||
if (userInfo == nil || notificationType == nil) {
|
||||
[self contentComplete];
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
NSString *iconName = @"";
|
||||
if ([notificationType isEqualToString:@"eqn"]) {
|
||||
// check if user has enabled critical alerts
|
||||
NSUserDefaults *sharedDefaults = [[NSUserDefaults alloc] initWithSuiteName:EQNUserDefaultAppGroupSuite];
|
||||
BOOL criticalAlertsEnabled = [sharedDefaults boolForKey:NOTIFICHE_ALLERA_SISMICA_CRITICAL_ALERTS];
|
||||
|
||||
// !!WORKAROUND
|
||||
// this is a workaround to use critical alerts with legacy FCM api
|
||||
// when the server implementation will be migrated to Firebase v1 APIs, this could be removed
|
||||
BOOL isCritical = [[userInfo objectForKey:@"critical"] boolValue];
|
||||
if (isCritical && criticalAlertsEnabled) {
|
||||
self.bestAttemptContent.sound = [UNNotificationSound criticalSoundNamed:EQNSoundNotificationEQN withAudioVolume:1.0];
|
||||
} else {
|
||||
self.bestAttemptContent.sound = [UNNotificationSound soundNamed:EQNSoundNotificationEQN];
|
||||
}
|
||||
|
||||
NSString *intensity = [userInfo objectForKey:@"intensity"];
|
||||
switch ([intensity intValue]) {
|
||||
case 0:
|
||||
iconName = @"star_realtime_white.png";
|
||||
break;
|
||||
case 1:
|
||||
iconName = @"star_realtime_lightblue.png";
|
||||
break;
|
||||
case 2:
|
||||
iconName = @"star_realtime_blue.png";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if ([notificationType isEqualToString:@"manual"]) {
|
||||
NSString *intensity = [userInfo objectForKey:@"magnitude"];
|
||||
switch ([intensity intValue]) {
|
||||
case 0:
|
||||
iconName = @"star_green.png";
|
||||
break;
|
||||
case 1:
|
||||
iconName = @"star_yellow.png";
|
||||
break;
|
||||
case 2:
|
||||
iconName = @"star_red.png";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if ([notificationType isEqualToString:@"official"]) {
|
||||
NSString *provaider = [userInfo objectForKey:@"provider"];
|
||||
double intensity = [[userInfo objectForKey:@"magnitude"] doubleValue];
|
||||
|
||||
NSString *colore = @"";
|
||||
if (intensity < 2.0) {
|
||||
colore = @"_white";
|
||||
} else if (intensity < 3.5) {
|
||||
colore = @"_green";
|
||||
} else if (intensity < 4.5) {
|
||||
colore = @"_yellow";
|
||||
} else if (intensity < 5.5) {
|
||||
colore = @"_red";
|
||||
} else {
|
||||
colore = @"_purple";
|
||||
}
|
||||
|
||||
if ([provaider isEqualToString:@"USGS"]) {
|
||||
iconName = [NSString stringWithFormat:@"star%@.png", colore];
|
||||
} else if ([provaider isEqualToString:@"SGC"]) {
|
||||
iconName = [NSString stringWithFormat:@"star3%@.png", colore];
|
||||
} else if ([provaider isEqualToString:@"CSN"]) {
|
||||
iconName = [NSString stringWithFormat:@"star3f%@.png", colore];
|
||||
} else if ([provaider isEqualToString:@"SSN"]) {
|
||||
iconName = [NSString stringWithFormat:@"star4%@.png", colore];
|
||||
} else if ([provaider isEqualToString:@"INPRES"]) {
|
||||
iconName = [NSString stringWithFormat:@"star4r%@.png", colore];
|
||||
} else if ([provaider isEqualToString:@"FUNVISIS"]) {
|
||||
iconName = [NSString stringWithFormat:@"star6%@.png", colore];
|
||||
} else if ([provaider isEqualToString:@"Ineter"]) {
|
||||
iconName = [NSString stringWithFormat:@"triangle%@.png", colore];
|
||||
} else if ([provaider isEqualToString:@"RSN"]) {
|
||||
iconName = [NSString stringWithFormat:@"triangle2%@.png", colore];
|
||||
} else if ([provaider isEqualToString:@"PHIVOLCS"]) {
|
||||
iconName = [NSString stringWithFormat:@"triround_inner%@.png", colore];
|
||||
} else if ([provaider isEqualToString:@"IGEPN"]) {
|
||||
iconName = [NSString stringWithFormat:@"triround%@.png", colore];
|
||||
} else if ([provaider isEqualToString:@"INGV"]) {
|
||||
iconName = [NSString stringWithFormat:@"circle%@.png", colore];
|
||||
} else if ([provaider isEqualToString:@"EMSC"]) {
|
||||
iconName = [NSString stringWithFormat:@"dyamond%@.png", colore];
|
||||
} else if ([provaider isEqualToString:@"IGP"]) {
|
||||
iconName = [NSString stringWithFormat:@"dyamond_round%@.png", colore];
|
||||
} else if ([provaider isEqualToString:@"JMA"]) {
|
||||
iconName = [NSString stringWithFormat:@"esa%@.png", colore];
|
||||
} else if ([provaider isEqualToString:@"GEONET"]) {
|
||||
iconName = [NSString stringWithFormat:@"oct%@.png", colore];
|
||||
} if ([provaider isEqualToString:@"CSI"]) {
|
||||
iconName = [NSString stringWithFormat:@"penta%@.png", colore];
|
||||
} else if ([provaider isEqualToString:@"IGN"]) {
|
||||
iconName = [NSString stringWithFormat:@"square%@.png", colore];
|
||||
} else if ([provaider isEqualToString:@"UASD"] || [provaider isEqualToString:@"BDTIM"] || [provaider isEqualToString:@"NCS"]) {
|
||||
iconName = [NSString stringWithFormat:@"thick_star%@.png", colore];
|
||||
} else if ([provaider isEqualToString:@"RSPR"]) {
|
||||
iconName = [NSString stringWithFormat:@"star6f%@.png", colore];
|
||||
}
|
||||
}
|
||||
|
||||
if (![iconName isEqualToString:@""]) {
|
||||
iconName = [iconName stringByReplacingOccurrencesOfString:@".png" withString:@""];
|
||||
NSURL *imageUrl = [NSBundle.mainBundle URLForResource:iconName withExtension:@"png"];
|
||||
NSError *attachmentError = nil;
|
||||
UNNotificationAttachment *attachment = [UNNotificationAttachment attachmentWithIdentifier:iconName
|
||||
URL:imageUrl
|
||||
options:nil
|
||||
error:&attachmentError];
|
||||
if (!attachmentError) {
|
||||
self.bestAttemptContent.attachments = @[attachment];
|
||||
}
|
||||
}
|
||||
|
||||
// remove same type posted notification
|
||||
[self removeNotificationsForType:notificationType completion:^{
|
||||
[self contentComplete];
|
||||
}];;
|
||||
}
|
||||
|
||||
- (void)serviceExtensionTimeWillExpire
|
||||
{
|
||||
// Called just before the extension will be terminated by the system.
|
||||
// Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
|
||||
[self contentComplete];
|
||||
}
|
||||
|
||||
- (void)contentComplete
|
||||
{
|
||||
self.contentHandler(self.bestAttemptContent);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,212 @@
|
||||
//
|
||||
// NotificationService.swift
|
||||
// EQNNotificationService
|
||||
//
|
||||
// Created by Andrea Busi on 24/11/22.
|
||||
// Copyright © 2022 Earthquake Network. All rights reserved.
|
||||
//
|
||||
|
||||
import UserNotifications
|
||||
import CoreLocation
|
||||
import Shogun
|
||||
|
||||
class NotificationService: UNNotificationServiceExtension {
|
||||
|
||||
private static let EQNSoundNotification = UNNotificationSoundName("alert_sound.wav")
|
||||
|
||||
|
||||
var contentHandler: ((UNNotificationContent) -> Void)?
|
||||
var bestAttemptContent: UNMutableNotificationContent?
|
||||
|
||||
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
|
||||
self.contentHandler = contentHandler
|
||||
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
|
||||
|
||||
guard let bestAttemptContent = bestAttemptContent else {
|
||||
print("[NotificationService] Unable to get notification content")
|
||||
contentComplete()
|
||||
return
|
||||
}
|
||||
|
||||
let userInfo = request.content.userInfo
|
||||
guard let notificationType = userInfo.string(forKey: "type") else {
|
||||
print("[NotificationService] Unable to get notification type")
|
||||
contentComplete()
|
||||
return
|
||||
}
|
||||
|
||||
var iconName = ""
|
||||
switch notificationType.lowercased() {
|
||||
case "eqn":
|
||||
// check if user has enabled critical alerts
|
||||
let criticalAlertsEnabled = UserDefaults.appGroup?.bool(forKey: UserDefaults.AllertaSismicaCriticalAlerts) ?? false
|
||||
|
||||
// !!WORKAROUND
|
||||
// this is a workaround to use critical alerts with legacy FCM api
|
||||
// when the server implementation will be migrated to Firebase v1 APIs, this could be removed
|
||||
let isCritical = userInfo.integer(forKey: "critical", orDefault: 0) == 1
|
||||
if isCritical && criticalAlertsEnabled {
|
||||
bestAttemptContent.sound = UNNotificationSound.criticalSoundNamed(Self.EQNSoundNotification)
|
||||
} else {
|
||||
bestAttemptContent.sound = UNNotificationSound(named: Self.EQNSoundNotification)
|
||||
}
|
||||
|
||||
let intensity = userInfo.integer(forKey: "intensity")
|
||||
switch intensity {
|
||||
case 0:
|
||||
iconName = "star_realtime_white.png"
|
||||
case 1:
|
||||
iconName = "star_realtime_lightblue.png"
|
||||
case 2:
|
||||
iconName = "star_realtime_blue.png"
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
// evaluate intensity and get proper string to display
|
||||
guard let latitude = userInfo.double(forKey: "latitude"),
|
||||
let longitude = userInfo.double(forKey: "longitude"),
|
||||
let peak = userInfo.double(forKey: "peak") else {
|
||||
print("[NotificationService] Unable to get base info for intensity calculation")
|
||||
return
|
||||
}
|
||||
|
||||
let location = CLLocation(latitude: latitude, longitude: longitude)
|
||||
guard let distance = EQNUserData.shared.lastLocation?.distance(from: location) else {
|
||||
print("[NotificationService] Unable to calculate distance or get last location")
|
||||
return
|
||||
}
|
||||
|
||||
let distanceKm = distance / 1_000
|
||||
let intensita = peak * exp(-distanceKm/peak/250)
|
||||
let stringSuffix: String
|
||||
|
||||
if intensita < 0.004 {
|
||||
stringSuffix = "no_shaking"
|
||||
} else if intensita < 0.30 {
|
||||
stringSuffix = "mild"
|
||||
} else if intensita < 0.70 {
|
||||
stringSuffix = "moderate"
|
||||
} else {
|
||||
stringSuffix = "strong"
|
||||
}
|
||||
bestAttemptContent.body = "alert_intensity_\(stringSuffix)".localized
|
||||
|
||||
case "manual":
|
||||
// there are 12 levels, so a customized icon doesn't make sense
|
||||
// use a generic warning icon instead
|
||||
iconName = "warning_yellow.png"
|
||||
case "official":
|
||||
let provider = userInfo.string(forKey: "provider", orDefault: "")
|
||||
let intensity = userInfo.double(forKey: "magnitude", orDefault: 0)
|
||||
|
||||
let color: String
|
||||
if intensity < 2.0 {
|
||||
color = "_white"
|
||||
} else if intensity < 3.5 {
|
||||
color = "_green"
|
||||
} else if intensity < 4.5 {
|
||||
color = "_yellow"
|
||||
} else if intensity < 5.5 {
|
||||
color = "_red"
|
||||
} else {
|
||||
color = "_purple"
|
||||
}
|
||||
|
||||
iconName = manualIconName(for: provider, color: color)
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
// add the icon as notification attachment
|
||||
if !iconName.isEmpty {
|
||||
iconName = iconName.replacingOccurrences(of: ".png", with: "")
|
||||
|
||||
if let imageUrl = Bundle(for: NotificationService.self).url(forResource: iconName, withExtension: "png"),
|
||||
let attachment = try? UNNotificationAttachment(identifier: iconName, url: imageUrl) {
|
||||
bestAttemptContent.attachments = [ attachment ]
|
||||
}
|
||||
}
|
||||
|
||||
// remove same type posted notification
|
||||
removeNotifications(for: notificationType) { [weak self] in
|
||||
self?.contentComplete()
|
||||
}
|
||||
}
|
||||
|
||||
override func serviceExtensionTimeWillExpire() {
|
||||
// Called just before the extension will be terminated by the system.
|
||||
// Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
|
||||
if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
|
||||
contentHandler(bestAttemptContent)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func contentComplete() {
|
||||
if let bestAttemptContent {
|
||||
contentHandler?(bestAttemptContent)
|
||||
}
|
||||
}
|
||||
|
||||
private func removeNotifications(
|
||||
for type: String?,
|
||||
completion: @escaping() -> Void
|
||||
) {
|
||||
guard let type = type else {
|
||||
completion()
|
||||
return
|
||||
}
|
||||
|
||||
let notificationCenter = UNUserNotificationCenter.current()
|
||||
|
||||
notificationCenter.getDeliveredNotifications { notifications in
|
||||
let sameTypeNotifications = notifications.filter { notification in
|
||||
let payload = notification.request.content.userInfo
|
||||
if let notificationType = payload["type"] as? String {
|
||||
return notificationType == type
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
let identifiers = sameTypeNotifications.map { $0.request.identifier }
|
||||
notificationCenter.removeDeliveredNotifications(withIdentifiers: identifiers)
|
||||
|
||||
// !! Note: this is a known issue/bug
|
||||
// we need to add a delay before invoking the completion, otherwise the notification will not be remved
|
||||
// ref: https://stackoverflow.com/questions/53697279/why-are-notifications-not-removed-with-removedeliverednotifications
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||
completion()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
private func manualIconName(for provider: String, color: String) -> String {
|
||||
switch provider.uppercased() {
|
||||
case "USGS": return "star\(color).png"
|
||||
case "SGC": return "star3\(color).png"
|
||||
case "CSN": return "star3f\(color).png"
|
||||
case "SSN": return "star4\(color).png"
|
||||
case "INPRES": return "star4r\(color).png"
|
||||
case "FUNVISIS": return "star6\(color).png"
|
||||
case "INETER": return "triangle\(color).png"
|
||||
case "RSN": return "triangle2\(color).png"
|
||||
case "PHIVOLCS": return "triround_inner\(color).png"
|
||||
case "IGEPN": return "triround\(color).png"
|
||||
case "INGV": return "circle\(color).png"
|
||||
case "EMSC": return "dyamond\(color).png"
|
||||
case "IGP": return "dyamond_round\(color).png"
|
||||
case "JMA": return "esa\(color).png"
|
||||
case "GEONET": return "oct\(color).png"
|
||||
case "CSI": return "penta\(color).png"
|
||||
case "IGN": return "square\(color).png"
|
||||
case "UASD", "BDTIM", "NCS": return "thick_star\(color).png"
|
||||
case "RSPR": return "star6f\(color).png"
|
||||
case "UOA": return "triangle\(color).png"
|
||||
default: return ""
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
+2
-1
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1200"
|
||||
LastUpgradeVersion = "1410"
|
||||
wasCreatedForAppExtension = "YES"
|
||||
version = "2.0">
|
||||
<BuildAction
|
||||
@@ -74,6 +74,7 @@
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
askForAppToLaunch = "Yes"
|
||||
launchAutomaticallySubstyle = "2">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
|
||||
+2
-1
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1200"
|
||||
LastUpgradeVersion = "1410"
|
||||
wasCreatedForAppExtension = "YES"
|
||||
version = "2.0">
|
||||
<BuildAction
|
||||
@@ -74,6 +74,7 @@
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
askForAppToLaunch = "Yes"
|
||||
launchAutomaticallySubstyle = "2">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1200"
|
||||
LastUpgradeVersion = "1410"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "shogun",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/andreabusi-it/Shogun",
|
||||
"state" : {
|
||||
"revision" : "3ffa7cfbdcbfb9868c853900f63d7e3248db797e",
|
||||
"version" : "1.1.1"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 2
|
||||
}
|
||||
@@ -18,11 +18,15 @@
|
||||
#import "EQNNotificheSegnalazioniUtente.h"
|
||||
#import "EQNNotificheReteSismiche.h"
|
||||
#import "EQNMainTabBarController.h"
|
||||
#import "NSDictionary+EQNExtensions.h"
|
||||
|
||||
@import Firebase;
|
||||
@import FirebaseCrashlytics;
|
||||
@import UserNotifications;
|
||||
@import AppTrackingTransparency;
|
||||
@import FirebaseCore;
|
||||
@import FirebaseMessaging;
|
||||
@import FirebaseCrashlytics;
|
||||
@import GoogleMobileAds;
|
||||
@import FBSDKCoreKit;
|
||||
|
||||
@interface AppDelegate () <FIRMessagingDelegate, UNUserNotificationCenterDelegate>
|
||||
@property (strong, nonatomic) NSArray<id<EQNCommandProtocol>> *commands;
|
||||
@@ -43,27 +47,21 @@
|
||||
}];
|
||||
#endif
|
||||
|
||||
[EQNUser defaultUser];
|
||||
[EQNManager defaultManager];
|
||||
[FIRApp configure];
|
||||
[FIRMessaging messaging].delegate = self;
|
||||
|
||||
// add some generic logs for Crashlytics
|
||||
NSString *language = [[NSLocale preferredLanguages] firstObject];
|
||||
[[FIRCrashlytics crashlytics] setCustomValue:language forKey:@"lang"];
|
||||
|
||||
// execute commands
|
||||
EQNStartupCommandsBuilder *builder = [[EQNStartupCommandsBuilder alloc] init];
|
||||
self.commands = [builder build];
|
||||
[builder executeWithCommands:self.commands];
|
||||
|
||||
[UNUserNotificationCenter currentNotificationCenter].delegate = self;
|
||||
UNAuthorizationOptions authOptions = UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge;
|
||||
[[UNUserNotificationCenter currentNotificationCenter]
|
||||
requestAuthorizationWithOptions:authOptions
|
||||
completionHandler:^(BOOL granted, NSError * _Nullable error) {
|
||||
[self registraNotifica];
|
||||
}];
|
||||
[EQNUser defaultUser];
|
||||
[EQNManager defaultManager];
|
||||
[self configureFirebase];
|
||||
[self configureFacebookSDKWithApplication:application andOptions:launchOptions];
|
||||
|
||||
// add some generic logs for Crashlytics
|
||||
NSString *language = [[NSLocale preferredLanguages] firstObject];
|
||||
[[FIRCrashlytics crashlytics] setCustomValue:language forKey:@"lang"];
|
||||
|
||||
[self configurePushNotifications];
|
||||
|
||||
[application setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
|
||||
[application registerForRemoteNotifications];
|
||||
@@ -89,9 +87,9 @@
|
||||
[[EQNManager defaultManager] avviaManager];
|
||||
[[EQNAccelerometroManager sharedInstance] startUpdatingLocationBackground];
|
||||
|
||||
NSUInteger counter = [[NSUserDefaults standardUserDefaults] integerForKey:EQNUserDefaultProDiscountOpenCounter];
|
||||
NSUInteger counter = [[NSUserDefaults standardUserDefaults] integerForKey:NSUserDefaults.UserDataProDiscountOpenCounter];
|
||||
counter += 1;
|
||||
[[NSUserDefaults standardUserDefaults] setInteger:counter forKey:EQNUserDefaultProDiscountOpenCounter];
|
||||
[[NSUserDefaults standardUserDefaults] setInteger:counter forKey:NSUserDefaults.UserDataProDiscountOpenCounter];
|
||||
}
|
||||
|
||||
|
||||
@@ -125,8 +123,8 @@
|
||||
{
|
||||
NSString *token = [self stringWithDeviceToken:deviceToken];
|
||||
NSLog(@"[DEBUG] push token: %@", token);
|
||||
[[NSUserDefaults standardUserDefaults] setObject:token forKey:EQNUserDefaultPushToken];
|
||||
|
||||
|
||||
[[NSUserDefaults standardUserDefaults] setObject:token forKey:NSUserDefaults.UserDataPushToken];
|
||||
FIRMessaging.messaging.APNSToken = deviceToken;
|
||||
}
|
||||
|
||||
@@ -149,8 +147,8 @@
|
||||
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler
|
||||
{
|
||||
// execute common logic and refresh the proper tab
|
||||
NSDictionary *userInfo = notification.request.content.userInfo;
|
||||
[self handlePushNotificationWithUserInfo:userInfo];
|
||||
UNNotificationContent *content = notification.request.content;
|
||||
[self handlePushNotificationWithNotificationContent:content];
|
||||
|
||||
// Change this to your preferred presentation option
|
||||
completionHandler(UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionSound);
|
||||
@@ -159,9 +157,11 @@
|
||||
// Handle notification messages after display notification is tapped by the user.
|
||||
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler
|
||||
{
|
||||
NSLog(@"[DEBUG] push payload\n%@", [response.notification.request.content.userInfo eqn_jsonStringWithPrettyPrint:YES]);
|
||||
|
||||
// execute common logic and refresh the proper tab
|
||||
NSDictionary *userInfo = response.notification.request.content.userInfo;
|
||||
[self handlePushNotificationWithUserInfo:userInfo];
|
||||
UNNotificationContent *content = response.notification.request.content;
|
||||
[self handlePushNotificationWithNotificationContent:content];
|
||||
|
||||
completionHandler();
|
||||
}
|
||||
@@ -178,18 +178,24 @@
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
- (void)handlePushNotificationWithUserInfo:(NSDictionary *)userInfo
|
||||
- (void)handlePushNotificationWithNotificationContent:(UNNotificationContent *)content
|
||||
{
|
||||
NSString *type = content.userInfo[@"type"];
|
||||
|
||||
EQNTabBarSection section = EQNTabBarSectionAllerte;
|
||||
if ([userInfo[@"type"] isEqualToString:@"eqn"]) {
|
||||
NSDate *dataRicezione = [NSDate date];
|
||||
[[NSUserDefaults standardUserDefaults] setObject:dataRicezione forKey:EQNUserDefaultRealTimeAlertDate];
|
||||
|
||||
[EQNUtility storeDictionary:userInfo toUserDefaultForKey:EQNUserDefaultRealTimeAlertPayload];
|
||||
if ([type isEqualToString:@"eqn"]) {
|
||||
// Store both original payload and modified title/body
|
||||
// This will be usefull to avoid to re-evaluate logic for title display.
|
||||
NSDictionary *notification = @{
|
||||
@"title": content.title,
|
||||
@"body": content.body,
|
||||
@"userInfo": content.userInfo
|
||||
};
|
||||
[EQNRealtimePushNotification storeNotificationWithPayload:notification];
|
||||
section = EQNTabBarSectionAllerte;
|
||||
} else if([userInfo[@"type"] isEqualToString:@"manual"]) {
|
||||
} else if([type isEqualToString:@"manual"]) {
|
||||
section = EQNTabBarSectionSegnalazioni;
|
||||
} else if([userInfo[@"type"] isEqualToString:@"official"]) {
|
||||
} else if([type isEqualToString:@"official"]) {
|
||||
section = EQNTabBarSectionRetiSismiche;
|
||||
}
|
||||
|
||||
@@ -197,19 +203,62 @@
|
||||
[self.mainTabBarController selectSection:section];
|
||||
}
|
||||
|
||||
#pragma mark - Configurations
|
||||
|
||||
- (void)configurePushNotifications
|
||||
{
|
||||
[UNUserNotificationCenter currentNotificationCenter].delegate = self;
|
||||
UNAuthorizationOptions authOptions = UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge;
|
||||
[[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions:authOptions completionHandler:^(BOOL granted, NSError * _Nullable error) {
|
||||
[self registraNotifica];
|
||||
[self configureAppTracking];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)configureAppTracking
|
||||
{
|
||||
if (@available(iOS 14, *)) {
|
||||
// add a delay otherwise the alert will not be displayed
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
[ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) {
|
||||
if (status == ATTrackingManagerAuthorizationStatusAuthorized) {
|
||||
FBSDKSettings.sharedSettings.isAdvertiserTrackingEnabled = YES;
|
||||
} else {
|
||||
FBSDKSettings.sharedSettings.isAdvertiserTrackingEnabled = NO;
|
||||
}
|
||||
}];
|
||||
});
|
||||
} else {
|
||||
FBSDKSettings.sharedSettings.isAdvertiserTrackingEnabled = YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)configureFirebase
|
||||
{
|
||||
[FIRApp configure];
|
||||
[FIRMessaging messaging].delegate = self;
|
||||
}
|
||||
|
||||
- (void)configureFacebookSDKWithApplication:(UIApplication *)application andOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
[FBSDKApplicationDelegate.sharedInstance application:application didFinishLaunchingWithOptions:launchOptions];
|
||||
[FBSDKSettings.sharedSettings setIsAdvertiserIDCollectionEnabled:YES];
|
||||
[FBSDKSettings.sharedSettings setIsAdvertiserIDCollectionEnabled:YES];
|
||||
}
|
||||
|
||||
#pragma mark - FIRMessagingDelegate
|
||||
|
||||
- (void)messaging:(FIRMessaging *)messaging didReceiveRegistrationToken:(NSString *)fcmToken
|
||||
{
|
||||
NSLog(@"[Firebase] fcmToken %@", fcmToken);
|
||||
if (![[NSUserDefaults standardUserDefaults] objectForKey:EQNUserDefaultUserFirebaseToken]) {
|
||||
if (EQNUserData.sharedData.isFirstStart) {
|
||||
// save default values for notification settings
|
||||
[EQNAllertaSismica saveDefaultValues];
|
||||
[EQNNotificheSegnalazioniUtente saveDefaultValues];
|
||||
[EQNNotificheReteSismiche saveDefaultValues];
|
||||
}
|
||||
|
||||
[EQNUser defaultUser].tokenUser = fcmToken;
|
||||
[EQNUser.defaultUser registerUserIfNeededWithFirebaseToken:fcmToken];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
//
|
||||
// Constants.swift
|
||||
// Earthquake Network
|
||||
//
|
||||
// Created by Andrea Busi on 14/07/23.
|
||||
// Copyright © 2023 Earthquake Network. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
@objc
|
||||
extension UserDefaults {
|
||||
// UserDefaults condivisi con l'AppGroup
|
||||
@objc(appGroupUserDefaults)
|
||||
static let appGroup = UserDefaults(suiteName: "group.com.finazzi.distquake")
|
||||
|
||||
// Impostazioni della sezione `Allerta in tempo reale`
|
||||
static let AllertaSismicaAbilitato = "NOTIFICHE_ALLERA_SISMICA_ABILITATO"
|
||||
static let AllertaSismicaCriticalAlerts = "NOTIFICHE_ALLERA_SISMICA_CRITICAL_ALERTS"
|
||||
static let AllertaSismicaSismiDaNotificare = "NOTIFICHE_ALLERA_SISMICA_SISMI_DA_NOTIFICARE"
|
||||
static let AllertaSismicaRaggioSismiLievi = "NOTIFICHE_ALLERA_SISMICA_RAGGIO_SISMI_LIEVI"
|
||||
static let AllertaSismicaRaggioSismiForti = "NOTIFICHE_ALLERA_SISMICA_RAGGIO_SISMI_FORTI"
|
||||
static let AllertaSismicaImpostaVolume = "NOTIFICHE_ALLERA_SISMICA_IMPOSTA_VOLUME"
|
||||
static let AllertaSismicaTestaAllarma = "NOTIFICHE_ALLERA_SISMICA_TESTA_ALLARME"
|
||||
static let AllertaSismicaAbilitaIntervallo = "NOTIFICHE_ALLERA_SISMICA_ABILITA_INTERVALLO"
|
||||
static let AllertaSismicaOraInizio = "NOTIFICHE_ALLERA_SISMICA_ORA_INIZIO"
|
||||
static let AllertaSismicaOraFine = "NOTIFICHE_ALLERA_SISMICA_ORA_INIZIO"
|
||||
|
||||
// Impostazioni della sezione `Notifiche da reti sismiche`
|
||||
static let NotificheRetiSismicheAbilitato = "NOTIFICHE_ATTIVA_RETI_SISMICHE"
|
||||
static let NotificheRetiSismicheViciniAbilitato = "NOTIFICHE_ATTIVA_RETI_SISMICHE_VICINE"
|
||||
static let NotificheRetiSismicheTerremotiFortiAbilitato = "NOTIFICHE_ATTIVA_RETI_TERREMOTI_FORTI"
|
||||
static let NotificheRetiSismicheDistanzaPosizione = "NOTIFICHE_DISTANZA_POSIZIONE_RETI_SISMICHE"
|
||||
static let NotificheRetiSismicheEnergiaSisma = "NOTIFICHE_ATTIVA_RETI_ENERGIA_SISMI"
|
||||
static let NotificheRetiSismicheEnergiaTerremotiForti = "NOTIFICHE_ATTIVA_RETI_ENERGIA_FORTI"
|
||||
static let NotificheRetiSismicheListaEnti = "NOTIFICHE_ATTIVA_RETI_LISTA_ENTI"
|
||||
|
||||
// Impostazioni della sezione `Notifiche segnalazioni utente`
|
||||
static let NotificheSegnalazioniUtenteAbilitato = "NOTIFICHE_SU_ATTIVA_SEGNALAZIONE_UTENTE"
|
||||
static let NotificheSegnalazioniUtenteDistanzaPosizione = "NOTIFICHE_SU_DISTANZA_POSIZIONE"
|
||||
|
||||
// Messaggio in segnalazione utente
|
||||
static let UserReportMessage = "DATA_MESSAGE_EQN"
|
||||
static let UserReportCodeStatus = "CODE_MESSAGE_EQN"
|
||||
|
||||
// Proprietà e preferenze dell'utente
|
||||
/// Ultima posizione conosciuta dell'utente
|
||||
static let UserDataLastLocation = "EQNLast_Location"
|
||||
/// Token Firebase dell'utente corrente
|
||||
static let UserDataFirebaseToken = "EQNToken_User"
|
||||
/// Server user ID dell'utente corrente
|
||||
static let UserDataUserId = "EQNUSER_ID"
|
||||
/// Reti sismiche selezionate
|
||||
static let UserDataSelectedSeismicNetworks = "IMPOSTAZIONE_ENTI_RETI_SISMICHEI"
|
||||
/// Token delle notifiche push
|
||||
static let UserDataPushToken = "EQNetwork.PushToken"
|
||||
/// Numero di aperture dell'app per sbloccare la versione Pro scontata
|
||||
static let UserDataProDiscountOpenCounter = "CONTEGGIO_APERTURE_PER_SCONTO"
|
||||
/// Prezzo scontato per la versione pro scaduto
|
||||
static let UserDataProDiscountExpired = "PREZZO_SCONTATO_SCADUTO"
|
||||
|
||||
// Migrazioni
|
||||
static let AppMigrationV5_3 = "EQNUserDefaultMigrationV5_3"
|
||||
static let AppMigrationV5_4 = "EQNUserDefaultMigrationV5_4"
|
||||
|
||||
// Notifica allerta salvata
|
||||
static let RealTimeAlertPayload = "EQNData.RealtimePushNotificationPayload"
|
||||
static let RealTimeAlertDate = "EQNData.RealtimeAlertDate"
|
||||
}
|
||||
@@ -26,7 +26,6 @@
|
||||
|
||||
@implementation AllerteViewController
|
||||
|
||||
static NSString * const SegueIdentifierProVersion = @"ShowProVersion";
|
||||
static NSString * const SegueIdentifierPrioritySubscriptions = @"ShowPrioritySubscriptions";
|
||||
|
||||
/// Sections inside the app
|
||||
@@ -36,7 +35,6 @@ typedef NS_ENUM(NSInteger, AllerteTableRow) {
|
||||
AllerteTableRowAllertePassate,
|
||||
AllerteTableRowReteSmartphone,
|
||||
AllerteTableRowServizioPriorita,
|
||||
AllerteTableRowVersionePro, // non più visualizzata con passaggio dell'app a pagamento
|
||||
AllerteTableRowDatiPosizione
|
||||
};
|
||||
|
||||
@@ -104,7 +102,7 @@ typedef NS_ENUM(NSInteger, AllerteTableRow) {
|
||||
- (void)setupUI
|
||||
{
|
||||
self.title = [NSLocalizedString(@"tab_network", nil) capitalizedString];
|
||||
self.tableView.estimatedRowHeight = 600.0;
|
||||
self.tableView.estimatedRowHeight = 200.0;
|
||||
self.tableView.rowHeight = UITableViewAutomaticDimension;
|
||||
}
|
||||
|
||||
@@ -113,19 +111,17 @@ typedef NS_ENUM(NSInteger, AllerteTableRow) {
|
||||
[super refreshUI];
|
||||
|
||||
// `AllerteTableRowReteSmartphone` and `AllerteTableRowDatiPosizione` are hidden bu default, user can show them
|
||||
BOOL showAllCards = [[NSUserDefaults standardUserDefaults] boolForKey:EQNUserDefaultKeyAlertsShowAllCards];
|
||||
BOOL showAllCards = AppPreferences.shared.alertsShowAllCards;
|
||||
self.expandeCollapseButton.image = showAllCards ? [UIImage imageNamed:@"navbar-icon-arrow-collapse"] : [UIImage imageNamed:@"navbar-icon-arrow-expand"];
|
||||
|
||||
// controlliamo se c'è una notifica in tempo reale da mostrare
|
||||
NSDate *date = [[NSUserDefaults standardUserDefaults] objectForKey:EQNUserDefaultRealTimeAlertDate];
|
||||
NSDictionary *info = [EQNUtility loadDictionaryFromUserDefaultsForKey:EQNUserDefaultRealTimeAlertPayload];
|
||||
if (date && info && [EQNUtility getDifferenceMinute:date] < EQNRealtimeAlertExpiration) {
|
||||
EQNRealtimePushNotification *notification = [EQNRealtimePushNotification storedNotification];
|
||||
if (notification) {
|
||||
self.isNotificaAttiva = YES;
|
||||
|
||||
// mostriamo la schermata solo se il countdown non è a zero
|
||||
EQNRealtimeAlert *alert = [[EQNRealtimeAlert alloc] initWithNotification:info];
|
||||
if (alert != nil && ![alert isCountdownExpired]) {
|
||||
RealtimeAlertViewController *controller = [[RealtimeAlertViewController alloc] initWithAlert:alert];
|
||||
if (![notification isCountdownExpired]) {
|
||||
RealtimeAlertViewController *controller = [[RealtimeAlertViewController alloc] initWithNotification:notification];
|
||||
if (@available(iOS 13.0, *)) {
|
||||
controller.modalInPresentation = YES;
|
||||
}
|
||||
@@ -162,8 +158,7 @@ typedef NS_ENUM(NSInteger, AllerteTableRow) {
|
||||
|
||||
- (void)resetRealtimeAlert
|
||||
{
|
||||
[[NSUserDefaults standardUserDefaults] removeObjectForKey:EQNUserDefaultRealTimeAlertDate];
|
||||
[[NSUserDefaults standardUserDefaults] removeObjectForKey:EQNUserDefaultRealTimeAlertPayload];
|
||||
[EQNRealtimePushNotification removeStoredNotification];
|
||||
self.isNotificaAttiva = NO;
|
||||
}
|
||||
|
||||
@@ -177,9 +172,7 @@ typedef NS_ENUM(NSInteger, AllerteTableRow) {
|
||||
- (IBAction)collapseExpandTapped:(id)sender
|
||||
{
|
||||
// toggle saved value
|
||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||
BOOL showAll = [defaults boolForKey:EQNUserDefaultKeyAlertsShowAllCards];
|
||||
[defaults setBool:!showAll forKey:EQNUserDefaultKeyAlertsShowAllCards];
|
||||
AppPreferences.shared.alertsShowAllCards = !AppPreferences.shared.alertsShowAllCards;
|
||||
|
||||
[self refreshUI];
|
||||
}
|
||||
@@ -269,8 +262,8 @@ typedef NS_ENUM(NSInteger, AllerteTableRow) {
|
||||
} else if (tableRow == AllerteTableRowSismiRilevati) {
|
||||
if (self.isNotificaAttiva) {
|
||||
AlertsSeismicNotificationExpandedTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"SeismicNotificationExpandedCell" forIndexPath:indexPath];
|
||||
NSDictionary *info = [EQNUtility loadDictionaryFromUserDefaultsForKey:EQNUserDefaultRealTimeAlertPayload];
|
||||
cell.notification = info;
|
||||
EQNRealtimePushNotification *notification = [EQNRealtimePushNotification storedNotification];
|
||||
cell.notification = notification;
|
||||
|
||||
__weak AllerteViewController *weakSelf = self;
|
||||
cell.onTapClose = ^{
|
||||
@@ -329,10 +322,6 @@ typedef NS_ENUM(NSInteger, AllerteTableRow) {
|
||||
cell.smartphoneNetwork = [EQNManager defaultManager].rete_smartphone;
|
||||
return cell;
|
||||
|
||||
} else if (tableRow == AllerteTableRowVersionePro) {
|
||||
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ProVersionCell" forIndexPath:indexPath];
|
||||
return cell;
|
||||
|
||||
} else if (tableRow == AllerteTableRowDatiPosizione) {
|
||||
AlertsPositionDataTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"PositionDataCell" forIndexPath:indexPath];
|
||||
cell.position = [EQNUser defaultUser].lastPosition;
|
||||
@@ -351,9 +340,6 @@ typedef NS_ENUM(NSInteger, AllerteTableRow) {
|
||||
case AllerteTableRowServizioPriorita:
|
||||
[self performSegueWithIdentifier:SegueIdentifierPrioritySubscriptions sender:nil];
|
||||
break;
|
||||
case AllerteTableRowVersionePro:
|
||||
[self performSegueWithIdentifier:SegueIdentifierProVersion sender:nil];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
+3
-2
@@ -43,11 +43,12 @@ class AlertsPositionDataTableViewCell: EQNBaseTableViewCell {
|
||||
positionLabel.text = EQNUtility.coordinateString(coordinate: position.coordinate)
|
||||
|
||||
if let solar = Solar(coordinate: position.coordinate) {
|
||||
let timeZone = TimeZone.current.localizedName(for: .generic, locale: .current) ?? TimeZone.current.identifier
|
||||
if let sunrise = solar.sunrise {
|
||||
sunriseTimeLabel.text = dateFormatter.string(from: sunrise) + " (\(TimeZone.current.identifier))"
|
||||
sunriseTimeLabel.text = dateFormatter.string(from: sunrise) + " \(timeZone)"
|
||||
}
|
||||
if let sunset = solar.sunset {
|
||||
sunsetTimeLabel.text = dateFormatter.string(from: sunset) + " (\(TimeZone.current.identifier))"
|
||||
sunsetTimeLabel.text = dateFormatter.string(from: sunset) + " \(timeZone)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+19
-2
@@ -40,7 +40,24 @@ class AlertsPriorityServiceTableViewCell: EQNBaseTableViewCell {
|
||||
private func updateUI() {
|
||||
guard let smartphoneNetwork = smartphoneNetwork else { return }
|
||||
|
||||
let formattedTime = EQNUtility.formattedString(forTimeDifference: smartphoneNetwork.lastSubscriptionDiff)
|
||||
lastSubscriptionLabel.text = String(format: NSLocalizedString("inapp_adv_time", comment: ""), formattedTime)
|
||||
lastSubscriptionLabel.text = subscriptionText(for: smartphoneNetwork.lastSubscriptionDiff)
|
||||
}
|
||||
|
||||
private func subscriptionText(for time: Int) -> String {
|
||||
var format = ""
|
||||
var finalValue = time
|
||||
|
||||
// check for minutes, hours or days
|
||||
if time < 60 {
|
||||
format = NSLocalizedString("inapp_adv_minutes", comment: "")
|
||||
} else if time < 1440 {
|
||||
finalValue = time / 60
|
||||
format = NSLocalizedString("inapp_adv_hours", comment: "")
|
||||
} else {
|
||||
finalValue = time / 1440
|
||||
format = NSLocalizedString("inapp_adv_days", comment: "")
|
||||
}
|
||||
|
||||
return String.localizedStringWithFormat(format, finalValue)
|
||||
}
|
||||
}
|
||||
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
//
|
||||
// AlertsProVersionTableViewCell.swift
|
||||
// Earthquake Network
|
||||
//
|
||||
// Created by Andrea Busi on 05/04/21.
|
||||
// Copyright © 2021 Earthquake Network. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class AlertsProVersionTableViewCell: EQNBaseTableViewCell {
|
||||
|
||||
@IBOutlet private weak var headerLabel: UILabel!
|
||||
@IBOutlet private weak var descriptionLabel: UILabel!
|
||||
|
||||
// MARK: - View Lifecycle
|
||||
|
||||
override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
|
||||
localizeUI()
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func localizeUI() {
|
||||
headerLabel.text = NSLocalizedString("network_pro", comment: "")
|
||||
descriptionLabel.text = NSLocalizedString("network_topro", comment: "")
|
||||
}
|
||||
}
|
||||
+42
-43
@@ -8,12 +8,13 @@
|
||||
|
||||
import UIKit
|
||||
import MapKit
|
||||
import Shogun
|
||||
|
||||
class AlertsSeismicNotificationExpandedTableViewCell: EQNBaseTableViewCell, MKMapViewDelegate {
|
||||
|
||||
typealias DefaultCompletion = () -> Void
|
||||
|
||||
@objc var notification: [String: Any]? {
|
||||
@objc var notification: EQNRealtimePushNotification? {
|
||||
didSet {
|
||||
updateUI()
|
||||
}
|
||||
@@ -28,6 +29,7 @@ class AlertsSeismicNotificationExpandedTableViewCell: EQNBaseTableViewCell, MKMa
|
||||
|
||||
@IBOutlet weak var notificationTitleLabel: UILabel!
|
||||
@IBOutlet weak var notificationDescriptionLabel: UILabel!
|
||||
@IBOutlet weak var notificationIntensityLabel: UILabel!
|
||||
@IBOutlet weak var waveTimeLabel: UILabel!
|
||||
@IBOutlet weak var mapView: MKMapView! {
|
||||
didSet {
|
||||
@@ -51,6 +53,7 @@ class AlertsSeismicNotificationExpandedTableViewCell: EQNBaseTableViewCell, MKMa
|
||||
super.awakeFromNib()
|
||||
|
||||
localizeUI()
|
||||
setUI()
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
@@ -63,56 +66,46 @@ class AlertsSeismicNotificationExpandedTableViewCell: EQNBaseTableViewCell, MKMa
|
||||
descriptionLabel.text = NSLocalizedString("map_smartphone_magnitude", comment: "")
|
||||
}
|
||||
|
||||
private func setUI() {
|
||||
shareButton.layer.borderColor = AppTheme.Colors.darkGray.cgColor
|
||||
rateAppButton.layer.borderColor = AppTheme.Colors.darkGray.cgColor
|
||||
viewOnTwitterButton.layer.borderColor = AppTheme.Colors.darkGray.cgColor
|
||||
closeButton.layer.borderColor = AppTheme.Colors.darkGray.cgColor
|
||||
}
|
||||
|
||||
private func updateUI() {
|
||||
// clearn any other previous notifications
|
||||
notificationTitleLabel.text = ""
|
||||
notificationDescriptionLabel.text = ""
|
||||
mapView.removeAnnotations(mapView.annotations)
|
||||
|
||||
guard let notification = notification,
|
||||
let aps = notification["aps"] as? [String: Any],
|
||||
let alert = aps["alert"] as? [String: Any] else { return }
|
||||
guard let notification = notification else { return }
|
||||
|
||||
let intensity = notification.eqn_intValue(for: "intensity") ?? 0
|
||||
containerView.backgroundColor = color(for: intensity)
|
||||
containerView.backgroundColor = backgroundColor(for: notification.relativeIntensity())
|
||||
notificationTitleLabel.text = notification.title
|
||||
notificationIntensityLabel.text = notification.displayBody
|
||||
notificationIntensityLabel.textColor = notification.relativeIntensityColor
|
||||
|
||||
if let title = alert["loc-key"] as? String, let args = alert["loc-args"] as? [String], let arg = args.first {
|
||||
notificationTitleLabel.text = String(format: NSLocalizedString(title, comment: ""), arg)
|
||||
}
|
||||
|
||||
// get coordinate
|
||||
var coordinate: CLLocation?
|
||||
if let latitude = notification.eqn_doubleValue(for: "latitude"),
|
||||
let longitude = notification.eqn_doubleValue(for: "longitude") {
|
||||
if let date = notification.dateTime {
|
||||
|
||||
coordinate = CLLocation(latitude: latitude, longitude: longitude)
|
||||
}
|
||||
|
||||
if let coordinate = coordinate,
|
||||
let counter = notification["counter"],
|
||||
let dateString = notification["datetime"] as? String,
|
||||
let date = EQNUtility.getDateFrom(dateString) {
|
||||
|
||||
let distance = EQNUser.default().lastPosition?.distance(from: coordinate) ?? 0.0
|
||||
let distance = EQNUser.default().lastPosition?.distance(from: notification.coordinate) ?? 0.0
|
||||
let distanceRound = Int(round(distance / 1_000))
|
||||
let difference = Int(NSDate().timeIntervalSince(date) / 60.0)
|
||||
|
||||
notificationDescriptionLabel.text = ""
|
||||
+ NSLocalizedString("official_card_distance", comment: "") + " \(distanceRound) km"
|
||||
+ " - " + EQNUtility.formattedString(forTimeDifference: difference)
|
||||
+ "\n" + String(format: NSLocalizedString("map_number", comment: ""), "\(counter)")
|
||||
+ "\n" + String(format: NSLocalizedString("map_number", comment: ""), "\(notification.counter)")
|
||||
}
|
||||
|
||||
if let coordinate = coordinate {
|
||||
let span = MKCoordinateSpan(latitudeDelta: 10.5, longitudeDelta: 10.5)
|
||||
let region = MKCoordinateRegion(center: coordinate.coordinate, span: span)
|
||||
|
||||
mapView.setCenter(coordinate.coordinate, animated: false)
|
||||
mapView.setRegion(region, animated: true)
|
||||
|
||||
let annotation = EQNMapAnnotationPastquake(title: "", coordinate: coordinate.coordinate, intensity: intensity)
|
||||
mapView.addAnnotation(annotation)
|
||||
}
|
||||
let span = MKCoordinateSpan(latitudeDelta: 10.5, longitudeDelta: 10.5)
|
||||
let region = MKCoordinateRegion(center: notification.coordinate.coordinate, span: span)
|
||||
|
||||
mapView.setCenter(notification.coordinate.coordinate, animated: false)
|
||||
mapView.setRegion(region, animated: true)
|
||||
|
||||
let annotation = EQNMapAnnotationPastquake(title: "", coordinate: notification.coordinate.coordinate, intensity: notification.intensity)
|
||||
mapView.addAnnotation(annotation)
|
||||
}
|
||||
|
||||
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
|
||||
@@ -125,15 +118,6 @@ class AlertsSeismicNotificationExpandedTableViewCell: EQNBaseTableViewCell, MKMa
|
||||
annotationView.title = annotation.title
|
||||
return annotationView
|
||||
}
|
||||
|
||||
private func color(for intensity: Int) -> UIColor {
|
||||
switch intensity {
|
||||
case 0: return UIColor(red: 208.0/255.0, green: 234.0/255.0, blue: 201.0/255.0, alpha:1.0)
|
||||
case 1: return UIColor(red: 254.0/255.0, green: 252.0/255.0, blue: 203.0/255.0, alpha:1.0)
|
||||
case 2: return UIColor(red: 254.0/255.0, green: 186.0/255.0, blue: 186.0/255.0, alpha:1.0)
|
||||
default: return UIColor.white
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
@@ -152,4 +136,19 @@ class AlertsSeismicNotificationExpandedTableViewCell: EQNBaseTableViewCell, MKMa
|
||||
@IBAction private func closeTapped(_ sender: UIButton) {
|
||||
onTapClose?()
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
private func backgroundColor(for intensity: Double) -> UIColor {
|
||||
switch intensity {
|
||||
case _ where intensity < 0.004:
|
||||
return UIColor(named: "Gray (card background)")!
|
||||
case _ where intensity < 0.30:
|
||||
return UIColor(named: "Green (card background)")!
|
||||
case _ where intensity < 0.70:
|
||||
return UIColor(named: "Yellow (card background)")!
|
||||
default:
|
||||
return UIColor(named: "Red (card background)")!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +65,8 @@ class PasquakesMapViewController: EQNBaseMapViewController {
|
||||
}
|
||||
|
||||
override func didTapAnnotation(_ annotation: MKAnnotation) {
|
||||
mapView.deselectAnnotation(annotation, animated: true)
|
||||
|
||||
guard let annotation = annotation as? EQNMapAnnotationPastquake, let pastquake = annotation.pastquake else {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -57,8 +57,8 @@ class EQNDebugViewController: UIViewController {
|
||||
}
|
||||
|
||||
private func loadDebugData() {
|
||||
let firebaseToken = UserDefaults.standard.string(forKey: EQNUserDefaultUserFirebaseToken) ?? ""
|
||||
let pushToken = UserDefaults.standard.string(forKey: EQNUserDefaultPushToken) ?? ""
|
||||
let firebaseToken = UserDefaults.standard.string(forKey: UserDefaults.UserDataFirebaseToken) ?? ""
|
||||
let pushToken = UserDefaults.standard.string(forKey: UserDefaults.UserDataPushToken) ?? ""
|
||||
|
||||
let text =
|
||||
"""
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
[[EQNManager defaultManager] avviaManager];
|
||||
[[EQNAccelerometroManager sharedInstance] startUpdatingLocationBackground];
|
||||
|
||||
self.testo = [NSString stringWithFormat:@" LOG ID UTENTE %@\n\nTOKEN FIREBASE:\n%@\n\n", [EQNUser defaultUser].user_ID, [EQNUser defaultUser].tokenUser];
|
||||
self.testo = [NSString stringWithFormat:@" LOG ID UTENTE %@\n\nTOKEN FIREBASE:\n%@\n\n", [EQNUser defaultUser].user_ID, EQNUserData.sharedData.firebaseToken];
|
||||
|
||||
self.logView.text = self.testo;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,11 @@
|
||||
|
||||
- (BOOL)isBannerVisible
|
||||
{
|
||||
#if ADS_ENABLED
|
||||
return ![EQNPurchaseUtility isProVersionEnabled];
|
||||
#else
|
||||
return NO;
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma mark - View Lifecycle
|
||||
|
||||
@@ -63,9 +63,9 @@ static NSString * const SegueIdentifierLogs = @"ShowLogs";
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"retry", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
|
||||
// retry server registration
|
||||
[[EQNUser defaultUser] verificaRegistrazione];
|
||||
[[EQNUser defaultUser] retryUserRegistration];
|
||||
}]];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"options_cancel", nil) style:UIAlertActionStyleCancel handler:nil]];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"status_cancel", nil) style:UIAlertActionStyleCancel handler:nil]];
|
||||
[self presentViewController:alert animated:YES completion:nil];
|
||||
});
|
||||
}
|
||||
@@ -136,7 +136,7 @@ static NSString * const SegueIdentifierLogs = @"ShowLogs";
|
||||
}
|
||||
|
||||
// tap 5 times on "Settings" to open debug view
|
||||
if ([controller isKindOfClass:[SettingsViewController class]]) {
|
||||
if ([controller isKindOfClass:[SettingsViewController class]] && EQNEnableDebugView) {
|
||||
self.debugTapCounter += 1;
|
||||
if (self.debugTapCounter == 5) {
|
||||
self.debugTapCounter = 0;
|
||||
|
||||
@@ -1,181 +0,0 @@
|
||||
//
|
||||
// PurchaseProVersionViewController.swift
|
||||
// Earthquake Network
|
||||
//
|
||||
// Created by Busi Andrea on 29/07/2020.
|
||||
// Copyright © 2020 Earthquake Network. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SafariServices
|
||||
import StoreKit
|
||||
|
||||
|
||||
class PurchaseProVersionViewController: UIViewController {
|
||||
|
||||
@IBOutlet private weak var containerView: UIView!
|
||||
@IBOutlet private weak var titleLabel: UILabel!
|
||||
@IBOutlet private weak var subtitleLabel: UILabel!
|
||||
@IBOutlet private weak var discountTextLabel: UILabel!
|
||||
@IBOutlet private weak var descriptionTextLabel: UILabel!
|
||||
@IBOutlet private weak var openPrivacyButton: UIButton!
|
||||
@IBOutlet private weak var openTermsButton: UIButton!
|
||||
@IBOutlet private weak var payingLabel: UILabel!
|
||||
@IBOutlet private weak var priceLabel: UILabel!
|
||||
@IBOutlet private weak var purchaseButton: UIButton!
|
||||
|
||||
// MARK: - Internal
|
||||
|
||||
private var products = [SKProduct]()
|
||||
private var proProduct: SKProduct?
|
||||
|
||||
private var restoreTapped = false
|
||||
/// Time remaining (in hours) for discounted price. If zero, no discount available
|
||||
private var discountTimeRemaining: Int = 0
|
||||
|
||||
// MARK: - View Lifecycle
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(handlePurchaseNotification(_:)),
|
||||
name: .EQNInAppPurchaseDidComplete,
|
||||
object: nil)
|
||||
|
||||
configureUI()
|
||||
loadProducts()
|
||||
checkDiscountPrice()
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func configureUI() {
|
||||
let restoreButton = UIBarButtonItem(title: NSLocalizedString("purchase_pro_restore", comment: ""),
|
||||
style: .plain,
|
||||
target: self,
|
||||
action: #selector(restoreTapped(_:)))
|
||||
navigationItem.rightBarButtonItem = restoreButton
|
||||
|
||||
purchaseButton.isEnabled = false
|
||||
titleLabel.text = NSLocalizedString("network_pro", comment: "")
|
||||
subtitleLabel.text = NSLocalizedString("network_pro_subtitle", comment: "")
|
||||
descriptionTextLabel.text = NSLocalizedString("purchase_pro_description", comment: "")
|
||||
discountTextLabel.text = NSLocalizedString("purchase_pro_discount", comment: "")
|
||||
discountTextLabel.isHidden = true
|
||||
openPrivacyButton.setTitle(NSLocalizedString("network_pro_privacy_disclaimer", comment: ""), for: .normal)
|
||||
openTermsButton.setTitle(NSLocalizedString("network_pro_terms_conditions", comment: ""), for: .normal)
|
||||
payingLabel.text = NSLocalizedString("network_pro_paying", comment: "")
|
||||
purchaseButton.setTitle(NSLocalizedString("network_pro_convert", comment: "").uppercased(), for: .normal)
|
||||
containerView.eqn_applyShadowAndRoundedCorners()
|
||||
}
|
||||
|
||||
private func updateUI() {
|
||||
// search for the Pro product
|
||||
let isDiscountEnabled = discountTimeRemaining > 0
|
||||
let identifier = isDiscountEnabled ? VersioneProProducts.Identifier.ProVersionDiscounted : VersioneProProducts.Identifier.ProVersionFullPrice
|
||||
guard let proProduct = products.first(where: { $0.productIdentifier.lowercased() == identifier.lowercased() }) else {
|
||||
return
|
||||
}
|
||||
|
||||
self.proProduct = proProduct
|
||||
priceFormatter.locale = proProduct.priceLocale
|
||||
|
||||
if isDiscountEnabled {
|
||||
discountTextLabel.isHidden = false
|
||||
let string = NSLocalizedString("purchase_pro_discount", comment: "")
|
||||
discountTextLabel.text = String(format: string, discountTimeRemaining)
|
||||
}
|
||||
|
||||
priceLabel.text = priceFormatter.string(from: proProduct.price)
|
||||
purchaseButton.isEnabled = true
|
||||
|
||||
if UserDefaults.standard.bool(forKey: VersioneProProducts.Identifier.ProVersionFullPrice) ||
|
||||
UserDefaults.standard.bool(forKey: VersioneProProducts.Identifier.ProVersionDiscounted) ||
|
||||
UserDefaults.standard.bool(forKey: VersioneProProducts.Identifier.Subscription10kYearly) ||
|
||||
UserDefaults.standard.bool(forKey: VersioneProProducts.Identifier.Subscription10kYearlyDiscounted) ||
|
||||
UserDefaults.standard.bool(forKey: VersioneProProducts.Identifier.Subscription100kYearly) ||
|
||||
UserDefaults.standard.bool(forKey: VersioneProProducts.Identifier.Subscription100kYearlyDiscounted) {
|
||||
|
||||
purchaseButton.isEnabled = false
|
||||
priceLabel.text = "-"
|
||||
}
|
||||
}
|
||||
|
||||
private func loadProducts() {
|
||||
VersioneProProducts.store.requestProducts { [weak self] success, products in
|
||||
guard let self = self, let products = products, success == true else { return }
|
||||
|
||||
self.products = products
|
||||
self.updateUI()
|
||||
}
|
||||
}
|
||||
|
||||
private func checkDiscountPrice() {
|
||||
EQNPurchaseUtility.offerTimeRemaining { (timeRemaining) in
|
||||
DispatchQueue.main.async {
|
||||
self.discountTimeRemaining = timeRemaining
|
||||
self.updateUI()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
@objc func restoreTapped(_ sender: Any) {
|
||||
restoreTapped = true
|
||||
VersioneProProducts.store.restorePurchases()
|
||||
}
|
||||
|
||||
@IBAction func purchaseTapped(_ sender: UIButton) {
|
||||
guard let product = proProduct else { return }
|
||||
|
||||
VersioneProProducts.store.buyProduct(product)
|
||||
}
|
||||
|
||||
@IBAction func openExternalLinkTapped(_ sender: UIButton) {
|
||||
var linkUrl: URL?
|
||||
if sender == openPrivacyButton {
|
||||
linkUrl = URL(string: "\(EQNWebsiteAddress)/privacy/")
|
||||
} else if sender == openTermsButton {
|
||||
linkUrl = URL(string: "\(EQNWebsiteAddress)/terms-conditions/")
|
||||
|
||||
}
|
||||
|
||||
if let url = linkUrl {
|
||||
let controller = SFSafariViewController(url: url)
|
||||
present(controller, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Notifications
|
||||
|
||||
@objc func handlePurchaseNotification(_ notification: Notification) {
|
||||
guard let productId = notification.object as? String,
|
||||
products.contains(where: { $0.productIdentifier == productId }) else {
|
||||
print("[PurchasePro] Unable to find the product")
|
||||
return
|
||||
}
|
||||
|
||||
if restoreTapped {
|
||||
restoreTapped = false
|
||||
let alert = UIAlertController(title: NSLocalizedString("purchase_pro_restore_alert_title", comment: ""),
|
||||
message: NSLocalizedString("purchase_pro_restore_alert_message", comment: ""),
|
||||
preferredStyle: .alert)
|
||||
alert.addAction(UIAlertAction(title: "ok", style: .default) { [weak self] _ in
|
||||
self?.navigationController?.popViewController(animated: true)
|
||||
})
|
||||
present(alert, animated: true)
|
||||
} else {
|
||||
navigationController?.popViewController(animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Helper
|
||||
|
||||
private var priceFormatter: NumberFormatter = {
|
||||
let formatter = NumberFormatter()
|
||||
formatter.formatterBehavior = .behavior10_4
|
||||
formatter.numberStyle = .currency
|
||||
return formatter
|
||||
}()
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
import UIKit
|
||||
import StoreKit
|
||||
import Shogun
|
||||
|
||||
|
||||
class SubscriptionsViewController: UITableViewController {
|
||||
|
||||
@@ -10,6 +10,52 @@ import UIKit
|
||||
import MapKit
|
||||
|
||||
|
||||
class RealtimeAlertContainerView: UIView {
|
||||
|
||||
lazy var alertView: RealtimeAlertView = {
|
||||
let view = RealtimeAlertView()
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
return view
|
||||
}()
|
||||
|
||||
var animationColor: UIColor = .white
|
||||
|
||||
// MARK: - Init
|
||||
|
||||
convenience init() {
|
||||
self.init(frame: .zero)
|
||||
configureUI()
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func configureUI() {
|
||||
backgroundColor = .white
|
||||
|
||||
addSubview(alertView)
|
||||
|
||||
let margin: CGFloat = 24
|
||||
alertView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: margin).isActive = true
|
||||
alertView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -margin).isActive = true
|
||||
alertView.topAnchor.constraint(equalTo: topAnchor, constant: margin).isActive = true
|
||||
alertView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -margin).isActive = true
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func startBackgroundAnimation() {
|
||||
let animation = CABasicAnimation(keyPath: "backgroundColor")
|
||||
animation.fromValue = UIColor.white.cgColor
|
||||
animation.toValue = animationColor.cgColor
|
||||
animation.duration = 2.0
|
||||
animation.beginTime = CACurrentMediaTime() + 1
|
||||
animation.autoreverses = true
|
||||
animation.repeatCount = .infinity
|
||||
|
||||
layer.add(animation, forKey: "backgroundColor")
|
||||
}
|
||||
}
|
||||
|
||||
class RealtimeAlertView: UIView {
|
||||
|
||||
let titleLabel: UILabel = {
|
||||
@@ -25,20 +71,31 @@ class RealtimeAlertView: UIView {
|
||||
label.translatesAutoresizingMaskIntoConstraints = false
|
||||
label.font = .preferredFont(forTextStyle: .body)
|
||||
label.numberOfLines = 0
|
||||
label.textAlignment = .center
|
||||
return label
|
||||
}()
|
||||
|
||||
let waveTimeLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.translatesAutoresizingMaskIntoConstraints = false
|
||||
label.font = .preferredFont(forTextStyle: .title3)
|
||||
label.font = .preferredFont(forTextStyle: .title2)
|
||||
label.textColor = AppTheme.Colors.red
|
||||
label.text = String(format: NSLocalizedString("alert_wave", comment: ""), 0)
|
||||
label.text = String.localizedStringWithFormat(NSLocalizedString("alert_wave", comment: ""), 0)
|
||||
label.textAlignment = .center
|
||||
label.isHidden = true
|
||||
return label
|
||||
}()
|
||||
|
||||
let intensityLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.translatesAutoresizingMaskIntoConstraints = false
|
||||
label.font = .preferredFont(forTextStyle: .title3)
|
||||
label.textColor = AppTheme.Colors.red
|
||||
label.textAlignment = .center
|
||||
label.numberOfLines = 2
|
||||
return label
|
||||
}()
|
||||
|
||||
lazy var mapView: MKMapView = {
|
||||
let map = MKMapView()
|
||||
map.translatesAutoresizingMaskIntoConstraints = false
|
||||
@@ -70,7 +127,6 @@ class RealtimeAlertView: UIView {
|
||||
addSubview(closeButton)
|
||||
addSubview(titleLabel)
|
||||
addSubview(descriptionLabel)
|
||||
addSubview(waveTimeLabel)
|
||||
addSubview(mapView)
|
||||
|
||||
closeButton.addDefaultConstraint(to: self)
|
||||
@@ -83,13 +139,20 @@ class RealtimeAlertView: UIView {
|
||||
descriptionLabel.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor).isActive = true
|
||||
descriptionLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 20.0).isActive = true
|
||||
|
||||
waveTimeLabel.leadingAnchor.constraint(equalTo: descriptionLabel.leadingAnchor).isActive = true
|
||||
waveTimeLabel.trailingAnchor.constraint(equalTo: descriptionLabel.trailingAnchor).isActive = true
|
||||
waveTimeLabel.topAnchor.constraint(equalTo: descriptionLabel.bottomAnchor, constant: 20.0).isActive = true
|
||||
let stackView = UIStackView(arrangedSubviews: [waveTimeLabel, intensityLabel])
|
||||
stackView.translatesAutoresizingMaskIntoConstraints = false
|
||||
stackView.axis = .vertical
|
||||
stackView.distribution = .equalSpacing
|
||||
stackView.spacing = 20.0
|
||||
addSubview(stackView)
|
||||
|
||||
stackView.leadingAnchor.constraint(equalTo: descriptionLabel.leadingAnchor).isActive = true
|
||||
stackView.trailingAnchor.constraint(equalTo: descriptionLabel.trailingAnchor).isActive = true
|
||||
stackView.topAnchor.constraint(equalTo: descriptionLabel.bottomAnchor, constant: 20.0).isActive = true
|
||||
|
||||
mapView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
|
||||
mapView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
|
||||
mapView.topAnchor.constraint(equalTo: waveTimeLabel.bottomAnchor, constant: 20.0).isActive = true
|
||||
mapView.topAnchor.constraint(equalTo: stackView.bottomAnchor, constant: 20.0).isActive = true
|
||||
mapView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
|
||||
}
|
||||
|
||||
|
||||
+35
-7
@@ -16,9 +16,12 @@ class RealtimeAlertViewController: UIViewController, MKMapViewDelegate {
|
||||
|
||||
// MARK: - Internal
|
||||
|
||||
private let notificationView = RealtimeAlertView()
|
||||
private let containerView = RealtimeAlertContainerView()
|
||||
private var notificationView: RealtimeAlertView {
|
||||
containerView.alertView
|
||||
}
|
||||
/// Alert to display
|
||||
private let realtimeAlert: EQNRealtimeAlert
|
||||
private let realtimeAlert: EQNRealtimePushNotification
|
||||
/// Timer to constantly update countdown label
|
||||
private var countdownTimer: Timer?
|
||||
/// Refresh time for wave animation
|
||||
@@ -32,8 +35,8 @@ class RealtimeAlertViewController: UIViewController, MKMapViewDelegate {
|
||||
// MARK: - Init
|
||||
|
||||
@objc
|
||||
init(alert: EQNRealtimeAlert) {
|
||||
self.realtimeAlert = alert
|
||||
init(notification: EQNRealtimePushNotification) {
|
||||
self.realtimeAlert = notification
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
|
||||
self.waveAnimationCurrentRadius = currentWavePosition()
|
||||
@@ -52,7 +55,7 @@ class RealtimeAlertViewController: UIViewController, MKMapViewDelegate {
|
||||
// MARK: - View Lifecycle
|
||||
|
||||
override func loadView() {
|
||||
view = notificationView
|
||||
view = containerView
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
@@ -65,10 +68,19 @@ class RealtimeAlertViewController: UIViewController, MKMapViewDelegate {
|
||||
startWaveAnimation()
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
containerView.startBackgroundAnimation()
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func configureUI() {
|
||||
notificationView.closeButton.addTarget(self, action: #selector(onTapClose(_:)), for: .touchUpInside)
|
||||
|
||||
// configure color for animation
|
||||
containerView.animationColor = realtimeAlert.relativeIntensityColor
|
||||
}
|
||||
|
||||
private func updateUI() {
|
||||
@@ -78,7 +90,10 @@ class RealtimeAlertViewController: UIViewController, MKMapViewDelegate {
|
||||
let distanceRound = Int(round(realtimeAlert.distanceFromUser() / 1_000))
|
||||
notificationView.descriptionLabel.text = (notificationView.descriptionLabel.text ?? "")
|
||||
+ ".\n"
|
||||
+ String(format: NSLocalizedString("timer_message2_other", comment: ""), distanceRound)
|
||||
+ String(format: NSLocalizedString("official_distance", comment: ""), distanceRound)
|
||||
|
||||
notificationView.intensityLabel.text = realtimeAlert.displayBody
|
||||
notificationView.intensityLabel.textColor = realtimeAlert.relativeIntensityColor
|
||||
|
||||
// center map on the earthquake coordinate
|
||||
let span = MKCoordinateSpan(latitudeDelta: 10.5, longitudeDelta: 10.5)
|
||||
@@ -131,7 +146,8 @@ class RealtimeAlertViewController: UIViewController, MKMapViewDelegate {
|
||||
|
||||
@objc private func countdownTimerFired(_ sender: Timer) {
|
||||
let countdown = realtimeAlert.currentCountdown()
|
||||
notificationView.waveTimeLabel.text = String(format: NSLocalizedString("alert_wave", comment: ""), countdown)
|
||||
notificationView.waveTimeLabel.text = String.localizedStringWithFormat(NSLocalizedString("alert_wave", comment: ""), countdown)
|
||||
notificationView.waveTimeLabel.textColor = waveTimeTextColor(for: countdown)
|
||||
|
||||
if countdown <= 0 {
|
||||
// stop the countdown
|
||||
@@ -165,4 +181,16 @@ class RealtimeAlertViewController: UIViewController, MKMapViewDelegate {
|
||||
let velocity = realtimeAlert.waveSpeed
|
||||
return velocity * waveAnimationRefreshRate
|
||||
}
|
||||
|
||||
/// Returns the text color based on impact countdown
|
||||
private func waveTimeTextColor(for countdown: Int) -> UIColor {
|
||||
switch countdown {
|
||||
case _ where countdown > 15:
|
||||
return UIColor(red: 255.0/255.0, green: 140.0/255.0, blue: 0.0, alpha: 1.0)
|
||||
case _ where countdown > 5:
|
||||
return UIColor(red: 255.0/255.0, green: 100.0/255.0, blue: 0.0, alpha: 1.0)
|
||||
default:
|
||||
return UIColor(red: 255.0/255.0, green: 0.0/255.0, blue: 0.0, alpha: 1.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Shogun
|
||||
|
||||
class SegnalazioniLast24HoursCell: EQNBaseTableViewCell {
|
||||
|
||||
@IBOutlet private weak var headerLabel: UILabel!
|
||||
@IBOutlet private weak var mildReportsLabel: UILabel!
|
||||
@IBOutlet private weak var strongReportsLabel: UILabel!
|
||||
@IBOutlet private weak var veryStrongReportsLabel: UILabel!
|
||||
@IBOutlet private weak var reportsLabel: UILabel!
|
||||
@IBOutlet private weak var reportsDescriptionLabel: UILabel!
|
||||
|
||||
@IBOutlet private weak var twitterButton: UIButton! {
|
||||
didSet {
|
||||
@@ -38,11 +38,11 @@ class SegnalazioniLast24HoursCell: EQNBaseTableViewCell {
|
||||
// MARK: - Private
|
||||
|
||||
private func localizeUI() {
|
||||
headerLabel.text = NSLocalizedString("main_map", comment: "")
|
||||
headerLabel.text = NSLocalizedString("tab_manual", comment: "").capitalized
|
||||
reportsDescriptionLabel.text = NSLocalizedString("main_map", comment: "")
|
||||
mapButton.setTitle(NSLocalizedString("official_button_map", comment: ""), for: .normal)
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
@objc func updateUI(for smartphoneNetwork: EQNReteSmartphone?) {
|
||||
@@ -50,8 +50,15 @@ class SegnalazioniLast24HoursCell: EQNBaseTableViewCell {
|
||||
return
|
||||
}
|
||||
|
||||
self.mildReportsLabel.text = "\(smartphoneNetwork.manualGreen)"
|
||||
self.strongReportsLabel.text = "\(smartphoneNetwork.manualYellow)"
|
||||
self.veryStrongReportsLabel.text = "\(smartphoneNetwork.manualRed)"
|
||||
let reports = smartphoneNetwork.manual
|
||||
self.reportsLabel.text = "\(reports)"
|
||||
|
||||
if reports < 100 {
|
||||
self.reportsLabel.textColor = UIColor(hex6: 0x01b400)
|
||||
} else if reports < 1000 {
|
||||
self.reportsLabel.textColor = UIColor(hex6: 0xe8da00)
|
||||
} else {
|
||||
self.reportsLabel.textColor = UIColor(hex6: 0xff0000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+131
-56
@@ -8,6 +8,7 @@
|
||||
|
||||
import Foundation
|
||||
import MapKit
|
||||
import Shogun
|
||||
|
||||
class SegnalazioniMapViewController: EQNBaseMapViewController {
|
||||
|
||||
@@ -16,11 +17,74 @@ class SegnalazioniMapViewController: EQNBaseMapViewController {
|
||||
let circle: MKCircle
|
||||
}
|
||||
|
||||
override var isCloseButtonVisible: Bool {
|
||||
false
|
||||
}
|
||||
|
||||
private let appPreferences = AppPreferences.shared
|
||||
/// Contains circles and related colors to draw overlays on the map
|
||||
private var mapCircles = [MapCircle]()
|
||||
/// Reports currently showned on the map
|
||||
private var filteredReports = [EQNSegnalazione]()
|
||||
|
||||
// MARK: - UI
|
||||
|
||||
// app icon and name displayed on the screenshot
|
||||
private lazy var watermarkView: UIView = {
|
||||
let view = UIView()
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.isHidden = true
|
||||
|
||||
let logo = UIImageView(image: .init(named: "eq_icon_transparent"))
|
||||
logo.translatesAutoresizingMaskIntoConstraints = false
|
||||
logo.contentMode = .scaleAspectFit
|
||||
view.addSubview(logo)
|
||||
logo.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
|
||||
logo.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
|
||||
logo.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
|
||||
logo.widthAnchor.constraint(equalTo: logo.heightAnchor).isActive = true
|
||||
|
||||
let title = UILabel()
|
||||
title.translatesAutoresizingMaskIntoConstraints = false
|
||||
title.text = NSLocalizedString("app_name", comment: "") + " App"
|
||||
title.textColor = AppTheme.Colors.red
|
||||
title.font = .preferredFont(forTextStyle: .title3, weight: .semibold)
|
||||
view.addSubview(title)
|
||||
title.leadingAnchor.constraint(equalTo: logo.trailingAnchor, constant: 10.0).isActive = true
|
||||
title.centerYAnchor.constraint(equalTo: logo.centerYAnchor).isActive = true
|
||||
title.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
|
||||
|
||||
return view
|
||||
}()
|
||||
|
||||
private lazy var magnitudeLegendView: UIView = {
|
||||
let view = UIView()
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
let stackView = UIStackView()
|
||||
stackView.translatesAutoresizingMaskIntoConstraints = false
|
||||
stackView.axis = .horizontal
|
||||
stackView.distribution = .fillEqually
|
||||
[20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120].forEach { magnitude in
|
||||
let label = UILabel()
|
||||
label.translatesAutoresizingMaskIntoConstraints = false
|
||||
label.text = (magnitude / 10).romanNumber()
|
||||
label.backgroundColor = UIColor(named: "Mercalli \(magnitude)")
|
||||
label.textAlignment = .center
|
||||
label.font = .preferredFont(forTextStyle: .callout)
|
||||
label.textColor = magnitude >= 100 ? .white : .black
|
||||
stackView.addArrangedSubview(label)
|
||||
}
|
||||
|
||||
view.addSubview(stackView)
|
||||
stackView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
|
||||
stackView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
|
||||
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
|
||||
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
|
||||
|
||||
return view
|
||||
}()
|
||||
|
||||
// MARK: - View Lifecycle
|
||||
|
||||
override func viewDidLoad() {
|
||||
@@ -29,8 +93,31 @@ class SegnalazioniMapViewController: EQNBaseMapViewController {
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
override func extraUI() {
|
||||
view.addSubview(magnitudeLegendView)
|
||||
view.addSubview(watermarkView)
|
||||
|
||||
magnitudeLegendView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
|
||||
magnitudeLegendView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
|
||||
magnitudeLegendView.heightAnchor.constraint(equalToConstant: 25.0).isActive = true
|
||||
magnitudeLegendView.topAnchor.constraint(equalTo: mapView.topAnchor).isActive = true
|
||||
|
||||
watermarkView.topAnchor.constraint(equalTo: mapView.topAnchor, constant: 10.0).isActive = true
|
||||
watermarkView.leadingAnchor.constraint(equalTo: mapView.leadingAnchor, constant: 10.0).isActive = true
|
||||
watermarkView.heightAnchor.constraint(equalToConstant: 40.0).isActive = true
|
||||
}
|
||||
|
||||
override func configureUI() {
|
||||
navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(onTapCloseButton(_:)))
|
||||
navigationItem.rightBarButtonItems = [
|
||||
UIBarButtonItem(image: UIImage(named: "navbar-icon-screenshot"), style: .plain, target: self, action: #selector(onTapScreenshotButton(_:))),
|
||||
UIBarButtonItem(image: UIImage(named: "navbar-icon-pin-arrow"), style: .plain, target: self, action: #selector(onTapMapDetailStyleButton(_:)))
|
||||
]
|
||||
}
|
||||
|
||||
override func registerMapAnnotationViews() {
|
||||
mapView.register(EQNCustomAnnotationView.self, forAnnotationViewWithReuseIdentifier: EQNCustomAnnotationView.SingleLineIdentifier)
|
||||
mapView.register(EQNCustomAnnotationView.self, forAnnotationViewWithReuseIdentifier: EQNCustomAnnotationView.SmallIdentifier)
|
||||
}
|
||||
|
||||
override func loadDataSource() {
|
||||
@@ -96,27 +183,41 @@ class SegnalazioniMapViewController: EQNBaseMapViewController {
|
||||
}
|
||||
}
|
||||
|
||||
override func didTapAnnotation(_ annotation: MKAnnotation) {
|
||||
guard let annotation = annotation as? EQNMapAnnotationUserReport, let report = annotation.report else {
|
||||
return
|
||||
// MARK: - Actions
|
||||
|
||||
@objc private func onTapCloseButton(_ sender: Any) {
|
||||
dismiss(animated: true)
|
||||
}
|
||||
|
||||
@objc private func onTapMapDetailStyleButton(_ sender: Any) {
|
||||
appPreferences.userReportExpandedView.toggle()
|
||||
loadDataSource()
|
||||
}
|
||||
|
||||
@objc private func onTapScreenshotButton(_ sender: Any) {
|
||||
let snapshot = createSnapshot()
|
||||
|
||||
let controller = UIActivityViewController(activityItems: [snapshot], applicationActivities: [])
|
||||
present(controller, animated: true)
|
||||
}
|
||||
|
||||
public func createSnapshot() -> UIImage {
|
||||
// mostriamo il watermark e nascondiamo la legenda
|
||||
watermarkView.isHidden = false
|
||||
magnitudeLegendView.isHidden = true
|
||||
|
||||
// riduciamo la porzione da salvare alla sola mappa (eliminiamo i filtri)
|
||||
let size = CGSize(width: view.bounds.width, height: mapView.bounds.maxY)
|
||||
let renderer = UIGraphicsImageRenderer(size: size)
|
||||
let image = renderer.image { ctx in
|
||||
view.drawHierarchy(in: view.bounds, afterScreenUpdates: true)
|
||||
}
|
||||
|
||||
let difference = Int(Date().timeIntervalSince(report.date) / 60.0)
|
||||
let title = EQNUtility.formattedString(forTimeDifference: difference) + " - \(report.intensity.description)"
|
||||
// torniamo allo stato originale
|
||||
watermarkView.isHidden = true
|
||||
magnitudeLegendView.isHidden = false
|
||||
|
||||
var message = ""
|
||||
+ "🏢 " + report.address
|
||||
+ "\n⏱ " + EQNUtility.formattedDate(from: report.date) + " \(NSLocalizedString("share_yourtime", comment: ""))"
|
||||
if !report.message.isEmpty {
|
||||
message += "\n💬 \(report.message)"
|
||||
}
|
||||
|
||||
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
|
||||
alert.addAction(UIAlertAction(title: NSLocalizedString("main_share", comment: ""), style: .default, handler: { [unowned self] _ in
|
||||
self.openShareActivity(for: report)
|
||||
}))
|
||||
alert.addAction(UIAlertAction(title: NSLocalizedString("official_close", comment: ""), style: .cancel, handler: nil))
|
||||
present(alert, animated: true, completion: nil)
|
||||
return image
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
@@ -125,7 +226,7 @@ class SegnalazioniMapViewController: EQNBaseMapViewController {
|
||||
let vector_latitude = reports.map { $0.coordinate.coordinate.latitude }
|
||||
let vector_longitude = reports.map { $0.coordinate.coordinate.longitude }
|
||||
let vector_date = reports.map { $0.date }
|
||||
let vector_state = reports.map { $0.intensity.rawValue }
|
||||
let vector_state = reports.map { $0.intensity }
|
||||
|
||||
let minutes: TimeInterval = filter.minutes
|
||||
|
||||
@@ -200,21 +301,7 @@ class SegnalazioniMapViewController: EQNBaseMapViewController {
|
||||
}
|
||||
|
||||
let circles = Array(0..<cluster_code).map { (i) -> MapCircle in
|
||||
var value_distance = max_distance[i] / 20.0
|
||||
if value_distance > 1.0 {
|
||||
value_distance = 1.0
|
||||
}
|
||||
let value_intensity = (cluster_intensity[i]-1.0) / 2.0
|
||||
let value_reference = max(value_distance, value_intensity)
|
||||
|
||||
let color: UIColor
|
||||
if value_reference <= 0.5 {
|
||||
let red = round(value_reference * 510)
|
||||
color = UIColor(red: CGFloat(red / 255.0), green: 230.0/255.0, blue: 0.0, alpha: 1.0)
|
||||
} else {
|
||||
let green = round(230 - (value_reference - 0.5) * 460)
|
||||
color = UIColor(red: 255.0, green: CGFloat(green / 255.0), blue: 0.0, alpha: 1.0)
|
||||
}
|
||||
let color: UIColor = AppTheme.Colors.darkGray
|
||||
|
||||
let centre = CLLocation(latitude: lat_centre[i], longitude: lon_centre[i])
|
||||
let farest = CLLocation(latitude: lat_farest[i], longitude: lon_farest[i])
|
||||
@@ -244,25 +331,6 @@ class SegnalazioniMapViewController: EQNBaseMapViewController {
|
||||
Date().timeIntervalSince(date) / 60.0
|
||||
}
|
||||
|
||||
private func openShareActivity(for report: EQNSegnalazione) {
|
||||
// create message to share
|
||||
let intensity = report.intensity.description
|
||||
let difference = Int(Date().timeIntervalSince(report.date) / 60.0)
|
||||
let time = EQNUtility.formattedString(forTimeDifference: difference)
|
||||
|
||||
let message = [
|
||||
NSLocalizedString("share_hashtag", comment: ""),
|
||||
intensity,
|
||||
NSLocalizedString("share_felt", comment: ""),
|
||||
report.address,
|
||||
time + ".",
|
||||
NSLocalizedString("share_notified", comment: "")
|
||||
].joined(separator: " ")
|
||||
|
||||
let controller = UIActivityViewController(activityItems: [message], applicationActivities: [])
|
||||
present(controller, animated: true)
|
||||
}
|
||||
|
||||
// MARK: - Map
|
||||
|
||||
override func setupAnnotationView(for annotation: MKAnnotation, on mapView: MKMapView) -> MKAnnotationView? {
|
||||
@@ -270,10 +338,17 @@ class SegnalazioniMapViewController: EQNBaseMapViewController {
|
||||
return nil
|
||||
}
|
||||
|
||||
let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: EQNCustomAnnotationView.SingleLineIdentifier, for: annotation) as! EQNCustomAnnotationView
|
||||
let identifier = appPreferences.userReportExpandedView ? EQNCustomAnnotationView.SingleLineIdentifier : EQNCustomAnnotationView.SmallIdentifier
|
||||
let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier, for: annotation) as! EQNCustomAnnotationView
|
||||
|
||||
annotationView.image = annotation.image
|
||||
annotationView.title = annotation.title
|
||||
let size = appPreferences.userReportExpandedView ? EQNCustomAnnotationView.SingleLineImageHeight : EQNCustomAnnotationView.SmallViewImageHeight
|
||||
annotationView.image = annotation.image(with: size)
|
||||
annotationView.title = annotation.timeDifference
|
||||
annotationView.canShowCallout = true
|
||||
// Psizioniamo più in alto le segnalazioni con intensità maggiore.
|
||||
// Valori maggiori di anchorPointZ mettono la view più in basso,
|
||||
// quindi invertiamo il valore dell'intensità
|
||||
annotationView.layer.anchorPointZ = (1000 - CGFloat(annotation.report?.intensity ?? 0))
|
||||
|
||||
return annotationView
|
||||
}
|
||||
|
||||
@@ -10,15 +10,23 @@ import UIKit
|
||||
|
||||
class SegnalazioniSendReportCell: EQNBaseTableViewCell {
|
||||
|
||||
@IBOutlet private weak var headerLabel: UILabel!
|
||||
@IBOutlet private weak var descriptionLabel: UILabel!
|
||||
@IBOutlet private weak var mildLabel: UILabel!
|
||||
@IBOutlet private weak var mildButton: UIButton!
|
||||
@IBOutlet private weak var strongLabel: UILabel!
|
||||
@IBOutlet private weak var strongButton: UIButton!
|
||||
@IBOutlet private weak var veryStrongLabel: UILabel!
|
||||
@IBOutlet private weak var veryStrongButton: UIButton!
|
||||
@objc var onTapReport: (_ magnitude: Int) -> Void = { _ in }
|
||||
|
||||
// MARK: - UI
|
||||
|
||||
@IBOutlet private weak var headerLabel: UILabel!
|
||||
@IBOutlet private weak var reportMercalli2: UILabel!
|
||||
@IBOutlet private weak var reportMercalli3: UILabel!
|
||||
@IBOutlet private weak var reportMercalli4: UILabel!
|
||||
@IBOutlet private weak var reportMercalli5: UILabel!
|
||||
@IBOutlet private weak var reportMercalli6: UILabel!
|
||||
@IBOutlet private weak var reportMercalli7: UILabel!
|
||||
@IBOutlet private weak var reportMercalli8: UILabel!
|
||||
@IBOutlet private weak var reportMercalli9: UILabel!
|
||||
@IBOutlet private weak var reportMercalli10: UILabel!
|
||||
@IBOutlet private weak var reportMercalli11: UILabel!
|
||||
@IBOutlet private weak var reportMercalli12: UILabel!
|
||||
|
||||
// MARK: - View Lifecycle
|
||||
|
||||
override func awakeFromNib() {
|
||||
@@ -31,21 +39,23 @@ class SegnalazioniSendReportCell: EQNBaseTableViewCell {
|
||||
|
||||
private func localizeUI() {
|
||||
headerLabel.text = NSLocalizedString("main_feel", comment: "")
|
||||
descriptionLabel.text = NSLocalizedString("manual_usebutton", comment: "")
|
||||
mildLabel.text = NSLocalizedString("manual_mild", comment: "")
|
||||
strongLabel.text = NSLocalizedString("manual_strong", comment: "")
|
||||
veryStrongLabel.text = NSLocalizedString("manual_verystrong", comment: "")
|
||||
reportMercalli2.text = NSLocalizedString("mercalli_II", comment: "")
|
||||
reportMercalli3.text = NSLocalizedString("mercalli_III", comment: "")
|
||||
reportMercalli4.text = NSLocalizedString("mercalli_IV", comment: "")
|
||||
reportMercalli5.text = NSLocalizedString("mercalli_V", comment: "")
|
||||
reportMercalli6.text = NSLocalizedString("mercalli_VI", comment: "")
|
||||
reportMercalli7.text = NSLocalizedString("mercalli_VII", comment: "")
|
||||
reportMercalli8.text = NSLocalizedString("mercalli_VIII", comment: "")
|
||||
reportMercalli9.text = NSLocalizedString("mercalli_IX", comment: "")
|
||||
reportMercalli10.text = NSLocalizedString("mercalli_X", comment: "")
|
||||
reportMercalli11.text = NSLocalizedString("mercalli_XI", comment: "")
|
||||
reportMercalli12.text = NSLocalizedString("mercalli_XII", comment: "")
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
let labels: [UILabel] = [mildLabel, strongLabel, veryStrongLabel]
|
||||
labels.forEach { (label) in
|
||||
label.layer.borderWidth = AppTheme.shared.buttonBorderWidth
|
||||
label.layer.borderColor = AppTheme.shared.buttonBorderColor.cgColor
|
||||
label.layer.cornerRadius = AppTheme.shared.buttonCornerRadius
|
||||
label.clipsToBounds = true
|
||||
}
|
||||
// MARK: - Actions
|
||||
|
||||
@IBAction private func onTapReportButton(_ sender: UIButton) {
|
||||
let magnitude = sender.tag
|
||||
onTapReport(magnitude)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,11 +46,11 @@
|
||||
{
|
||||
[super refreshUI];
|
||||
|
||||
if ([self.userDefoult objectForKey:DATA_MESSAGE_EQN]){
|
||||
NSDate *dateMessage = [self.userDefoult objectForKey:DATA_MESSAGE_EQN];
|
||||
if ([EQNUtility getDifferenceMinute:dateMessage] >= EQNSendReportDelayBetweenComments){
|
||||
[self.userDefoult removeObjectForKey:DATA_MESSAGE_EQN];
|
||||
[self.userDefoult removeObjectForKey:CODE_MESSAGE_EQN];
|
||||
if ([self.userDefoult objectForKey:NSUserDefaults.UserReportMessage]){
|
||||
NSDate *dateMessage = [self.userDefoult objectForKey:NSUserDefaults.UserReportMessage];
|
||||
if (![dateMessage isBeforeInterval:EQNSendReportDelayBetweenComments]) {
|
||||
[self.userDefoult removeObjectForKey:NSUserDefaults.UserReportMessage];
|
||||
[self.userDefoult removeObjectForKey:NSUserDefaults.UserReportCodeStatus];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,14 +70,6 @@
|
||||
return 2;
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
switch (indexPath.row) {
|
||||
case 0: return 140;
|
||||
default: return UITableViewAutomaticDimension;
|
||||
}
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
if (indexPath.row == 0) {
|
||||
@@ -87,7 +79,10 @@
|
||||
return cell;
|
||||
}
|
||||
|
||||
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ReportEarthquakeCell" forIndexPath:indexPath];
|
||||
SegnalazioniSendReportCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ReportEarthquakeCell" forIndexPath:indexPath];
|
||||
cell.onTapReport = ^(NSInteger magnitude) {
|
||||
[self sendReportTappedWithMagnitude:magnitude];
|
||||
};
|
||||
return cell;
|
||||
}
|
||||
|
||||
@@ -96,7 +91,8 @@
|
||||
- (IBAction)openMapTapped:(id)sender
|
||||
{
|
||||
SegnalazioniMapViewController *controller = [[SegnalazioniMapViewController alloc] init];
|
||||
[self presentViewController:controller animated:YES completion:nil];
|
||||
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:controller];
|
||||
[self presentViewController:navController animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (IBAction)openTwitterTapped:(id)sender
|
||||
@@ -117,12 +113,12 @@
|
||||
[[EQNManager defaultManager] sincronizza];
|
||||
}
|
||||
|
||||
- (IBAction)sendReportTapped:(UIButton *)sender
|
||||
- (void)sendReportTappedWithMagnitude:(NSInteger)magnitude
|
||||
{
|
||||
// check to avoid multiple consecutive reports
|
||||
if ([self.userDefoult objectForKey:CODE_MESSAGE_EQN]) {
|
||||
NSDate *dateMessage = [self.userDefoult objectForKey:DATA_MESSAGE_EQN];
|
||||
if ([EQNUtility getDifferenceMinute:dateMessage] <= EQNSendReportDelayBetweenMessages){
|
||||
if ([self.userDefoult objectForKey:NSUserDefaults.UserReportCodeStatus]) {
|
||||
NSDate *dateMessage = [self.userDefoult objectForKey:NSUserDefaults.UserReportMessage];
|
||||
if (![dateMessage isBeforeInterval:EQNSendReportDelayBetweenMessages]) {
|
||||
NSString *message = NSLocalizedString(@"manual_wait", @"");
|
||||
[self showErrorAlertWithMessage:message];
|
||||
[self performSelectorOnMainThread:@selector(sincronizzazione) withObject:nil waitUntilDone:YES];
|
||||
@@ -133,9 +129,9 @@
|
||||
// ask for user confirmation
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"attention", @"") message:NSLocalizedString(@"manual_sure", nil) preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"manual_yes", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
|
||||
[self executeSendReportWithMagnitude:sender.tag];
|
||||
[self executeSendReportWithMagnitude:magnitude];
|
||||
}]];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"options_cancel", nil) style:UIAlertActionStyleCancel handler:nil]];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"status_cancel", nil) style:UIAlertActionStyleCancel handler:nil]];
|
||||
[self presentViewController:alert animated:YES completion:nil];
|
||||
}
|
||||
|
||||
@@ -158,16 +154,14 @@
|
||||
|
||||
[[ServerRequest defaultServerConnectionSingleton] inviaInformazioniAlServerWithURL:url richiesta:EQNTipoChiamataSegnalazioneTerremoto success:^(id result) {
|
||||
|
||||
[self.userDefoult setObject:result forKey:CODE_MESSAGE_EQN];
|
||||
[self.userDefoult setObject:[NSDate date] forKey:DATA_MESSAGE_EQN];
|
||||
[self.userDefoult setObject:result forKey:NSUserDefaults.UserReportCodeStatus];
|
||||
[self.userDefoult setObject:[NSDate date] forKey:NSUserDefaults.UserReportMessage];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"report", @"")
|
||||
message:NSLocalizedString(@"manual_ok", @"")
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"ok", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
|
||||
[self sendComment];
|
||||
}]];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"ok", nil) style:UIAlertActionStyleDefault handler:nil]];
|
||||
[self presentViewController:alert animated:YES completion:nil];
|
||||
});
|
||||
} failure:^(NSError * error) {
|
||||
@@ -177,42 +171,6 @@
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)sendComment
|
||||
{
|
||||
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"manual_sendmessage_button" , @"")
|
||||
message:NSLocalizedString(@"manual_sendmessage", @"")
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
|
||||
textField.clearButtonMode = UITextFieldViewModeWhileEditing;
|
||||
}];
|
||||
|
||||
[alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"ok" ,@"") style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
|
||||
UITextField * messaggio = alertController.textFields.firstObject;
|
||||
|
||||
NSURL *url = [EQNGeneratoreURLServer urlInvioCommentoTerremoto:messaggio.text codeMessage:[self.userDefoult objectForKey:CODE_MESSAGE_EQN]];
|
||||
[[ServerRequest defaultServerConnectionSingleton] inviaInformazioniAlServerWithURL:url richiesta:EQNTipoChiamataCommentoTerremoto success:^(id result) {
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"manual_sendmessage_button" , @"")
|
||||
message:NSLocalizedString(@"manual_message_received", @"")
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"ok", nil) style:UIAlertActionStyleCancel handler:nil]];
|
||||
[self presentViewController:alert animated:YES completion:nil];
|
||||
});
|
||||
|
||||
} failure:^(NSError * error) {
|
||||
[self showErrorAlertWithMessage:error.localizedDescription];
|
||||
}];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self refreshUI];
|
||||
});
|
||||
}]];
|
||||
|
||||
[alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"options_cancel", @"") style:UIAlertActionStyleCancel handler:nil]];
|
||||
[self presentViewController:alertController animated:YES completion:nil];
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
- (void)showErrorAlertWithMessage:(NSString *)errorMessage
|
||||
|
||||
+4
-71
@@ -9,12 +9,12 @@
|
||||
import UIKit
|
||||
import MapKit
|
||||
import CoreLocation
|
||||
import Shogun
|
||||
|
||||
protocol SeismicNetworkTableViewCellDelegate: AnyObject {
|
||||
func seismicNetworkCellDidTapShare(_ cell: SeismicNetworkTableViewCell)
|
||||
func seismicNetworkCellDidTapMap(_ cell: SeismicNetworkTableViewCell)
|
||||
func seismicNetworkCellDidTapMapDetail(_ cell: SeismicNetworkTableViewCell)
|
||||
func seismicNetworkCellDidTapWeather(_ cell: SeismicNetworkTableViewCell, hasValidWeatherData: Bool)
|
||||
func seismicNetworkCellDidTapCalendar(_ cell: SeismicNetworkTableViewCell)
|
||||
func seismicNetworkCellDidTapSettings(_ cell: SeismicNetworkTableViewCell)
|
||||
func seismicNetworkCellDidTapClose(_ cell: SeismicNetworkTableViewCell)
|
||||
@@ -44,8 +44,6 @@ class SeismicNetworkTableViewCell: UITableViewCell {
|
||||
case normal
|
||||
/// Cell with map visible
|
||||
case mapExpanded
|
||||
/// Cell with weather info visible
|
||||
case weatherExpanded
|
||||
}
|
||||
|
||||
/// Delegate
|
||||
@@ -55,7 +53,7 @@ class SeismicNetworkTableViewCell: UITableViewCell {
|
||||
|
||||
private static let DefaultVerticalSpacing: CGFloat = 6.0
|
||||
private static let DefaultBodyFont = UIFont.preferredFont(forTextStyle: .body)
|
||||
private static let DefaultBodyFontLight = UIFont.preferredFont(for: .body, weight: .light)
|
||||
private static let DefaultBodyFontLight = UIFont.preferredFont(forTextStyle: .body, weight: .light)
|
||||
|
||||
/// Seismic to show
|
||||
private var seismic: EQNSisma?
|
||||
@@ -89,7 +87,7 @@ class SeismicNetworkTableViewCell: UITableViewCell {
|
||||
private lazy var placeLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.translatesAutoresizingMaskIntoConstraints = false
|
||||
label.font = UIFont.preferredFont(for: .title2, weight: .semibold)
|
||||
label.font = UIFont.preferredFont(forTextStyle: .title2, weight: .semibold)
|
||||
label.numberOfLines = 3
|
||||
return label
|
||||
}()
|
||||
@@ -173,20 +171,6 @@ class SeismicNetworkTableViewCell: UITableViewCell {
|
||||
return mapView
|
||||
}()
|
||||
|
||||
private lazy var weatherImageView: UIImageView = {
|
||||
let imageView = UIImageView(frame: .zero)
|
||||
imageView.translatesAutoresizingMaskIntoConstraints = false
|
||||
return imageView
|
||||
}()
|
||||
|
||||
private lazy var weatherInfoLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.translatesAutoresizingMaskIntoConstraints = false
|
||||
label.numberOfLines = 0
|
||||
label.font = Self.DefaultBodyFontLight
|
||||
return label
|
||||
}()
|
||||
|
||||
// MARK: - Init
|
||||
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
@@ -203,7 +187,6 @@ class SeismicNetworkTableViewCell: UITableViewCell {
|
||||
|
||||
private func setupUI() {
|
||||
selectionStyle = .default
|
||||
translatesAutoresizingMaskIntoConstraints = false
|
||||
backgroundColor = .clear
|
||||
|
||||
// container view
|
||||
@@ -345,8 +328,6 @@ class SeismicNetworkTableViewCell: UITableViewCell {
|
||||
|
||||
let buttonMap = createRoundedButton(title: "🗺", action: #selector(mapTapped(_:)))
|
||||
stackViewButtons.addArrangedSubview(buttonMap)
|
||||
let buttonWeather = createRoundedButton(title: "🌤", action: #selector(weatherTapped(_:)))
|
||||
stackViewButtons.addArrangedSubview(buttonWeather)
|
||||
let buttonCalendar = createRoundedButton(title: "📆", action: #selector(calendarTapped(_:)))
|
||||
stackViewButtons.addArrangedSubview(buttonCalendar)
|
||||
let buttonSettings = createRoundedButton(title: "🔧", action: #selector(settingsTapped(_:)))
|
||||
@@ -369,32 +350,9 @@ class SeismicNetworkTableViewCell: UITableViewCell {
|
||||
mapView.trailingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.trailingAnchor).isActive = true
|
||||
|
||||
previousView = mapView
|
||||
} else if displayType == .weatherExpanded {
|
||||
let weatherTitleLabel = UILabel()
|
||||
weatherTitleLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
weatherTitleLabel.text = NSLocalizedString("weather_weather", comment: "")
|
||||
weatherTitleLabel.font = UIFont.preferredFont(forTextStyle: .headline)
|
||||
|
||||
containerView.addSubview(weatherTitleLabel)
|
||||
weatherTitleLabel.topAnchor.constraint(equalTo: previousView.bottomAnchor, constant: Self.DefaultVerticalSpacing).isActive = true
|
||||
weatherTitleLabel.leadingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.leadingAnchor).isActive = true
|
||||
weatherTitleLabel.trailingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.trailingAnchor).isActive = true
|
||||
|
||||
containerView.addSubview(weatherInfoLabel)
|
||||
containerView.addSubview(weatherImageView)
|
||||
weatherImageView.heightAnchor.constraint(equalToConstant: 60.0).isActive = true
|
||||
weatherImageView.widthAnchor.constraint(equalTo: weatherImageView.heightAnchor).isActive = true
|
||||
weatherImageView.leadingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.leadingAnchor).isActive = true
|
||||
weatherImageView.trailingAnchor.constraint(equalTo: weatherInfoLabel.leadingAnchor, constant: -8.0).isActive = true
|
||||
weatherImageView.centerYAnchor.constraint(equalTo: weatherInfoLabel.centerYAnchor).isActive = true
|
||||
|
||||
weatherInfoLabel.topAnchor.constraint(equalTo: weatherTitleLabel.bottomAnchor, constant: 4.0).isActive = true
|
||||
weatherInfoLabel.trailingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.trailingAnchor).isActive = true
|
||||
|
||||
previousView = weatherInfoLabel
|
||||
}
|
||||
|
||||
if (displayType == .mapExpanded || displayType == .weatherExpanded) {
|
||||
if (displayType == .mapExpanded) {
|
||||
let buttonClose = createRoundedButton(title: NSLocalizedString("official_close", comment: "").uppercased(), action: #selector(closeTapped(_:)))
|
||||
|
||||
containerView.addSubview(buttonClose)
|
||||
@@ -463,14 +421,6 @@ class SeismicNetworkTableViewCell: UITableViewCell {
|
||||
|
||||
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(mapDetailTapped(_:)))
|
||||
mapView.addGestureRecognizer(tapRecognizer)
|
||||
} else if displayType == .weatherExpanded {
|
||||
weatherInfoLabel.text = ""
|
||||
+ String(format: NSLocalizedString("weather_temperature", comment: ""), seismic.weatherTemperature.doubleValue - EQNMathKelvin) + "\n"
|
||||
+ String(format: NSLocalizedString("weather_pressure", comment: ""), seismic.weatherPressure) + "\n"
|
||||
+ String(format: NSLocalizedString("weather_windspeed", comment: ""), seismic.weatherWindSpeed) + "\n"
|
||||
+ String(format: NSLocalizedString("weather_humidity", comment: ""), seismic.weatherHumidity) + "\n"
|
||||
+ String(format: NSLocalizedString("weather_clouds", comment: ""), seismic.weatherCloud)
|
||||
weatherImageView.image = UIImage(named: "weather_\(seismic.weatherIcon).png")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -505,16 +455,6 @@ class SeismicNetworkTableViewCell: UITableViewCell {
|
||||
updateUI()
|
||||
}
|
||||
|
||||
/// Creates a snapshot of the current cell
|
||||
/// - Returns: Image with the snapshot of the cell
|
||||
public func createSnapshot() -> UIImage {
|
||||
let renderer = UIGraphicsImageRenderer(size: contentView.bounds.size)
|
||||
let image = renderer.image { ctx in
|
||||
contentView.drawHierarchy(in: contentView.bounds, afterScreenUpdates: true)
|
||||
}
|
||||
return image
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
@objc func shareTapped(_ sender: UIButton) {
|
||||
@@ -526,13 +466,6 @@ class SeismicNetworkTableViewCell: UITableViewCell {
|
||||
delegate?.seismicNetworkCellDidTapMap(self)
|
||||
}
|
||||
}
|
||||
|
||||
@objc func weatherTapped(_ sender: UIButton) {
|
||||
if displayType != .weatherExpanded {
|
||||
let validData = seismic?.weatherCode != nil
|
||||
delegate?.seismicNetworkCellDidTapWeather(self, hasValidWeatherData: validData)
|
||||
}
|
||||
}
|
||||
|
||||
@objc func calendarTapped(_ sender: UIButton) {
|
||||
delegate?.seismicNetworkCellDidTapCalendar(self)
|
||||
|
||||
+2
-1
@@ -7,6 +7,7 @@
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Shogun
|
||||
|
||||
protocol SeismicFiltersViewControllerDelegate: AnyObject {
|
||||
func seismicFiltersControllerDidUpdateFilters(_ controller: SeismicFiltersViewController)
|
||||
@@ -37,7 +38,7 @@ class SeismicFiltersViewController: UIViewController, UITableViewDelegate, UITab
|
||||
|
||||
private var settings = [
|
||||
SettingItem(type: .slider, title: NSLocalizedString("filter_magnitude", comment: "")),
|
||||
SettingItem(type: .slider, title: NSLocalizedString("Distanza massima", comment: "")),
|
||||
SettingItem(type: .slider, title: NSLocalizedString("filter_distance", comment: "")),
|
||||
SettingItem(type: .slider, title: NSLocalizedString("filter_timeframe", comment: "")),
|
||||
SettingItem(type: .enable, title: NSLocalizedString("filter_strong", comment: "")),
|
||||
SettingItem(type: .slider, title: NSLocalizedString("options_strong_magnitude", comment: "")),
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ struct SeismicNetworkViewModel {
|
||||
|
||||
// distance
|
||||
let distanceRounded = Int(round(seismic.userDistance))
|
||||
self.distance = String(format: NSLocalizedString("timer_message2_other", comment: ""), distanceRounded)
|
||||
self.distance = String(format: NSLocalizedString("official_distance", comment: ""), distanceRounded)
|
||||
let coordinateText = EQNUtility.coordinateString(coordinate: seismic.coordinate.coordinate)
|
||||
self.coordinate = "\(coordinateText)"
|
||||
|
||||
|
||||
+4
-1
@@ -8,6 +8,7 @@
|
||||
|
||||
import UIKit
|
||||
import MapKit
|
||||
import Shogun
|
||||
|
||||
protocol SeismicNetworksMapDetailViewControllerDelegate: AnyObject {
|
||||
func seismicNetworksMapDetailControllerWillUpdateData(_ controller: SeismicNetworksMapDetailViewController, needsDataUpdate: Bool)
|
||||
@@ -107,6 +108,8 @@ class SeismicNetworksMapDetailViewController: EQNBaseMapViewController {
|
||||
}
|
||||
|
||||
override func didTapAnnotation(_ annotation: MKAnnotation) {
|
||||
mapView.deselectAnnotation(annotation, animated: true)
|
||||
|
||||
guard let annotation = annotation as? EQNMapAnnotationSeismic else { return }
|
||||
|
||||
let viewModel = SeismicNetworkViewModel(seismic: annotation.seismic)
|
||||
@@ -162,7 +165,7 @@ class SeismicNetworksMapDetailViewController: EQNBaseMapViewController {
|
||||
// MARK: - Actions
|
||||
|
||||
@objc override func filtersTapped(_ sender: UIGestureRecognizer) {
|
||||
let controller = SeismicFiltersViewController.makeController()
|
||||
let controller = SeismicFiltersViewController.makeViewController()
|
||||
controller.delegate = self
|
||||
controller.modalPresentationStyle = .overCurrentContext
|
||||
controller.modalTransitionStyle = .crossDissolve
|
||||
|
||||
+4
-29
@@ -9,6 +9,7 @@
|
||||
import UIKit
|
||||
import EventKitUI
|
||||
import DZNEmptyDataSet
|
||||
import Shogun
|
||||
|
||||
class SeismicNetworksViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
|
||||
|
||||
@@ -43,8 +44,6 @@ class SeismicNetworksViewController: UIViewController, UITableViewDelegate, UITa
|
||||
private var informations = [SeismicNetworkTableViewCell.InformationType]()
|
||||
/// Index path of row with map expanded
|
||||
private var openMapIndexPath: IndexPath?
|
||||
/// Index path of row with weather expanded
|
||||
private var openWeatherIndexPath: IndexPath?
|
||||
|
||||
// MARK: - View Lifecycle
|
||||
|
||||
@@ -116,7 +115,6 @@ class SeismicNetworksViewController: UIViewController, UITableViewDelegate, UITa
|
||||
|
||||
@objc func didReceiveDownloadCompleteNotification(_ sender: Notification) {
|
||||
self.openMapIndexPath = nil
|
||||
self.openWeatherIndexPath = nil
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.refreshUI()
|
||||
@@ -218,8 +216,6 @@ class SeismicNetworksViewController: UIViewController, UITableViewDelegate, UITa
|
||||
var type = SeismicNetworkTableViewCell.DisplayType.normal
|
||||
if openMapIndexPath == indexPath {
|
||||
type = .mapExpanded
|
||||
} else if openWeatherIndexPath == indexPath {
|
||||
type = .weatherExpanded
|
||||
}
|
||||
|
||||
cell.configure(with: seismic, type: type, informations: informations)
|
||||
@@ -317,7 +313,7 @@ extension SeismicNetworksViewController: SeismicNetworkTableViewCellDelegate {
|
||||
guard let index = tableView?.indexPath(for: cell), case let .seismic(seismic) = rows[index.row] else { return }
|
||||
|
||||
// create a snapshot of the cell and share with default share sheet
|
||||
let snapshot = cell.createSnapshot()
|
||||
let snapshot = cell.contentView.createSnapshot()
|
||||
|
||||
// text to share with the snapshot
|
||||
let shareHashtag = NSLocalizedString("share_hashtag", comment: "")
|
||||
@@ -330,32 +326,12 @@ extension SeismicNetworksViewController: SeismicNetworkTableViewCellDelegate {
|
||||
present(controller, animated: true)
|
||||
}
|
||||
|
||||
func seismicNetworkCellDidTapWeather(_ cell: SeismicNetworkTableViewCell, hasValidWeatherData: Bool) {
|
||||
guard let index = tableView?.indexPath(for: cell) else { return }
|
||||
|
||||
if !hasValidWeatherData {
|
||||
let alert = UIAlertController(title: NSLocalizedString("attention", comment: ""),
|
||||
message: NSLocalizedString("weather_nodata", comment: ""),
|
||||
preferredStyle: .alert)
|
||||
alert.addAction(UIAlertAction(title: NSLocalizedString("ok", comment: ""), style: .default))
|
||||
present(alert, animated: true)
|
||||
return
|
||||
}
|
||||
|
||||
let indexToReloads = [openMapIndexPath, openWeatherIndexPath, index].compactMap { $0 }
|
||||
|
||||
openWeatherIndexPath = index
|
||||
openMapIndexPath = nil
|
||||
tableView?.reloadRows(at: indexToReloads, with: .automatic)
|
||||
}
|
||||
|
||||
func seismicNetworkCellDidTapMap(_ cell: SeismicNetworkTableViewCell) {
|
||||
guard let index = tableView?.indexPath(for: cell) else { return }
|
||||
|
||||
let indexToReloads = [openMapIndexPath, openWeatherIndexPath, index].compactMap { $0 }
|
||||
let indexToReloads = [openMapIndexPath, index].compactMap { $0 }
|
||||
|
||||
openMapIndexPath = index
|
||||
openWeatherIndexPath = nil
|
||||
tableView?.reloadRows(at: indexToReloads, with: .automatic)
|
||||
}
|
||||
|
||||
@@ -378,10 +354,9 @@ extension SeismicNetworksViewController: SeismicNetworkTableViewCellDelegate {
|
||||
func seismicNetworkCellDidTapClose(_ cell: SeismicNetworkTableViewCell) {
|
||||
guard let index = tableView?.indexPath(for: cell) else { return }
|
||||
|
||||
let indexToReloads = [openMapIndexPath, openWeatherIndexPath, index].compactMap { $0 }
|
||||
let indexToReloads = [openMapIndexPath, index].compactMap { $0 }
|
||||
|
||||
openMapIndexPath = nil
|
||||
openWeatherIndexPath = nil
|
||||
tableView?.reloadRows(at: indexToReloads, with: .automatic)
|
||||
}
|
||||
}
|
||||
|
||||
+3
-2
@@ -39,7 +39,8 @@ class SeismicSettingsNetworksViewController: UITableViewController {
|
||||
networks = EQNData.seismicNetworks().sorted(by: { $0.acronym < $1.acronym })
|
||||
|
||||
// load saved selected networks or fill with all available networks
|
||||
if let savedNetworks = UserDefaults.standard.object(forKey: IMPOSTAZIONE_ENTI_RETI_SISMICHEI) as? [String] {
|
||||
let savedNetworks = EQNUserData.shared.seismicNetworksSelected()
|
||||
if !savedNetworks.isEmpty {
|
||||
self.savedNetworks = savedNetworks
|
||||
} else {
|
||||
self.savedNetworks = EQNData.seismicNetworkAcronyms()
|
||||
@@ -102,7 +103,7 @@ class SeismicSettingsNetworksViewController: UITableViewController {
|
||||
|
||||
@IBAction func saveTapped(_ sender: Any) {
|
||||
// save selected networks
|
||||
UserDefaults.standard.set(savedNetworks, forKey: IMPOSTAZIONE_ENTI_RETI_SISMICHEI)
|
||||
EQNUserData.shared.saveSelectedSeismicNetworks(savedNetworks)
|
||||
|
||||
// se solo un'ente è selezionato, salviamolo anche come nazione
|
||||
if savedNetworks.count == 1 {
|
||||
|
||||
+3
-3
@@ -54,7 +54,7 @@ class SeismicSettingsViewController: UIViewController {
|
||||
confirmButton.setLocalizedTitle(key: "official_select_confirm", uppercased: false)
|
||||
otherwiseLabel.text = NSLocalizedString("official_select_or", comment: "")
|
||||
manageNetworksButton.setLocalizedTitle(key: "official_select_networks", uppercased: false)
|
||||
cancelButton.setLocalizedTitle(key: "options_cancel", uppercased: false)
|
||||
cancelButton.setLocalizedTitle(key: "status_cancel", uppercased: false)
|
||||
|
||||
// load saved country (if exists)
|
||||
let savedCountry = UserDefaults.standard.object(forKey: IMPOSTAZIONE_NAZIONE_RETI_SISMICHE) as? String
|
||||
@@ -82,7 +82,7 @@ class SeismicSettingsViewController: UIViewController {
|
||||
|
||||
// gli enti selezionati conterranno solo l'ente della nazione selezionata
|
||||
let selectedNetworks = [network.acronym]
|
||||
UserDefaults.standard.set(selectedNetworks, forKey: IMPOSTAZIONE_ENTI_RETI_SISMICHEI)
|
||||
EQNUserData.shared.saveSelectedSeismicNetworks(selectedNetworks)
|
||||
|
||||
// aggiorniamo le impostazioni di notifica
|
||||
EQNNotificheReteSismiche.shared().listaEnti = selectedNetworks
|
||||
@@ -112,7 +112,7 @@ class SeismicSettingsViewController: UIViewController {
|
||||
let alert = UIAlertController(title: NSLocalizedString("attention", comment: ""),
|
||||
message: NSLocalizedString("official_select_message", comment: ""),
|
||||
preferredStyle: .alert)
|
||||
alert.addAction(UIAlertAction(title: NSLocalizedString("options_cancel", comment: ""), style: .cancel))
|
||||
alert.addAction(UIAlertAction(title: NSLocalizedString("status_cancel", comment: ""), style: .cancel))
|
||||
alert.addAction(UIAlertAction(title: NSLocalizedString("official_select_confirm", comment: ""), style: .default, handler: { [unowned self] (action) in
|
||||
self.performSave(for: network)
|
||||
}))
|
||||
|
||||
+2
-145
@@ -14,35 +14,17 @@
|
||||
|
||||
@property (nonatomic, strong) NSArray<SettingItem *> *settings;
|
||||
|
||||
@property (strong, nonatomic) NSArray<EQNGenericValue *> *dataSourceSismi;
|
||||
@property (nonatomic, strong) NSArray<EQNGenericValue *> *dataSourceRaggioSisma;
|
||||
|
||||
@property (nonatomic, strong) EQNGenericValue *currentSeismicToNotify;
|
||||
@property (strong, nonatomic) EQNGenericValue *currentLowSeismicRadius;
|
||||
@property (strong, nonatomic) EQNGenericValue *currentStrongSeismicRadius;
|
||||
@property (strong, nonatomic) NSDate *currentStartTime;
|
||||
@property (nonatomic) BOOL isStartTimeExpanded;
|
||||
@property (strong, nonatomic) NSDate *currentEndTime;
|
||||
@property (nonatomic) BOOL isEndTimeExpanded;
|
||||
|
||||
@property (nonatomic, strong) NSDateFormatter *dateFormatter;
|
||||
|
||||
@property (nonatomic, assign) BOOL notificationEnabled;
|
||||
@property (nonatomic, assign) BOOL criticalAlertsEnabled;
|
||||
@property (nonatomic, assign) BOOL doNotDisturbEnabled;
|
||||
@end
|
||||
|
||||
@implementation SettingsRealTimeAlertsViewController
|
||||
|
||||
typedef NS_ENUM(NSInteger, RowIdentifier) {
|
||||
RowIdentifierAbilitaNotifiche = 0,
|
||||
RowIdentifierAbilitaCriticalAlerts,
|
||||
RowIdentifierSismiDaNotificare,
|
||||
RowIdentifierRaggioSismiLievi,
|
||||
RowIndntifierRaggioSismiForti,
|
||||
RowIdentifierNonDisturbare,
|
||||
RowIdentifierNonDisturbareOraInizio,
|
||||
RowIdentifierNonDisturbareOraFine
|
||||
RowIdentifierAbilitaCriticalAlerts
|
||||
};
|
||||
|
||||
#pragma mark - Accessories
|
||||
@@ -66,14 +48,8 @@ typedef NS_ENUM(NSInteger, RowIdentifier) {
|
||||
|
||||
self.settings = @[
|
||||
[[SettingItem alloc] initWithType:SettingTypeEnable title:NSLocalizedString(@"options_notification_enable_alarm", @"")],
|
||||
[[SettingItem alloc] initWithType:SettingTypeEnable title:NSLocalizedString(@"critical_alerts_setting", @"")],
|
||||
[[SettingItem alloc] initWithType:SettingTypeSegmented title:NSLocalizedString(@"options_notification_eqn_intensity", @"") subtitle:NSLocalizedString(@"", @"")],
|
||||
[[SettingItem alloc] initWithType:SettingTypeSlider title:NSLocalizedString(@"options_radius_mild", @"")],
|
||||
[[SettingItem alloc] initWithType:SettingTypeSlider title:NSLocalizedString(@"options_radius_strong", @"")]
|
||||
[[SettingItem alloc] initWithType:SettingTypeEnable title:NSLocalizedString(@"critical_alerts_setting", @"")]
|
||||
];
|
||||
|
||||
self.dataSourceSismi = [EQNData seismicToNotify];
|
||||
self.dataSourceRaggioSisma = [EQNData raggioSismi];
|
||||
|
||||
[self loadDataSource];
|
||||
[self.tableView reloadData];
|
||||
@@ -99,27 +75,7 @@ typedef NS_ENUM(NSInteger, RowIdentifier) {
|
||||
{
|
||||
self.notificationEnabled = [EQNAllertaSismica sharedInstance].isAbilitato;
|
||||
self.criticalAlertsEnabled = [EQNAllertaSismica sharedInstance].isCriticalAlertsEnabled;
|
||||
self.doNotDisturbEnabled = [EQNAllertaSismica sharedInstance].isintervalloAllarme;
|
||||
|
||||
// sismi da notificare
|
||||
EQNGenericValue *sismiDaNotificare = [EQNData seismicToNotifyFor:[EQNAllertaSismica sharedInstance].sismiDaNotificare];
|
||||
self.currentSeismicToNotify = sismiDaNotificare;
|
||||
|
||||
// raggio sismi lievi
|
||||
EQNGenericValue *raggioSismiLievi = [EQNData raggioSismaFor:[EQNAllertaSismica sharedInstance].raggioSismiLievi];
|
||||
self.currentLowSeismicRadius = raggioSismiLievi;
|
||||
|
||||
// raggio sismi forti
|
||||
EQNGenericValue *raggioSismiForti = [EQNData raggioSismaFor:[EQNAllertaSismica sharedInstance].raggioSismiForti];
|
||||
self.currentStrongSeismicRadius = raggioSismiForti;
|
||||
|
||||
// non disturbare, orari
|
||||
NSDate *startTime = [EQNData doNotDisturbEndDateFrom:[EQNAllertaSismica sharedInstance].oraioInizio];
|
||||
self.currentStartTime = startTime;
|
||||
|
||||
NSDate *endTime = [EQNData doNotDisturbEndDateFrom:[EQNAllertaSismica sharedInstance].orarioFine];
|
||||
self.currentEndTime = endTime;
|
||||
|
||||
[[EQNAllertaSismica sharedInstance] saveUserInfo];
|
||||
}
|
||||
|
||||
@@ -172,89 +128,22 @@ typedef NS_ENUM(NSInteger, RowIdentifier) {
|
||||
[[EQNAllertaSismica sharedInstance] saveUserInfo];
|
||||
[self.tableView reloadData];
|
||||
};
|
||||
} else if (indexPath.row == RowIdentifierNonDisturbare) {
|
||||
cell.toggleSwitch.on = self.doNotDisturbEnabled;
|
||||
cell.isDisabled = !self.notificationEnabled;
|
||||
cell.valueChanged = ^(BOOL enabled) {
|
||||
self.doNotDisturbEnabled = enabled;
|
||||
[EQNAllertaSismica sharedInstance].isintervalloAllarme = self.doNotDisturbEnabled;
|
||||
[[EQNAllertaSismica sharedInstance] saveUserInfo];
|
||||
[self.tableView reloadData];
|
||||
};
|
||||
}
|
||||
|
||||
return cell;
|
||||
} else if (setting.type == SettingTypeSegmented) {
|
||||
SettingSegmentedTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SettingSegmentedTableViewCell.Identifier forIndexPath:indexPath];
|
||||
cell.titleLabel.text = setting.displayTitle;
|
||||
|
||||
if (indexPath.row == RowIdentifierSismiDaNotificare) {
|
||||
cell.isDisabled = !self.notificationEnabled;
|
||||
[cell configureControlWith:self.dataSourceSismi current:self.currentSeismicToNotify];
|
||||
cell.valueChanged = ^(EQNGenericValue *item) {
|
||||
[self updateSismicToNotify:item];
|
||||
};
|
||||
}
|
||||
|
||||
return cell;
|
||||
} else if (setting.type == SettingTypeSlider) {
|
||||
SettingSliderTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SettingSliderTableViewCell.Identifier forIndexPath:indexPath];
|
||||
cell.titleLabel.text = setting.displayTitle;
|
||||
|
||||
if (indexPath.row == RowIdentifierRaggioSismiLievi) {
|
||||
cell.isDisabled = !self.notificationEnabled;
|
||||
[cell configureSliderWith:self.dataSourceRaggioSisma current:self.currentLowSeismicRadius];
|
||||
cell.valueChanged = ^(EQNGenericValue *item) {
|
||||
[self updateLowSeismicRadius:item];
|
||||
};
|
||||
} else if (indexPath.row == RowIndntifierRaggioSismiForti) {
|
||||
cell.isDisabled = !self.notificationEnabled;
|
||||
[cell configureSliderWith:self.dataSourceRaggioSisma current:self.currentStrongSeismicRadius];
|
||||
cell.valueChanged = ^(EQNGenericValue *item) {
|
||||
[self updateStrongSeismicRadius:item];
|
||||
};
|
||||
}
|
||||
|
||||
return cell;
|
||||
} else if (setting.type == SettingTypeDate) {
|
||||
SettingDateTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SettingDateTableViewCell.Identifier forIndexPath:indexPath];
|
||||
cell.isDisabled = !self.doNotDisturbEnabled || !self.notificationEnabled;
|
||||
cell.userInteractionEnabled = self.doNotDisturbEnabled && self.notificationEnabled;
|
||||
cell.titleLabel.text = setting.title;
|
||||
|
||||
if (indexPath.row == RowIdentifierNonDisturbareOraInizio) {
|
||||
cell.isPickerVisible = self.isStartTimeExpanded;
|
||||
[cell updateDate:self.currentStartTime];
|
||||
cell.valuesLabel.text = [self.dateFormatter stringFromDate:self.currentStartTime];
|
||||
cell.valueChanged = ^(NSDate *date) {
|
||||
[self updateStartTime:date];
|
||||
};
|
||||
} else if (indexPath.row == RowIdentifierNonDisturbareOraFine) {
|
||||
cell.isPickerVisible = self.isEndTimeExpanded;
|
||||
[cell updateDate:self.currentEndTime];
|
||||
cell.valuesLabel.text = [self.dateFormatter stringFromDate:self.currentEndTime];
|
||||
cell.valueChanged = ^(NSDate *date) {
|
||||
[self updateEndTime:date];
|
||||
};
|
||||
}
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
if (indexPath.row == RowIdentifierNonDisturbareOraInizio) {
|
||||
self.isStartTimeExpanded = !self.isStartTimeExpanded;
|
||||
} else if (indexPath.row == RowIdentifierNonDisturbareOraFine) {
|
||||
self.isEndTimeExpanded = !self.isEndTimeExpanded;
|
||||
}
|
||||
|
||||
[tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
- (void)updateSismicToNotify:(EQNGenericValue *)seismic
|
||||
@@ -265,38 +154,6 @@ typedef NS_ENUM(NSInteger, RowIdentifier) {
|
||||
[self loadDataSource];
|
||||
}
|
||||
|
||||
- (void)updateLowSeismicRadius:(EQNGenericValue *)radius
|
||||
{
|
||||
[EQNAllertaSismica sharedInstance].raggioSismiLievi = radius.value;
|
||||
[[EQNAllertaSismica sharedInstance] saveUserInfo];
|
||||
|
||||
[self loadDataSource];
|
||||
}
|
||||
|
||||
- (void)updateStrongSeismicRadius:(EQNGenericValue *)radius
|
||||
{
|
||||
[EQNAllertaSismica sharedInstance].raggioSismiForti = radius.value;
|
||||
[[EQNAllertaSismica sharedInstance] saveUserInfo];
|
||||
|
||||
[self loadDataSource];
|
||||
}
|
||||
|
||||
- (void)updateStartTime:(NSDate *)date
|
||||
{
|
||||
[EQNAllertaSismica sharedInstance].oraioInizio = date;
|
||||
[[EQNAllertaSismica sharedInstance] saveUserInfo];
|
||||
|
||||
[self loadDataSource];
|
||||
}
|
||||
|
||||
- (void)updateEndTime:(NSDate *)date
|
||||
{
|
||||
[EQNAllertaSismica sharedInstance].orarioFine = date;
|
||||
[[EQNAllertaSismica sharedInstance] saveUserInfo];
|
||||
|
||||
[self loadDataSource];
|
||||
}
|
||||
|
||||
- (void)askForCriticalAlertsPermission
|
||||
{
|
||||
UNAuthorizationOptions authOptions = UNAuthorizationOptionCriticalAlert;
|
||||
|
||||
@@ -27,6 +27,11 @@ class EQNBaseMapViewController: EQNBaseViewController, MKMapViewDelegate {
|
||||
!availableFilters.isEmpty
|
||||
}
|
||||
|
||||
/// If `true` the close button will be shown on top of the map view
|
||||
var isCloseButtonVisible: Bool {
|
||||
true
|
||||
}
|
||||
|
||||
// MARK: - Internal
|
||||
|
||||
/// Annotations displayed on the map
|
||||
@@ -116,13 +121,14 @@ class EQNBaseMapViewController: EQNBaseViewController, MKMapViewDelegate {
|
||||
private func setupUI() {
|
||||
view.backgroundColor = .white
|
||||
view.addSubview(mapView)
|
||||
view.addSubview(closeButton)
|
||||
view.addSubview(containerView)
|
||||
if isFilterViewVisible {
|
||||
view.addSubview(filtersView)
|
||||
}
|
||||
|
||||
closeButton.addDefaultConstraint(to: view)
|
||||
if isCloseButtonVisible {
|
||||
view.addSubview(closeButton)
|
||||
closeButton.addDefaultConstraint(to: view)
|
||||
}
|
||||
|
||||
containerView.bottomAnchor.constraint(equalTo: view.layoutMarginsGuide.bottomAnchor).isActive = true
|
||||
containerView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
|
||||
@@ -145,6 +151,8 @@ class EQNBaseMapViewController: EQNBaseViewController, MKMapViewDelegate {
|
||||
mapView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
|
||||
mapView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
|
||||
mapView.bottomAnchor.constraint(equalTo: (isFilterViewVisible ? filtersView : containerView).topAnchor).isActive = true
|
||||
|
||||
extraUI()
|
||||
}
|
||||
|
||||
// MARK: - View Lifecycle
|
||||
@@ -152,6 +160,7 @@ class EQNBaseMapViewController: EQNBaseViewController, MKMapViewDelegate {
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
configureUI()
|
||||
registerMapAnnotationViews()
|
||||
loadDataSource()
|
||||
}
|
||||
@@ -167,6 +176,16 @@ class EQNBaseMapViewController: EQNBaseViewController, MKMapViewDelegate {
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
/// Add extra UI not available in the base class
|
||||
func extraUI() {
|
||||
// nope, subclass will implement some logic
|
||||
}
|
||||
|
||||
/// Configure UI after view initialization
|
||||
func configureUI() {
|
||||
// nope, subclass will implement some logic
|
||||
}
|
||||
|
||||
/// Load data to display on the map
|
||||
func loadDataSource() {
|
||||
// nope, subclass will implement some logic
|
||||
@@ -249,7 +268,7 @@ class EQNBaseMapViewController: EQNBaseViewController, MKMapViewDelegate {
|
||||
self.applyFilter(filter)
|
||||
}))
|
||||
}
|
||||
sheet.addAction(UIAlertAction(title: NSLocalizedString("options_cancel", comment: ""), style: .cancel, handler: nil))
|
||||
sheet.addAction(UIAlertAction(title: NSLocalizedString("status_cancel", comment: ""), style: .cancel, handler: nil))
|
||||
present(sheet, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
@@ -277,7 +296,5 @@ class EQNBaseMapViewController: EQNBaseViewController, MKMapViewDelegate {
|
||||
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
|
||||
guard let annotation = view.annotation else { return }
|
||||
didTapAnnotation(annotation)
|
||||
|
||||
mapView.deselectAnnotation(annotation, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
import UIKit
|
||||
import MapKit
|
||||
import Shogun
|
||||
|
||||
class AlertSimulatorViewController: UIViewController, MKMapViewDelegate {
|
||||
|
||||
@@ -243,7 +244,7 @@ class AlertSimulatorViewController: UIViewController, MKMapViewDelegate {
|
||||
}
|
||||
|
||||
private func navigateToSubscriptions() {
|
||||
let controller = SubscriptionsViewController.makeController()
|
||||
let controller = SubscriptionsViewController.makeViewController()
|
||||
let navigationController = UINavigationController(rootViewController: controller)
|
||||
present(navigationController, animated: true)
|
||||
}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
//
|
||||
// Costanti+Extensions.swift
|
||||
// Earthquake Network
|
||||
//
|
||||
// Created by Andrea Busi on 06/03/21.
|
||||
// Copyright © 2021 Earthquake Network. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
extension EQNPastquakeIntensity {
|
||||
|
||||
var description: String {
|
||||
switch self {
|
||||
case .mild: return NSLocalizedString("widget_mild", comment: "")
|
||||
case .strong: return NSLocalizedString("widget_strong", comment: "")
|
||||
case .veryStrong: return NSLocalizedString("widget_verystring", comment: "")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,8 @@
|
||||
|
||||
/// Stampa le risposte delle chiamate al server
|
||||
static BOOL const EQNDebugPrintResponse = NO;
|
||||
/// Visualizza schermata con info di debug (ex. push token)
|
||||
static BOOL const EQNEnableDebugView = NO;
|
||||
|
||||
#pragma mark - Urls
|
||||
|
||||
@@ -31,11 +33,9 @@ static double const EQNMathKelvin = 273.15;
|
||||
#pragma mark - Server APIs
|
||||
|
||||
/// Download reti sismiche
|
||||
static NSString * const EQNServerUrlDownloadRetiSismiche = @"https://srv.earthquakenetwork.it/distquake_download_automatic18%@.php";
|
||||
static NSString * const EQNServerUrlDownloadRetiSismiche = @"https://cache.earthquakenetwork.it/distquake_download_automatic21.php%@";
|
||||
/// Recupera il tempo ancora disponibile per la versione Pro scontata
|
||||
static NSString * const EQNServerUrlOfferTimeRemaining = @"https://srv.earthquakenetwork.it/distquake_download_offer_time_remaining.php";
|
||||
/// Recupera il numero di sottoscrizioni ancora disponibili per ogni prodotto
|
||||
static NSString * const EQNServerUrlAvailableSubscriptionsCounter = @"https://srv.earthquakenetwork.it/distquake_count_top_redis.php";
|
||||
/// Registra l'abbonamento acquistato dall'utente
|
||||
static NSString * const EQNServerUrlRegisterSubscription = @"https://srv.earthquakenetwork.it/distquake_upload_subscription.php";
|
||||
/// Carica le impostazioni delle notifiche definite dall'utente
|
||||
@@ -47,15 +47,15 @@ static NSString * const EQNServerUrlUserLocation = @"https://srv.earthquakenetwo
|
||||
|
||||
static NSString * const EQNServerUrlCalibration = @"https://srv.earthquakenetwork.it/distquake_upload4.php";
|
||||
// download rete smartphone
|
||||
static NSString * const EQNServerUrlDownloadSmartphoneNetwork = @"https://srv.earthquakenetwork.it/distquake_count_redis.php";
|
||||
static NSString * const EQNServerUrlDownloadSmartphoneNetwork = @"https://cache.earthquakenetwork.it/distquake_count_redis3.php";
|
||||
// download area check
|
||||
static NSString * const EQNServerUrlDownloadAreaCheck = @"https://srv.earthquakenetwork.it/distquake_download_areacheck.php";
|
||||
// download pastquakes
|
||||
static NSString * const EQNServerUrlDownloadPastQuakes = @"https://srv.earthquakenetwork.it/distquake_download_pastquakes.php";
|
||||
// download segnalazioni
|
||||
static NSString * const EQNServerUrlDownloadUserReports = @"https://srv.earthquakenetwork.it/distquake_download_manual.php";
|
||||
static NSString * const EQNServerUrlDownloadUserReports = @"https://cache.earthquakenetwork.it/distquake_download_manual3.php";
|
||||
// Invio segnalazione
|
||||
static NSString * const EQNServerUrlSendUserReport = @"https://srv.earthquakenetwork.it/distquake_upload_manual3.php";
|
||||
static NSString * const EQNServerUrlSendUserReport = @"https://srv.earthquakenetwork.it/distquake_upload_manual4.php";
|
||||
static NSString * const EQNServerUrlSendUserReportMessage = @"https://srv.earthquakenetwork.it/distquake_upload_manual_message.php";
|
||||
/// Effettua un test delle notifiche push
|
||||
static NSString * const EQNServerUrlTestAlarm = @"https://srv.earthquakenetwork.it/distquake_upload_testalarm.php";
|
||||
@@ -64,26 +64,11 @@ static NSString * const EQNServerUrlAlertSimulator = @"https://srv.earthquakenet
|
||||
|
||||
#pragma mark - UserDefaults Keys
|
||||
|
||||
static NSString * const EQNUserDefaultAppGroupSuite = @"group.com.finazzi.distquake";
|
||||
|
||||
static NSString * const EQNUserDefaultKeyAlertsShowAllCards = @"EQNetwork.AlertsShowAllCards";
|
||||
static NSString * const EQNUserDefaultKeySesmicInformations = @"EQNetwork.SeismicInformations";
|
||||
static NSString * const EQNUserDefaultKeyOneShotShowCountry = @"EQNetwork.OneShot.CountrySelection";
|
||||
static NSString * const EQNUserDefaultLastLocation = @"EQNLast_Location";
|
||||
static NSString * const EQNUserDefaultSeismicNetworkCards = @"EQNData.RetiSismiche";
|
||||
static NSString * const EQNUserDefaultRealTimeAlertPayload = @"EQNData.RealtimeAlertPayload";
|
||||
static NSString * const EQNUserDefaultRealTimeAlertDate = @"EQNData.RealtimeAlertDate";
|
||||
|
||||
/// Numero di aperture dell'app per sbloccare la versione Pro scontata
|
||||
static NSString * const EQNUserDefaultProDiscountOpenCounter = @"CONTEGGIO_APERTURE_PER_SCONTO";
|
||||
/// Prezzo scontato per la versione pro scaduto
|
||||
static NSString * const EQNUserDefaultProDiscountExpired = @"PREZZO_SCONTATO_SCADUTO";
|
||||
/// Token Firebase dell'utente corrente
|
||||
static NSString * const EQNUserDefaultUserFirebaseToken = @"EQNToken_User";
|
||||
/// Server user ID dell'utente corrente
|
||||
static NSString * const EQNUserDefaultUserId = @"EQNUSER_ID";
|
||||
/// Token delle notifiche push
|
||||
static NSString * const EQNUserDefaultPushToken = @"EQNetwork.PushToken";
|
||||
static NSString * const EQNUserDefaultUserReportExpandedView = @"EQNData.UserReportExpandedView";
|
||||
|
||||
#pragma mark - NSNotification
|
||||
|
||||
@@ -105,12 +90,10 @@ static NSNotificationName const EQNDebugLogWillUpdateNotification = @"EQNDebugLo
|
||||
#pragma mark - Other constants
|
||||
|
||||
static NSTimeInterval const EQNSeismicDataRefreshInterval = 120.0;
|
||||
/// Tempo di attesa (minuti) tra l'invio di due segnalazioni
|
||||
static NSTimeInterval const EQNSendReportDelayBetweenMessages = 5.0;
|
||||
/// Tempo di attesa (minuti) per l'invio di due commenti
|
||||
static NSTimeInterval const EQNSendReportDelayBetweenComments = 30.0;
|
||||
/// Tempo (in minuti) entro cui vengono mostrate allerte in tempo reale ricevute
|
||||
static NSTimeInterval const EQNRealtimeAlertExpiration = 480;
|
||||
/// Tempo di attesa (secondi) tra l'invio di due segnalazioni
|
||||
static NSTimeInterval const EQNSendReportDelayBetweenMessages = 300.0; // 5 minuti
|
||||
/// Tempo di attesa (secondi) per l'invio di due commenti
|
||||
static NSTimeInterval const EQNSendReportDelayBetweenComments = 900.0; // 30 minuti
|
||||
|
||||
#ifdef DEBUG
|
||||
static NSString * const EQNAdMobAppIdAdaptiveBanner = @"ca-app-pub-3940256099942544/2934735716"; // test
|
||||
@@ -187,40 +170,9 @@ typedef enum : NSInteger {
|
||||
nonCalibrato
|
||||
} EQNStatoCal;
|
||||
|
||||
//////////////////////////////////////// SEGNALAZIONE MANUALE TERREMOTI ////////////////////////////////////////
|
||||
#define CODE_MESSAGE_EQN @"CODE_MESSAGE_EQN"
|
||||
#define DATA_MESSAGE_EQN @"DATA_MESSAGE_EQN"
|
||||
|
||||
/////////////////// Segnalazioni Utente ////////////////////////////
|
||||
#define NOTIFICHE_SU_DISTANZA_POSIZIONE @"NOTIFICHE_SU_DISTANZA_POSIZIONE"
|
||||
#define NOTIFICHE_SU_ATTIVA_SEGNALAZIONE_UTENTE @"NOTIFICHE_SU_ATTIVA_SEGNALAZIONE_UTENTE"
|
||||
|
||||
/////////////////// Reti sismiche ////////////////////////////
|
||||
#define NOTIFICHE_DISTANZA_POSIZIONE_RETI_SISMICHE @"NOTIFICHE_DISTANZA_POSIZIONE_RETI_SISMICHE"
|
||||
#define NOTIFICHE_ATTIVA_RETI_SISMICHE @"NOTIFICHE_ATTIVA_RETI_SISMICHE"
|
||||
#define NOTIFICHE_ATTIVA_RETI_SISMICHE_VICINE @"NOTIFICHE_ATTIVA_RETI_SISMICHE_VICINE"
|
||||
#define NOTIFICHE_ATTIVA_RETI_TERREMOTI_FORTI @"NOTIFICHE_ATTIVA_RETI_TERREMOTI_FORTI"
|
||||
#define NOTIFICHE_ATTIVA_RETI_ENERGIA_SISMI @"NOTIFICHE_ATTIVA_RETI_ENERGIA_SISMI"
|
||||
#define NOTIFICHE_ATTIVA_RETI_ENERGIA_FORTI @"NOTIFICHE_ATTIVA_RETI_ENERGIA_FORTI"
|
||||
#define NOTIFICHE_ATTIVA_RETI_LISTA_ENTI @"NOTIFICHE_ATTIVA_RETI_LISTA_ENTI"
|
||||
|
||||
// Sigla della rete sismica selezionata
|
||||
#define IMPOSTAZIONE_NAZIONE_RETI_SISMICHE @"IMPOSTAIONE_NAZIONE_RETI_SISMICHE"
|
||||
|
||||
#define IMPOSTAZIONE_ENTI_RETI_SISMICHEI @"IMPOSTAZIONE_ENTI_RETI_SISMICHEI"
|
||||
|
||||
/////////////////// Allera sismica ////////////////////////////
|
||||
#define NOTIFICHE_ALLERA_SISMICA_ABILITATO @"NOTIFICHE_ALLERA_SISMICA_ABILITATO"
|
||||
#define NOTIFICHE_ALLERA_SISMICA_CRITICAL_ALERTS @"NOTIFICHE_ALLERA_SISMICA_CRITICAL_ALERTS"
|
||||
#define NOTIFICHE_ALLERA_SISMICA_SISMI_DA_NOTIFICARE @"NOTIFICHE_ALLERA_SISMICA_SISMI_DA_NOTIFICARE"
|
||||
#define NOTIFICHE_ALLERA_SISMICA_RAGGIO_SISMI_LIEVI @"NOTIFICHE_ALLERA_SISMICA_RAGGIO_SISMI_LIEVI"
|
||||
#define NOTIFICHE_ALLERA_SISMICA_RAGGIO_SISMI_FORTI @"NOTIFICHE_ALLERA_SISMICA_RAGGIO_SISMI_FORTI"
|
||||
#define NOTIFICHE_ALLERA_SISMICA_IMPOSTA_VOLUME @"NOTIFICHE_ALLERA_SISMICA_IMPOSTA_VOLUME"
|
||||
#define NOTIFICHE_ALLERA_SISMICA_TESTA_ALLARME @"NOTIFICHE_ALLERA_SISMICA_TESTA_ALLARME"
|
||||
#define NOTIFICHE_ALLERA_SISMICA_ABILITA_INTERVALLO @"NOTIFICHE_ALLERA_SISMICA_ABILITA_INTERVALLO"
|
||||
#define NOTIFICHE_ALLERA_SISMICA_ORA_INIZIO @"NOTIFICHE_ALLERA_SISMICA_ORA_INIZIO"
|
||||
#define NOTIFICHE_ALLERA_SISMICA_ORA_FINE @"NOTIFICHE_ALLERA_SISMICA_ORA_INIZIO"
|
||||
|
||||
|
||||
// FILTRO ENTI
|
||||
#define EQN_MAGNITUDO_MINIMA @"EQN_MAGNITUDO_MINIMA"
|
||||
|
||||
@@ -8,6 +8,6 @@
|
||||
|
||||
#ifdef __OBJC__
|
||||
|
||||
#import "Earthquake_Network-Swift.h"
|
||||
#import <Earthquake_Network-Swift.h>
|
||||
|
||||
#endif
|
||||
|
||||
@@ -78,5 +78,31 @@
|
||||
</array>
|
||||
<key>UIUserInterfaceStyle</key>
|
||||
<string>Light</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>fb1444404982546319</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>FacebookAppID</key>
|
||||
<string>1444404982546319</string>
|
||||
<key>FacebookClientToken</key>
|
||||
<string>46c7a338b2bbd2186b2f1c12865b4004</string>
|
||||
<key>FacebookDisplayName</key>
|
||||
<string>Earthquake Network</string>
|
||||
<key>LSApplicationQueriesSchemes</key>
|
||||
<array>
|
||||
<string>fbapi</string>
|
||||
<string>fb-messenger-share-api</string>
|
||||
</array>
|
||||
<key>FacebookAutoLogAppEventsEnabled</key>
|
||||
<true/>
|
||||
<key>FacebookAdvertiserIDCollectionEnabled</key>
|
||||
<true/>
|
||||
<key>NSUserTrackingUsageDescription</key>
|
||||
<string>Il tracciamento serve a capire se la pubblicità dell'app è efficace</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
//
|
||||
// Dictionary+EQNExtensions.swift
|
||||
// Earthquake Network
|
||||
//
|
||||
// Created by Andrea Busi on 27/03/21.
|
||||
// Copyright © 2021 Earthquake Network. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
extension Dictionary {
|
||||
|
||||
func eqn_intValue(for key: Key) -> Int? {
|
||||
if let value = self[key] as? Int {
|
||||
return value
|
||||
} else if let stringValue = self[key] as? String, let value = Int(stringValue) {
|
||||
return value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func eqn_doubleValue(for key: Key) -> Double? {
|
||||
if let value = self[key] as? Double {
|
||||
return value
|
||||
} else if let stringValue = self[key] as? String, let value = Double(stringValue) {
|
||||
return value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// Foundation+Extensions.swift
|
||||
// Earthquake Network
|
||||
//
|
||||
// Created by Andrea Busi on 14/07/23.
|
||||
// Copyright © 2023 Earthquake Network. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
extension Date {
|
||||
func isBeforeInterval(
|
||||
_ interval: TimeInterval
|
||||
) -> Bool {
|
||||
let now = Date()
|
||||
return self.addingTimeInterval(interval) >= now
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
extension NSDate {
|
||||
func isBeforeInterval(
|
||||
_ interval: TimeInterval
|
||||
) -> Bool {
|
||||
return (self as Date).isBeforeInterval(interval)
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
//
|
||||
// UIFont+Extensions.swift
|
||||
// Earthquake Network
|
||||
//
|
||||
// Created by Busi Andrea on 25/09/2020.
|
||||
// Copyright © 2020 Earthquake Network. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
extension UIFont {
|
||||
static func preferredFont(for style: TextStyle, weight: Weight) -> UIFont {
|
||||
let metrics = UIFontMetrics(forTextStyle: style)
|
||||
let desc = UIFontDescriptor.preferredFontDescriptor(withTextStyle: style)
|
||||
let font = UIFont.systemFont(ofSize: desc.pointSize, weight: weight)
|
||||
return metrics.scaledFont(for: font)
|
||||
}
|
||||
}
|
||||
@@ -22,3 +22,29 @@ extension UIButton {
|
||||
setTitle(title, for: .normal)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension UIImage {
|
||||
class func circle(
|
||||
diameter: CGFloat,
|
||||
color: UIColor,
|
||||
borderWidth: CGFloat = 0.0,
|
||||
borderColor: UIColor = .black
|
||||
) -> UIImage {
|
||||
let size = CGSize(width: diameter, height: diameter)
|
||||
let renderer = UIGraphicsImageRenderer(size: size)
|
||||
let img = renderer.image { ctx in
|
||||
ctx.cgContext.setFillColor(color.cgColor)
|
||||
ctx.cgContext.setStrokeColor(borderColor.cgColor)
|
||||
ctx.cgContext.setLineWidth(borderWidth)
|
||||
|
||||
// reduce circle size to keep space for the border
|
||||
// without this, the image view is cropped
|
||||
let circleDiameter = diameter - 2*borderWidth
|
||||
let rectangle = CGRect(x: borderWidth, y: borderWidth, width: circleDiameter, height: circleDiameter)
|
||||
ctx.cgContext.addEllipse(in: rectangle)
|
||||
ctx.cgContext.drawPath(using: .fillStroke)
|
||||
}
|
||||
return img
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
//
|
||||
// StoryboardInitializable.swift
|
||||
//
|
||||
// Created by Busi Andrea on 10/11/2020.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
protocol StoryboardInitializable where Self: UIViewController {
|
||||
static var storyboardName: String { get }
|
||||
static var storyboardControllerId: String { get }
|
||||
|
||||
static func makeController() -> Self
|
||||
}
|
||||
|
||||
extension StoryboardInitializable {
|
||||
static func makeController() -> Self {
|
||||
let storyboard = UIStoryboard(name: storyboardName, bundle: Bundle(for: Self.self))
|
||||
guard let controller = storyboard.instantiateViewController(withIdentifier: storyboardControllerId) as? Self else {
|
||||
fatalError("Unable to instantiate controller with anem \(storyboardControllerId)")
|
||||
}
|
||||
return controller
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// AppPreferences.swift
|
||||
// Earthquake Network
|
||||
//
|
||||
// Created by Andrea Busi on 17/11/22.
|
||||
// Copyright © 2022 Earthquake Network. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
@objc
|
||||
class AppPreferences: NSObject {
|
||||
|
||||
@objc
|
||||
static let shared = AppPreferences()
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
/// Defines if time has to be shown on map annotations in User Reports
|
||||
var userReportExpandedView: Bool {
|
||||
get { UserDefaults.standard.bool(forKey: EQNUserDefaultUserReportExpandedView) }
|
||||
set { UserDefaults.standard.set(newValue, forKey: EQNUserDefaultUserReportExpandedView) }
|
||||
}
|
||||
|
||||
@objc
|
||||
var alertsShowAllCards: Bool {
|
||||
get { UserDefaults.standard.bool(forKey: EQNUserDefaultKeyAlertsShowAllCards) }
|
||||
set { UserDefaults.standard.set(newValue, forKey: EQNUserDefaultKeyAlertsShowAllCards) }
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreLocation
|
||||
|
||||
|
||||
public class EQNUserDefaultsCommand: EQNCommandProtocol {
|
||||
@@ -18,6 +19,9 @@ public class EQNUserDefaultsCommand: EQNCommandProtocol {
|
||||
|
||||
applyDefaultSettings()
|
||||
saveMissingValues()
|
||||
|
||||
migrationV5_3()
|
||||
migrationV5_4()
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
@@ -33,8 +37,45 @@ public class EQNUserDefaultsCommand: EQNCommandProtocol {
|
||||
|
||||
private func saveMissingValues() {
|
||||
// `raggio sismi forti` was not saved before v2.3
|
||||
if UserDefaults.standard.object(forKey: NOTIFICHE_ALLERA_SISMICA_RAGGIO_SISMI_FORTI) == nil {
|
||||
UserDefaults.standard.set("600", forKey: NOTIFICHE_ALLERA_SISMICA_RAGGIO_SISMI_FORTI)
|
||||
if UserDefaults.standard.object(forKey: UserDefaults.AllertaSismicaRaggioSismiForti) == nil {
|
||||
UserDefaults.standard.set("600", forKey: UserDefaults.AllertaSismicaRaggioSismiForti)
|
||||
}
|
||||
}
|
||||
|
||||
private func migrationV5_3() {
|
||||
let migrationPerformed = UserDefaults.standard.bool(forKey: UserDefaults.AppMigrationV5_3)
|
||||
if migrationPerformed {
|
||||
print("[EQNUserDefaultsCommand] Migration v5.3 already performed")
|
||||
return
|
||||
}
|
||||
|
||||
// l'ultima posizione era salvata come array, la trasformiamo in valore singolo
|
||||
let lastLocations = EQNUtility.loadArray(of: CLLocation.self, fromUserDefaultsForKey: UserDefaults.UserDataLastLocation) as? [CLLocation]
|
||||
if let lastLocation = lastLocations?.last {
|
||||
UserDefaults.standard.removeObject(forKey: UserDefaults.UserDataLastLocation)
|
||||
EQNUserData.shared.saveLastLocation(lastLocation)
|
||||
}
|
||||
|
||||
// resettiamo il Firebase token in modo da ri-eseguire la procedura di registrazione corretta
|
||||
EQNUserData.shared.saveFirebaseToken(nil)
|
||||
|
||||
UserDefaults.standard.set(true, forKey: UserDefaults.AppMigrationV5_3)
|
||||
}
|
||||
|
||||
private func migrationV5_4() {
|
||||
let migrationPerformed = UserDefaults.standard.bool(forKey: UserDefaults.AppMigrationV5_4)
|
||||
if migrationPerformed {
|
||||
print("[EQNUserDefaultsCommand] Migration v5.4 already performed")
|
||||
return
|
||||
}
|
||||
|
||||
// migriamo l'ultima posizione negli user defaults condivisi
|
||||
let userDefaults = UserDefaults.standard
|
||||
let groupUserDefaults = UserDefaults.appGroup
|
||||
if let encodedLocation = userDefaults.object(forKey: UserDefaults.UserDataLastLocation) as? Data {
|
||||
groupUserDefaults?.set(encodedLocation, forKey: UserDefaults.UserDataLastLocation)
|
||||
}
|
||||
|
||||
userDefaults.set(true, forKey: UserDefaults.AppMigrationV5_4)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,9 +14,6 @@ import Foundation
|
||||
@objc public static let DefaultRaggioSisma = EQNGenericValue(value:MaxRaggioSisma, display:"radius_any_distance")
|
||||
@objc public static let DefaultMagitudoDebole = EQNGenericValue(value:"2.0", display:"official_magnitude_value_20")
|
||||
@objc public static let DefaultMagitudoForte = EQNGenericValue(value:"5.5", display:"official_magnitude_value_55")
|
||||
@objc public static let DefaultSeismicToNotify = EQNGenericValue(value: "0", display: "eqn_intensity_any")
|
||||
@objc public static let DefaultDoNotDisturbStartTime = 8
|
||||
@objc public static let DefaultDoNotDisturbEndTime = 22
|
||||
@objc public static let DefaultPeriodoTemporale = EQNGenericValue(value: "1440", display: "report_timeframe_one_day")
|
||||
|
||||
// MARK: - Public
|
||||
@@ -100,7 +97,7 @@ import Foundation
|
||||
EQNSeismicNetwork(acronym: "USGS", country: NSLocalizedString("configuration_countries_united_states", comment: ""), extended: ""),
|
||||
EQNSeismicNetwork(acronym: "INGV", country: NSLocalizedString("configuration_countries_italy", comment: ""), extended: ""),
|
||||
EQNSeismicNetwork(acronym: "IGN", country: NSLocalizedString("configuration_countries_spain", comment: ""), extended: ""),
|
||||
EQNSeismicNetwork(acronym: "EMSC", country: NSLocalizedString("configuration_countries_greece", comment: ""), extended: ""),
|
||||
EQNSeismicNetwork(acronym: "UOA", country: NSLocalizedString("configuration_countries_greece", comment: ""), extended: ""),
|
||||
EQNSeismicNetwork(acronym: "EMSC", country: NSLocalizedString("configuration_countries_france", comment: ""), extended: ""),
|
||||
EQNSeismicNetwork(acronym: "EMSC", country: NSLocalizedString("configuration_countries_croatia", comment: ""), extended: ""),
|
||||
EQNSeismicNetwork(acronym: "CSI", country: NSLocalizedString("configuration_countries_china", comment: ""), extended: ""),
|
||||
@@ -136,49 +133,6 @@ import Foundation
|
||||
return Self.seismicNetworks().first(where: { $0.acronym == acronym })
|
||||
}
|
||||
|
||||
@objc class func seismicToNotify() -> [EQNGenericValue] {
|
||||
[
|
||||
EQNGenericValue(value:"0", display:"eqn_intensity_any"),
|
||||
EQNGenericValue(value:"1", display:"eqn_intensity_strong")
|
||||
]
|
||||
}
|
||||
|
||||
@objc class func seismicToNotify(for value: String?) -> EQNGenericValue {
|
||||
if let value = value, let genericValue = Self.seismicToNotify().first(where: { $0.value == value }) {
|
||||
return genericValue
|
||||
}
|
||||
return Self.DefaultSeismicToNotify
|
||||
}
|
||||
|
||||
@objc class func doNotDisturbStartDate(from date: Date?) -> Date {
|
||||
if let date = date {
|
||||
return date
|
||||
}
|
||||
|
||||
// return default
|
||||
let calendar = Calendar(identifier: .gregorian)
|
||||
let units: Set<Calendar.Component> = [.year, .month, .day, .hour, .minute]
|
||||
var components = calendar.dateComponents(units, from: Date())
|
||||
components.hour = Self.DefaultDoNotDisturbStartTime
|
||||
components.minute = 00
|
||||
return calendar.date(from: components)!
|
||||
}
|
||||
|
||||
|
||||
@objc class func doNotDisturbEndDate(from date: Date?) -> Date {
|
||||
if let date = date {
|
||||
return date
|
||||
}
|
||||
|
||||
// return default
|
||||
let calendar = Calendar(identifier: .gregorian)
|
||||
let units: Set<Calendar.Component> = [.year, .month, .day, .hour, .minute]
|
||||
var components = calendar.dateComponents(units, from: Date())
|
||||
components.hour = Self.DefaultDoNotDisturbEndTime
|
||||
components.minute = 00
|
||||
return calendar.date(from: components)!
|
||||
}
|
||||
|
||||
@objc class func periodiTemporali() -> [EQNGenericValue] {
|
||||
[
|
||||
EQNGenericValue(value: "10", display: "10 minuti"),
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// EQNDebugHelper.swift
|
||||
// Earthquake Network
|
||||
//
|
||||
// Created by Andrea Busi on 14/07/23.
|
||||
// Copyright © 2023 Earthquake Network. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
@objc
|
||||
class EQNDebugHelper: NSObject {
|
||||
|
||||
@objc
|
||||
static let shared = EQNDebugHelper()
|
||||
|
||||
private var timers: [String: Timer] = [:]
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
@objc
|
||||
func printPositions(
|
||||
interval: TimeInterval = 2.0,
|
||||
repeats: Bool = true
|
||||
) {
|
||||
let timer = Timer.scheduledTimer(withTimeInterval: interval, repeats: true) { timer in
|
||||
let current = EQNUserData.shared.lastLocation?.coordinate
|
||||
print("[EQNDebugHelper] Current | lat: \(current?.latitude ?? 0) - lon: \(current?.longitude ?? 0)")
|
||||
|
||||
let saved = EQNUser.default().lastPosition?.coordinate
|
||||
print("[EQNDebugHelper] Saved | lat: \(saved?.latitude ?? 0) - lon: \(saved?.longitude ?? 0)")
|
||||
}
|
||||
timers["positions"] = timer
|
||||
}
|
||||
}
|
||||
@@ -120,28 +120,21 @@
|
||||
|
||||
- (void)scaricaReteSismica
|
||||
{
|
||||
// Per ridurre i dati trasferiti tra app e server, ci sono degli endpoint dedicati
|
||||
// in base alla magnitudo impostata dall'utente (per valori inferiori a 2.0).
|
||||
// Se l'opzione `Mostra sismi di qualsiasi magnitudo se a meno di 50km` è attiva,
|
||||
// dobbiamo utilizzare l'endpoint `_M0_0` perchè dobbiamo scaricare tutti i dati
|
||||
|
||||
NSString *queryString = @"";
|
||||
double filterMagnitude = [[EQNSeismic shared].magnitudoMinima doubleValue];
|
||||
bool filterAnyNearEarthquake = [EQNSeismic shared].sismiQualsiasiAbilitati;
|
||||
|
||||
if (filterMagnitude < 0.5 || filterAnyNearEarthquake) {
|
||||
queryString = @"_M0_0";
|
||||
} else if (filterMagnitude < 1.0) {
|
||||
queryString = @"_M0_5";
|
||||
} else if (filterMagnitude < 1.5) {
|
||||
queryString = @"_M1_0";
|
||||
} else if (filterMagnitude < 2.0) {
|
||||
queryString = @"_M1_5";
|
||||
// L'endpoint per lo scaricamento dei dati prende due parametri: pro per il provider selezionato, mag per la
|
||||
// magnitudo minima. Se l'utente ha selezionato più di un provider, inviamo ALL. Mentre la magnitudo
|
||||
// deve avere sempre una cifra decimale (es 2.0).
|
||||
|
||||
NSString *filterProvider = @"";
|
||||
NSArray<NSString *> *networks = [EQNUserData.sharedData seismicNetworksSelected];
|
||||
if (networks.count == 1) {
|
||||
filterProvider = [networks firstObject];
|
||||
} else {
|
||||
// verrà usato l'url base
|
||||
queryString = @"";
|
||||
filterProvider = @"ALL";
|
||||
}
|
||||
|
||||
NSString *filterMagnitude = [EQNSeismic shared].magnitudoMinima;
|
||||
|
||||
NSString *queryString = [NSString stringWithFormat:@"?pro=%@&mag=%@", filterProvider, filterMagnitude];
|
||||
NSString *urlString = [NSString stringWithFormat:EQNServerUrlDownloadRetiSismiche, queryString];
|
||||
NSURL *url = [NSURL URLWithString:urlString];
|
||||
NSLog(@"[EQNManager] Url utilizzato per download reti sismiche: %@", url.absoluteURL);
|
||||
|
||||
@@ -15,6 +15,14 @@ struct EQNPurchaseAvailability {
|
||||
|
||||
// MARK: - Init
|
||||
|
||||
init(
|
||||
top10kAvailable: Int,
|
||||
top100kAvailable: Int
|
||||
) {
|
||||
self.top10kAvailable = top10kAvailable
|
||||
self.top100kAvailable = top100kAvailable
|
||||
}
|
||||
|
||||
init(data: Data) {
|
||||
guard let availabilities = try? JSONSerialization.jsonObject(with: data, options: []) as? [[String: String]] else {
|
||||
return
|
||||
|
||||
@@ -20,13 +20,13 @@ public class EQNPurchaseUtility: NSObject {
|
||||
/// If zero, no discounted price is available
|
||||
/// - Parameter completion: Completion
|
||||
static func offerTimeRemaining(completion: @escaping (_ timeRemaining: Int) -> Void) {
|
||||
let appOpenCounter = UserDefaults.standard.integer(forKey:EQNUserDefaultProDiscountOpenCounter)
|
||||
let appOpenCounter = UserDefaults.standard.integer(forKey: UserDefaults.UserDataProDiscountOpenCounter)
|
||||
if appOpenCounter < Self.AppOpenCountForDiscount {
|
||||
completion(0)
|
||||
return
|
||||
}
|
||||
|
||||
let discountExpired = UserDefaults.standard.bool(forKey: EQNUserDefaultProDiscountExpired)
|
||||
let discountExpired = UserDefaults.standard.bool(forKey: UserDefaults.UserDataProDiscountExpired)
|
||||
if discountExpired {
|
||||
completion(0)
|
||||
return
|
||||
@@ -34,7 +34,7 @@ public class EQNPurchaseUtility: NSObject {
|
||||
|
||||
EQNUser.default().downloadOfferTimeRemaining { (timeOffer) in
|
||||
if timeOffer == 0 {
|
||||
UserDefaults.standard.set(true, forKey: EQNUserDefaultProDiscountExpired)
|
||||
UserDefaults.standard.set(true, forKey: UserDefaults.UserDataProDiscountExpired)
|
||||
}
|
||||
|
||||
let timeInHours = timeOffer / 60
|
||||
@@ -45,21 +45,13 @@ public class EQNPurchaseUtility: NSObject {
|
||||
/// Returns availabilities for active subscriptions
|
||||
/// - Parameter completion: Completion
|
||||
static func availableSubscriptions(completion: @escaping (_ availability: EQNPurchaseAvailability?) -> Void) {
|
||||
guard let url = URL(string: EQNServerUrlAvailableSubscriptionsCounter) else {
|
||||
completion(nil)
|
||||
return
|
||||
}
|
||||
// previously, to get this data a separated call was required
|
||||
// starting from March 2023, the values are available from the EQNServerUrlDownloadSmartphoneNetwork request
|
||||
|
||||
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
|
||||
guard let data = data else {
|
||||
completion(nil)
|
||||
return
|
||||
}
|
||||
|
||||
let availability = EQNPurchaseAvailability(data: data)
|
||||
completion(availability)
|
||||
}
|
||||
task.resume()
|
||||
let reteSmartPhone = EQNManager.manager().rete_smartphone
|
||||
let availability = EQNPurchaseAvailability(top10kAvailable: reteSmartPhone?.top10kAvailable ?? 0,
|
||||
top100kAvailable: reteSmartPhone?.top100kAvailable ?? 0)
|
||||
completion(availability)
|
||||
}
|
||||
|
||||
/// Check if user has bought pro app version
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
//
|
||||
// EQNRealtimeAlert.swift
|
||||
// Earthquake Network
|
||||
//
|
||||
// Created by Andrea Busi on 17/06/22.
|
||||
// Copyright © 2022 Earthquake Network. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreLocation
|
||||
|
||||
|
||||
@objc
|
||||
class EQNRealtimeAlert: NSObject {
|
||||
|
||||
typealias NotificationPayload = [String: Any]
|
||||
|
||||
/// Title to display
|
||||
var title: String = ""
|
||||
/// Earthquake coordinate
|
||||
let coordinate: CLLocation
|
||||
/// Earthquake intensity
|
||||
let intensity: Int
|
||||
/// Earthquake wave speed
|
||||
let waveSpeed: Double
|
||||
/// Calculated timestamp for earthquake on user position
|
||||
let impactTimestamp: Date
|
||||
|
||||
// MARK: - Init
|
||||
|
||||
@objc
|
||||
init?(notification: [String: Any]) {
|
||||
guard let alert = Self.getPushAlertPayload(from: notification),
|
||||
let coordinate = Self.getCoordinate(from: notification),
|
||||
let impactTimestamp = EQNUtility.calculateUserSeismicTimestamp(fromUserInfo: notification) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.coordinate = coordinate
|
||||
self.impactTimestamp = impactTimestamp
|
||||
if let title = alert["loc-key"] as? String, let args = alert["loc-args"] as? [String], let arg = args.first {
|
||||
self.title = String(format: NSLocalizedString(title, comment: ""), arg)
|
||||
}
|
||||
self.intensity = notification.eqn_intValue(for: "intensity") ?? 0
|
||||
self.waveSpeed = (notification.eqn_doubleValue(for: "wave_speed") ?? 0) * 1000 // m/s
|
||||
}
|
||||
|
||||
func distanceFromUser() -> CLLocationDistance {
|
||||
EQNUser.default().lastPosition?.distance(from: coordinate) ?? 0.0
|
||||
}
|
||||
|
||||
/// Remaining time before earthquake gets user position
|
||||
func currentCountdown() -> Int {
|
||||
let now = Date()
|
||||
let difference = lround(max(impactTimestamp.timeIntervalSince(now), 0))
|
||||
return difference
|
||||
}
|
||||
|
||||
@objc
|
||||
func isCountdownExpired() -> Bool {
|
||||
currentCountdown() <= 0
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
/// Get `aps.alert` object inside a given notification payload
|
||||
/// - Parameter notification: Notification payload
|
||||
/// - Returns: `aps.alert` object if found, nil otherwise
|
||||
static func getPushAlertPayload(
|
||||
from notification: NotificationPayload
|
||||
) -> NotificationPayload? {
|
||||
guard let aps = notification["aps"] as? [String: Any],
|
||||
let alert = aps["alert"] as? [String: Any] else {
|
||||
return nil
|
||||
}
|
||||
return alert
|
||||
}
|
||||
|
||||
/// Retrieve coordinate of earthquake from the notification payload
|
||||
/// - Parameter notification: Notification payload
|
||||
/// - Returns: Coordinate if found, nil otherwise
|
||||
static func getCoordinate(
|
||||
from notification: NotificationPayload
|
||||
) -> CLLocation? {
|
||||
guard let latitude = notification.eqn_doubleValue(for: "latitude"),
|
||||
let longitude = notification.eqn_doubleValue(for: "longitude") else {
|
||||
return nil
|
||||
}
|
||||
return CLLocation(latitude: latitude, longitude: longitude)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
//
|
||||
// EQNRealtimePushNotification.swift
|
||||
// Earthquake Network
|
||||
//
|
||||
// Created by Andrea Busi on 13/07/23.
|
||||
// Copyright © 2023 Earthquake Network. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
import CoreLocation
|
||||
import Shogun
|
||||
|
||||
@objc
|
||||
class EQNRealtimePushNotification: NSObject, Codable {
|
||||
|
||||
/// Tempo (in secondi) entro cui vengono mostrate allerte in tempo reale ricevute
|
||||
private static let RealtimeAlertExpiration: TimeInterval = 28_800 // 8 ore
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case type
|
||||
case intensity
|
||||
case latitude
|
||||
case longitude
|
||||
case counter
|
||||
case dateTime
|
||||
case waveSpeed
|
||||
case impactTimestamp
|
||||
case peak
|
||||
case title
|
||||
case displayTitle
|
||||
case displayBody
|
||||
}
|
||||
|
||||
|
||||
let type: String
|
||||
/// Earthquake intensity
|
||||
let intensity: Int
|
||||
/// Earthquake coordinate
|
||||
let latitude: Double
|
||||
let longitude: Double
|
||||
/// Number of smartphones that report the earthquake
|
||||
let counter: Int
|
||||
let dateTime: Date?
|
||||
/// Earthquake wave speed
|
||||
let waveSpeed: Double
|
||||
/// Calculated timestamp for earthquake on user position
|
||||
let impactTimestamp: Date?
|
||||
let peak: Double?
|
||||
|
||||
// Title received inside `aps.alert`
|
||||
let title: String
|
||||
// Title and body elaborated in NotificationService
|
||||
let displayTitle: String
|
||||
let displayBody: String
|
||||
|
||||
var coordinate: CLLocation {
|
||||
.init(latitude: latitude, longitude: longitude)
|
||||
}
|
||||
|
||||
// MARK: - Init
|
||||
|
||||
init(
|
||||
type: String,
|
||||
intensity: Int,
|
||||
latitude: Double,
|
||||
longitude: Double,
|
||||
counter: Int,
|
||||
dateTime: Date?,
|
||||
waveSpeed: Double,
|
||||
impactTimestamp: Date?,
|
||||
peak: Double?,
|
||||
title: String,
|
||||
displayTitle: String,
|
||||
displayBody: String
|
||||
) {
|
||||
self.type = type
|
||||
self.intensity = intensity
|
||||
self.latitude = latitude
|
||||
self.longitude = longitude
|
||||
self.counter = counter
|
||||
self.dateTime = dateTime
|
||||
self.waveSpeed = waveSpeed
|
||||
self.impactTimestamp = impactTimestamp
|
||||
self.peak = peak
|
||||
self.title = title
|
||||
self.displayTitle = displayTitle
|
||||
self.displayBody = displayBody
|
||||
}
|
||||
|
||||
func distanceFromUser() -> CLLocationDistance {
|
||||
// Usiamo la posizione salvata, che coincide con quella presnete in EQNUser.
|
||||
// In questo modo non abbiamo dipendenze e possiamo includere questa classe
|
||||
// anche nel target NotificationService
|
||||
EQNUserData.shared.lastLocation?.distance(from: coordinate) ?? 0.0
|
||||
}
|
||||
|
||||
/// Remaining time before earthquake gets user position
|
||||
func currentCountdown() -> Int {
|
||||
guard let impactTimestamp else { return 0 }
|
||||
|
||||
let now = Date()
|
||||
let difference = lround(max(impactTimestamp.timeIntervalSince(now), 0))
|
||||
return difference
|
||||
}
|
||||
|
||||
@objc
|
||||
func isCountdownExpired() -> Bool {
|
||||
currentCountdown() <= 0
|
||||
}
|
||||
|
||||
/// Intensity on user location
|
||||
func relativeIntensity() -> Double {
|
||||
guard distanceFromUser() > 0, let peak else { return 0 }
|
||||
|
||||
let distanceKm = distanceFromUser() / 1_000 // get in km
|
||||
let relativeIntensity = peak * exp(-distanceKm/peak/250)
|
||||
return relativeIntensity
|
||||
}
|
||||
|
||||
// MARK: - Class
|
||||
|
||||
/// Remove any saved notification
|
||||
@objc(removeStoredNotification)
|
||||
static func removeStored() {
|
||||
UserDefaults.standard.removeObject(forKey: UserDefaults.RealTimeAlertPayload)
|
||||
UserDefaults.standard.removeObject(forKey: UserDefaults.RealTimeAlertDate)
|
||||
}
|
||||
|
||||
@objc(storedNotification)
|
||||
static func stored() -> EQNRealtimePushNotification? {
|
||||
guard let date = UserDefaults.standard.object(forKey: UserDefaults.RealTimeAlertDate) as? Date else {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard date.isBeforeInterval(Self.RealtimeAlertExpiration) else {
|
||||
print("[EQNRealtimePushNotification] Saved notification expired")
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let data = UserDefaults.standard.object(forKey: UserDefaults.RealTimeAlertPayload) as? Data else {
|
||||
print("[EQNRealtimePushNotification] No notification saved for key '\(UserDefaults.RealTimeAlertPayload)'")
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let notification = try? JSONDecoder().decode(EQNRealtimePushNotification.self, from: data) else {
|
||||
print("[EQNRealtimePushNotification] Unable to decode given notification")
|
||||
return nil
|
||||
}
|
||||
|
||||
return notification
|
||||
}
|
||||
|
||||
/// Convert and store a push notification payload.
|
||||
/// Expected payload has the following structure:
|
||||
/// ```
|
||||
/// {
|
||||
/// "title": "Allerta sismica in tempo reale",
|
||||
/// "body": "Previsto uno scuotimento forte",
|
||||
/// "userInfo": {
|
||||
/// "datetime" : "2023-07-13 12:24:04",
|
||||
/// ...
|
||||
/// "aps": {
|
||||
/// "alert" : {
|
||||
/// "loc-key" : "Rilevato sisma forte a",
|
||||
/// "title-loc-key" : "Allerta sismica in tempo reale",
|
||||
/// "loc-args" : [
|
||||
/// "150 km (Test)"
|
||||
/// ]
|
||||
/// },
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
/// - Parameter payload: Notification payload
|
||||
/// - Returns: `true` if save succeed, `false` otherwise
|
||||
@objc(storeNotificationWithPayload:)
|
||||
@discardableResult
|
||||
static func store(payload: [String: Any]) -> Bool {
|
||||
guard let notification = from(payload: payload) else {
|
||||
print("[EQNRealtimePushNotification] Unable to convert received notification")
|
||||
return false
|
||||
}
|
||||
|
||||
guard let data = try? JSONEncoder().encode(notification) else {
|
||||
print("[EQNRealtimePushNotification] Unable to encode given notification")
|
||||
return false
|
||||
}
|
||||
|
||||
UserDefaults.standard.set(data, forKey: UserDefaults.RealTimeAlertPayload)
|
||||
UserDefaults.standard.set(Date(), forKey: UserDefaults.RealTimeAlertDate)
|
||||
return true
|
||||
}
|
||||
|
||||
@objc
|
||||
private static func from(payload: [String: Any]) -> EQNRealtimePushNotification? {
|
||||
guard let userInfo = payload["userInfo"] as? [String: Any],
|
||||
let aps = userInfo["aps"] as? [String: Any],
|
||||
let alert = aps["alert"] as? [String: Any] else {
|
||||
print("[EQNRealtimePushNotification] Missing required info to parse push notification")
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let latitude = userInfo.double(forKey: "latitude"),
|
||||
let longitude = userInfo.double(forKey: "longitude") else {
|
||||
print("[EQNRealtimePushNotification] Unable to get coordinate from push notification")
|
||||
return nil
|
||||
}
|
||||
|
||||
let type = userInfo.string(forKey: "type", orDefault: "")
|
||||
let intensity = userInfo.integer(forKey: "intensity", orDefault: 0)
|
||||
|
||||
let counter = userInfo.integer(forKey: "counter", orDefault: 0)
|
||||
var dateTime: Date?
|
||||
if let dateString = userInfo.string(forKey: "datetime"), let date = EQNUtility.getDateFrom(dateString) {
|
||||
dateTime = date
|
||||
}
|
||||
let waveSpeed = userInfo.double(forKey: "wave_speed", orDefault: 0.0) * 1000 // m/s
|
||||
var impactTimestamp: Date?
|
||||
if let timestamp = EQNUtility.calculateUserSeismicTimestamp(fromUserInfo: userInfo) {
|
||||
impactTimestamp = timestamp
|
||||
}
|
||||
let peak = userInfo.double(forKey: "peak")
|
||||
|
||||
var title: String = ""
|
||||
if let titleKey = alert["loc-key"] as? String, let args = alert["loc-args"] as? [String], let arg = args.first {
|
||||
title = String(format: NSLocalizedString(titleKey, comment: ""), arg)
|
||||
}
|
||||
let displayTitle = payload.string(forKey: "title", orDefault: "")
|
||||
let displayBody = payload.string(forKey: "body", orDefault: "")
|
||||
|
||||
return .init(
|
||||
type: type,
|
||||
intensity: intensity,
|
||||
latitude: latitude,
|
||||
longitude: longitude,
|
||||
counter: counter,
|
||||
dateTime: dateTime,
|
||||
waveSpeed: waveSpeed,
|
||||
impactTimestamp: impactTimestamp,
|
||||
peak: peak,
|
||||
title: title,
|
||||
displayTitle: displayTitle,
|
||||
displayBody: displayBody
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension EQNRealtimePushNotification {
|
||||
/// Returns the color based on relative intensity
|
||||
var relativeIntensityColor: UIColor {
|
||||
let intensity = relativeIntensity()
|
||||
switch intensity {
|
||||
case _ where intensity < 0.004:
|
||||
return UIColor(red: 90.0/255.0, green: 90.0/255.0, blue: 90.0/255.0, alpha: 1.0)
|
||||
case _ where intensity < 0.30:
|
||||
return UIColor(red: 38.0/255.0, green: 100.0/255.0, blue: 38.0/255.0, alpha: 1.0)
|
||||
case _ where intensity < 0.70:
|
||||
return UIColor(red: 255.0/255.0, green: 140.0/255.0, blue: 0.0, alpha: 1.0)
|
||||
default:
|
||||
return UIColor(red: 215.0/255.0, green: 0.0, blue: 0.0, alpha: 1.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Shogun
|
||||
|
||||
|
||||
@objc
|
||||
@@ -14,11 +15,11 @@ class EQNReteSmartphone: NSObject {
|
||||
@objc let counterLastDayAlerts: Int
|
||||
@objc let counterTotalAlerts: Int
|
||||
@objc let counterSmartphones: Int
|
||||
@objc let manualGreen: Int
|
||||
@objc let manualYellow: Int
|
||||
@objc let manualRed: Int
|
||||
@objc let manual: Int
|
||||
@objc let lastSubscriptionDiff: Int
|
||||
@objc let subscriptionsDiscounted: Bool
|
||||
let top10kAvailable: Int
|
||||
let top100kAvailable: Int
|
||||
|
||||
// MARK: - Init
|
||||
|
||||
@@ -28,30 +29,16 @@ class EQNReteSmartphone: NSObject {
|
||||
return result.merging(dictionary, uniquingKeysWith: { (_, new) in new })
|
||||
}
|
||||
|
||||
self.counterLastDayAlerts = Self.getValue(from: allValues, for: "eq")
|
||||
self.counterTotalAlerts = Self.getValue(from: allValues, for: "eq_p")
|
||||
self.counterSmartphones = Self.getValue(from: allValues, for: "green")
|
||||
self.manualGreen = Self.getValue(from: allValues, for: "g_man")
|
||||
self.manualYellow = Self.getValue(from: allValues, for: "y_man")
|
||||
self.manualRed = Self.getValue(from: allValues, for: "r_man")
|
||||
self.lastSubscriptionDiff = Self.getValue(from: allValues, for: "diff")
|
||||
let subscriptionsDiscounted = Self.getValue(from: allValues, for: "st")
|
||||
self.counterLastDayAlerts = allValues.integer(forKey: "eq", orDefault: 0)
|
||||
self.counterTotalAlerts = allValues.integer(forKey: "eq_p", orDefault: 0)
|
||||
self.counterSmartphones = allValues.integer(forKey: "green", orDefault: 0)
|
||||
self.manual = allValues.integer(forKey: "man", orDefault: 0)
|
||||
self.lastSubscriptionDiff = allValues.integer(forKey: "diff", orDefault: 0)
|
||||
let subscriptionsDiscounted = allValues.integer(forKey: "st", orDefault: 0)
|
||||
self.subscriptionsDiscounted = subscriptionsDiscounted == 1
|
||||
self.top10kAvailable = allValues.integer(forKey: "t10k")
|
||||
self.top100kAvailable = allValues.integer(forKey: "t100k")
|
||||
|
||||
super.init()
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
/// This method helps to extract an int value from the received values (where data are both strings and integers).
|
||||
/// If convertion is not possible, it will return zero.
|
||||
private static func getValue(from values: [String: Any], for key: String) -> Int {
|
||||
if let intValue = values[key] as? Int {
|
||||
return intValue
|
||||
}
|
||||
if let stringValue = values[key] as? String, let intValue = Int(stringValue) {
|
||||
return intValue
|
||||
}
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,23 +9,15 @@
|
||||
@import Foundation;
|
||||
@import CoreLocation;
|
||||
|
||||
// Intensità terremoti segnalazioni utente
|
||||
typedef NS_CLOSED_ENUM(NSInteger, EQNPastquakeIntensity) {
|
||||
EQNPastquakeIntensityMild = 1,
|
||||
EQNPastquakeIntensityStrong = 2,
|
||||
EQNPastquakeIntensityVeryStrong = 3
|
||||
};
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface EQNSegnalazione : NSObject
|
||||
|
||||
@property (nonatomic, strong) CLLocation *coordinate;
|
||||
@property (nonatomic, strong) NSString *address;
|
||||
@property (nonatomic, strong) NSDate *date;
|
||||
@property (nonatomic) NSInteger difference;
|
||||
@property (nonatomic) EQNPastquakeIntensity intensity;
|
||||
@property (nonatomic, strong) NSString *message;
|
||||
// values are from 20 to 120
|
||||
@property (nonatomic) NSInteger intensity;
|
||||
|
||||
- (instancetype)initWithDictionary:(NSDictionary *)dictionary;
|
||||
|
||||
|
||||
@@ -17,15 +17,12 @@
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
double latitude = [dictionary[@"latitude"] doubleValue];
|
||||
double longitude = [dictionary[@"longitude"] doubleValue];
|
||||
double latitude = [dictionary[@"la"] doubleValue];
|
||||
double longitude = [dictionary[@"lo"] doubleValue];
|
||||
_coordinate = [[CLLocation alloc] initWithLatitude:latitude longitude:longitude];
|
||||
_address = dictionary[@"address"];
|
||||
NSDate *date = [EQNUtility getDateFromString:dictionary[@"date"]];
|
||||
NSDate *date = [EQNUtility getDateFromString:dictionary[@"dt"]];
|
||||
_date = date == nil ? [NSDate date] : date;
|
||||
_difference = [dictionary[@"difference"] integerValue];
|
||||
_intensity = [dictionary[@"magnitude"] integerValue];
|
||||
_message = dictionary[@"msg"];
|
||||
_intensity = [dictionary[@"ma"] integerValue];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -34,24 +31,18 @@
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)encoder
|
||||
{
|
||||
[encoder encodeObject:self.address forKey:@"address"];
|
||||
[encoder encodeObject:self.date forKey:@"date"];
|
||||
[encoder encodeInteger:self.difference forKey:@"difference"];
|
||||
[encoder encodeObject:self.coordinate forKey:@"coordinate"];
|
||||
[encoder encodeInteger:self.intensity forKey:@"intensity"];
|
||||
[encoder encodeObject:self.message forKey:@"message"];
|
||||
}
|
||||
|
||||
- (id)initWithCoder:(NSCoder *)decoder
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.address = [decoder decodeObjectForKey:@"address"];
|
||||
self.date = [decoder decodeObjectForKey:@"date"];
|
||||
self.difference = [decoder decodeIntegerForKey:@"difference"];
|
||||
self.coordinate = [decoder decodeObjectForKey:@"coordinate"];
|
||||
self.intensity = [decoder decodeIntegerForKey:@"intensity"];
|
||||
self.message = [decoder decodeObjectForKey:@"message"];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -109,10 +109,8 @@ import Foundation
|
||||
|
||||
@objc func filterSeismicList(_ list: [EQNSisma]) -> [EQNSisma] {
|
||||
// enti abilitati
|
||||
var networks: [String]
|
||||
if let savedNetworks = UserDefaults.standard.object(forKey: IMPOSTAZIONE_ENTI_RETI_SISMICHEI) as? [String] {
|
||||
networks = savedNetworks
|
||||
} else {
|
||||
var networks = EQNUserData.shared.seismicNetworksSelected()
|
||||
if networks.isEmpty {
|
||||
networks = EQNData.seismicNetworkAcronyms()
|
||||
}
|
||||
networks = networks.map { $0.lowercased() }
|
||||
|
||||
@@ -33,14 +33,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@property (nonatomic, strong) NSNumber *preliminary;
|
||||
@property (nonatomic, strong) NSNumber *smartphoneNumber;
|
||||
@property (nonatomic, strong) NSNumber *userNumber;
|
||||
@property (nonatomic, strong, nullable) NSString *weatherCode;
|
||||
@property (nonatomic, strong) NSString *weatherIcon;
|
||||
@property (nonatomic, strong) NSNumber *weatherCloud;
|
||||
@property (nonatomic, strong) NSNumber *weatherWindSpeed;
|
||||
@property (nonatomic, strong) NSNumber *weatherPressure;
|
||||
@property (nonatomic, strong) NSNumber *weatherHumidity;
|
||||
@property (nonatomic, strong) NSNumber *weatherTemperature;
|
||||
@property (nonatomic, strong) NSNumber *pictureCount;
|
||||
|
||||
- (instancetype)initWithInfo:(NSDictionary *)info;
|
||||
|
||||
|
||||
@@ -42,15 +42,6 @@
|
||||
self.preliminary = info[@"py"];
|
||||
self.smartphoneNumber = info[@"sm"];
|
||||
self.userNumber = info[@"rp"];
|
||||
|
||||
self.weatherCode = [info eqn_safeObjectForKey:@"wc"];
|
||||
self.weatherIcon = info[@"ic"];
|
||||
self.weatherCloud = info[@"cl"];
|
||||
self.weatherWindSpeed = info[@"ws"];
|
||||
self.weatherPressure = info[@"pe"];
|
||||
self.weatherHumidity = info[@"hu"];
|
||||
self.weatherTemperature = info[@"te"];
|
||||
self.pictureCount = info[@"pc"];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -74,14 +65,6 @@
|
||||
[encoder encodeObject:self.preliminary forKey:@"preliminary"];
|
||||
[encoder encodeObject:self.smartphoneNumber forKey:@"smartphoneNumber"];
|
||||
[encoder encodeObject:self.userNumber forKey:@"userNumber"];
|
||||
[encoder encodeObject:self.weatherCode forKey:@"weatherCode"];
|
||||
[encoder encodeObject:self.weatherIcon forKey:@"weatherIcon"];
|
||||
[encoder encodeObject:self.weatherCloud forKey:@"weatherCloud"];
|
||||
[encoder encodeObject:self.weatherWindSpeed forKey:@"weatherWindSpeed"];
|
||||
[encoder encodeObject:self.weatherPressure forKey:@"weatherPressure"];
|
||||
[encoder encodeObject:self.weatherHumidity forKey:@"weatherHumidity"];
|
||||
[encoder encodeObject:self.weatherTemperature forKey:@"weatherTemperature"];
|
||||
[encoder encodeObject:self.pictureCount forKey:@"pictureCount"];
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)decoder
|
||||
@@ -103,14 +86,6 @@
|
||||
self.preliminary = [decoder decodeObjectForKey:@"preliminary"];
|
||||
self.smartphoneNumber = [decoder decodeObjectForKey:@"smartphoneNumber"];
|
||||
self.userNumber = [decoder decodeObjectForKey:@"userNumber"];
|
||||
self.weatherCode = [decoder decodeObjectForKey:@"weatherCode"];
|
||||
self.weatherIcon = [decoder decodeObjectForKey:@"weatherIcon"];
|
||||
self.weatherCloud = [decoder decodeObjectForKey:@"weatherCloud"];
|
||||
self.weatherWindSpeed = [decoder decodeObjectForKey:@"weatherWindSpeed"];
|
||||
self.weatherPressure = [decoder decodeObjectForKey:@"weatherPressure"];
|
||||
self.weatherHumidity = [decoder decodeObjectForKey:@"weatherHumidity"];
|
||||
self.weatherTemperature = [decoder decodeObjectForKey:@"weatherTemperature"];
|
||||
self.pictureCount = [decoder decodeObjectForKey:@"pictureCount"];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -15,20 +15,16 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface EQNUser : NSObject
|
||||
|
||||
@property (nonatomic, strong, nullable) NSString *tokenUser;
|
||||
@property (nonatomic, strong, nullable) NSString *user_ID;
|
||||
@property (nonatomic, strong, nullable) CLLocation *lastPosition;
|
||||
@property (nonatomic, assign) CLLocationDistance distanza;
|
||||
@property (nonatomic, assign) BOOL monitorOn;
|
||||
@property (nonatomic, assign) BOOL inCarica;
|
||||
@property (nonatomic, assign) BOOL registrato;
|
||||
|
||||
+ (instancetype)defaultUser;
|
||||
- (void)inviaPosizioneServer;
|
||||
- (void)saveUserInfo;
|
||||
- (void)removeUser;
|
||||
- (void)verificaRegistrazione;
|
||||
|
||||
- (void)retryUserRegistration;
|
||||
- (void)registerUserIfNeededWithFirebaseToken:(NSString *)firebaseToken;
|
||||
- (void)downloadOfferTimeRemainingWithCompletion:(timeRemainingCompletion)completionHandler;
|
||||
|
||||
@end
|
||||
|
||||
@@ -8,12 +8,16 @@
|
||||
|
||||
#import "EQNUser.h"
|
||||
#import "Costanti.h"
|
||||
#import "EQNAccelerometroManager.h"
|
||||
#import "ServerRequest.h"
|
||||
#import "EQNAccelerometroManager.h"
|
||||
#import "EQNGeneratoreURLServer.h"
|
||||
#import "EQNUtility.h"
|
||||
#import "EQNManager.h"
|
||||
|
||||
@interface EQNUser ()
|
||||
@property (strong, nonatomic) NSString *currentFirebaseToken;
|
||||
@property (nonatomic) BOOL registrationInProgress;
|
||||
@end
|
||||
|
||||
@implementation EQNUser
|
||||
|
||||
@@ -35,109 +39,61 @@
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_tokenUser = [[NSUserDefaults standardUserDefaults] objectForKey:EQNUserDefaultUserFirebaseToken];
|
||||
id savedUserId = [[NSUserDefaults standardUserDefaults] objectForKey:EQNUserDefaultUserId];
|
||||
_user_ID = [self convertUserIdIntoString:savedUserId];
|
||||
self.registrationInProgress = NO;
|
||||
self.user_ID = EQNUserData.sharedData.userId;
|
||||
self.lastPosition = EQNUserData.sharedData.lastLocation;
|
||||
|
||||
NSArray *lastPosArray = [EQNUtility loadArrayOfClass:[CLLocation class] fromUserDefaultsForKey:EQNUserDefaultLastLocation];
|
||||
if (lastPosArray.count > 0) {
|
||||
_lastPosition = [lastPosArray lastObject];
|
||||
}
|
||||
|
||||
_registrato = NO;
|
||||
if (_user_ID) {
|
||||
_registrato = YES;
|
||||
}
|
||||
|
||||
[[EQNAccelerometroManager sharedInstance] addObserver:(id)self
|
||||
forKeyPath:@"currentLocation"
|
||||
options:NSKeyValueObservingOptionNew
|
||||
context:nil];
|
||||
|
||||
[[EQNAccelerometroManager sharedInstance] startUpdatingLocationBackground];
|
||||
[self registerForLocationUpdates];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Accessories
|
||||
|
||||
- (void)setTokenUser:(NSString *)tokenUser
|
||||
{
|
||||
// token could be retrieved after some times
|
||||
// thanks to this, we force the server registration when the token is received
|
||||
_tokenUser = tokenUser;
|
||||
[[NSUserDefaults standardUserDefaults] setObject:tokenUser forKey:EQNUserDefaultUserFirebaseToken];
|
||||
[self verificaRegistrazione];
|
||||
}
|
||||
|
||||
#pragma mark - Public
|
||||
|
||||
- (void)saveUserInfo
|
||||
{
|
||||
[[NSUserDefaults standardUserDefaults] setObject:self.tokenUser forKey:EQNUserDefaultUserFirebaseToken];
|
||||
[[NSUserDefaults standardUserDefaults] setObject:self.user_ID forKey:EQNUserDefaultUserId];
|
||||
if (self.lastPosition) {
|
||||
NSArray *lastPosiArray = @[self.lastPosition];
|
||||
[EQNUtility storeArray:lastPosiArray toUserDefaultForKey:EQNUserDefaultLastLocation];
|
||||
}
|
||||
[EQNUserData.sharedData saveFirebaseToken:self.currentFirebaseToken];
|
||||
[EQNUserData.sharedData saveUserId:self.user_ID];
|
||||
[EQNUserData.sharedData saveLastLocation:self.lastPosition];
|
||||
}
|
||||
|
||||
- (void)removeUser
|
||||
{
|
||||
[[NSUserDefaults standardUserDefaults] removeObjectForKey:EQNUserDefaultUserFirebaseToken];
|
||||
[[NSUserDefaults standardUserDefaults] removeObjectForKey:EQNUserDefaultUserId];
|
||||
[[NSUserDefaults standardUserDefaults] removeObjectForKey:EQNUserDefaultLastLocation];
|
||||
[[NSUserDefaults standardUserDefaults] synchronize];
|
||||
self.tokenUser = nil;
|
||||
self.currentFirebaseToken = nil;
|
||||
[EQNUserData.sharedData removeAllData];
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
|
||||
#pragma mark - Public
|
||||
|
||||
- (void)retryUserRegistration
|
||||
{
|
||||
if ([keyPath isEqualToString:@"currentLocation"]) {
|
||||
// do some stuff
|
||||
if (self.lastPosition) {
|
||||
self.distanza = [self.lastPosition distanceFromLocation:[EQNAccelerometroManager sharedInstance].currentLocation];
|
||||
}
|
||||
self.lastPosition = [EQNAccelerometroManager sharedInstance].currentLocation;
|
||||
[self verificaRegistrazione];
|
||||
|
||||
if (![EQNManager defaultManager].isBackground)
|
||||
[[EQNAccelerometroManager sharedInstance] stopUpdatingLocation];
|
||||
}
|
||||
[self registerUserIfNeededWithFirebaseToken:self.currentFirebaseToken];
|
||||
}
|
||||
|
||||
- (void)verificaRegistrazione
|
||||
- (void)registerUserIfNeededWithFirebaseToken:(NSString *)firebaseToken
|
||||
{
|
||||
if (!self.user_ID && self.tokenUser) {
|
||||
[self inviaregistrazioneServer:self.tokenUser withPosition:self.lastPosition];
|
||||
self.currentFirebaseToken = firebaseToken;
|
||||
NSString *previousFirebaseToken = EQNUserData.sharedData.firebaseToken;
|
||||
|
||||
// dobbiamo effettuare la registrazione se:
|
||||
// - userId non disponibile (si tratta di prima registrazione)
|
||||
// - i due token di Firebase sono diversi (il token è stato aggiornato)
|
||||
|
||||
if (!self.user_ID) {
|
||||
// prima registrazione dell'utente
|
||||
NSLog(@"[EQNUser] perform first registration");
|
||||
[self performServerRegistrationWithFirebaseToken:firebaseToken existingUserId:nil];
|
||||
} else if (![previousFirebaseToken isEqualToString:firebaseToken]) {
|
||||
// token cambiato, effettuiamo una nuova registrazione
|
||||
NSLog(@"[EQNUser] firebase token is changed, update registration");
|
||||
[self performServerRegistrationWithFirebaseToken:firebaseToken existingUserId:self.user_ID];
|
||||
} else {
|
||||
[self inviaPosizioneServer];
|
||||
// non serve la registrazione, monitorniamo la posizione
|
||||
NSLog(@"[EQNUser] user already registered, start location update");
|
||||
[self registerForLocationUpdates];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)inviaregistrazioneServer:(NSString *)token withPosition:(CLLocation *)location
|
||||
{
|
||||
[[ServerRequest defaultServerConnectionSingleton] inviaInformazioniAlServerWithURL:[EQNGeneratoreURLServer urlRegistrazione] richiesta:EQNTipoChiamataRegistrazione success:^(id result) {
|
||||
|
||||
self.user_ID = [self convertUserIdIntoString:result];
|
||||
[self saveUserInfo];
|
||||
|
||||
} failure:^(NSError *errore) {
|
||||
NSLog(@"USER_ID Error %@", errore);
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:EQNServerRegistrationDidFailNotification object:nil];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)inviaPosizioneServer
|
||||
{
|
||||
[[ServerRequest defaultServerConnectionSingleton] inviaInformazioniAlServerWithURL:[EQNGeneratoreURLServer urlPosizione] richiesta:EQNTipoChiamataPosizione success:^(id result) {
|
||||
NSLog(@"[EQNUtility] Position saved on server");
|
||||
[self saveUserInfo];
|
||||
} failure:^(NSError *error) {
|
||||
NSLog(@"[EQNUtility] Unable to save position. Error: %@", error.localizedDescription);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)downloadOfferTimeRemainingWithCompletion:(_Nonnull timeRemainingCompletion)completionHandler
|
||||
{
|
||||
NSURL *url = [EQNGeneratoreURLServer urlDownloadOfferTimeRemaining];
|
||||
@@ -157,8 +113,92 @@
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - Network
|
||||
|
||||
- (void)performServerRegistrationWithFirebaseToken:(NSString *)token existingUserId:(NSString *)userId
|
||||
{
|
||||
if (self.registrationInProgress) {
|
||||
NSLog(@"[EQNUser] Registration already in progress");
|
||||
return;
|
||||
}
|
||||
|
||||
self.registrationInProgress = YES;
|
||||
NSURL *url = [EQNGeneratoreURLServer urlRegistrazioneFirebaseToken:token existingUserId:userId];
|
||||
[[ServerRequest defaultServerConnectionSingleton] inviaInformazioniAlServerWithURL:url richiesta:EQNTipoChiamataRegistrazione success:^(id result) {
|
||||
NSLog(@"[EQNUser] User registration completed");
|
||||
|
||||
// store userId
|
||||
self.registrationInProgress = NO;
|
||||
self.user_ID = [self convertUserIdIntoString:result];
|
||||
[self saveUserInfo];
|
||||
|
||||
// inviamo, se già disponibile, la posizione al server
|
||||
[self performServerSendLocation];
|
||||
} failure:^(NSError *errore) {
|
||||
NSLog(@"[EQNUser] Unable to perform user registration: %@", errore);
|
||||
self.registrationInProgress = NO;
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:EQNServerRegistrationDidFailNotification object:nil];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)performServerSendLocation
|
||||
{
|
||||
if (!self.user_ID) {
|
||||
NSLog(@"[EQNUser] User id not available");
|
||||
return;
|
||||
}
|
||||
|
||||
NSURL *url = [EQNGeneratoreURLServer urlPosizione];
|
||||
[[ServerRequest defaultServerConnectionSingleton] inviaInformazioniAlServerWithURL:url richiesta:EQNTipoChiamataPosizione success:^(id result) {
|
||||
|
||||
if ([result isKindOfClass:[NSString class]]) {
|
||||
NSString *stringResult = (NSString *)result;
|
||||
if ([stringResult isEqualToString:@"reg"]) {
|
||||
// l'utente non è stato trovato sul server, ri-eseguiamo la registrazione
|
||||
NSLog(@"[EQNUser] User not found, retry registration");
|
||||
[self retryUserRegistration];
|
||||
} else {
|
||||
NSLog(@"[EQNUser] Position saved on server");
|
||||
}
|
||||
}
|
||||
|
||||
[self saveUserInfo];
|
||||
} failure:^(NSError *error) {
|
||||
NSLog(@"[EQNUser] Unable to save position. Error: %@", error.localizedDescription);
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
- (void)registerForLocationUpdates
|
||||
{
|
||||
[[EQNAccelerometroManager sharedInstance] addObserver:(id)self
|
||||
forKeyPath:@"currentLocation"
|
||||
options:NSKeyValueObservingOptionNew
|
||||
context:nil];
|
||||
|
||||
[[EQNAccelerometroManager sharedInstance] startUpdatingLocationBackground];
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
|
||||
{
|
||||
if ([keyPath isEqualToString:@"currentLocation"]) {
|
||||
NSLog(@"[EQNUser] currentLocation changed");
|
||||
// do some stuff
|
||||
if (self.lastPosition) {
|
||||
self.distanza = [self.lastPosition distanceFromLocation:[EQNAccelerometroManager sharedInstance].currentLocation];
|
||||
}
|
||||
self.lastPosition = [EQNAccelerometroManager sharedInstance].currentLocation;
|
||||
[self performServerSendLocation];
|
||||
|
||||
if (![EQNManager defaultManager].isBackground) {
|
||||
[[EQNAccelerometroManager sharedInstance] stopUpdatingLocation];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Helpers
|
||||
|
||||
/// user_id saved as a Number, but is used as a NSString
|
||||
- (NSString *)convertUserIdIntoString:(id)userId
|
||||
{
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
//
|
||||
// EQNUserData.swift
|
||||
// Earthquake Network
|
||||
//
|
||||
// Created by Andrea Busi on 04/11/22.
|
||||
// Copyright © 2022 Earthquake Network. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreLocation
|
||||
|
||||
|
||||
@objc class EQNUserData: NSObject {
|
||||
|
||||
@objc(sharedData) static let shared = EQNUserData()
|
||||
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
@objc
|
||||
var isFirstStart: Bool {
|
||||
firebaseToken == nil
|
||||
}
|
||||
|
||||
// MARK: - Firebase Token
|
||||
|
||||
@objc
|
||||
var firebaseToken: String? {
|
||||
UserDefaults.standard.object(forKey: UserDefaults.UserDataFirebaseToken) as? String
|
||||
}
|
||||
|
||||
@objc
|
||||
func saveFirebaseToken(_ token: String?) {
|
||||
if let token = token {
|
||||
UserDefaults.standard.set(token, forKey: UserDefaults.UserDataFirebaseToken)
|
||||
} else {
|
||||
UserDefaults.standard.removeObject(forKey: UserDefaults.UserDataFirebaseToken)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - User id
|
||||
|
||||
@objc
|
||||
var userId: String? {
|
||||
let userId = UserDefaults.standard.object(forKey: UserDefaults.UserDataUserId)
|
||||
|
||||
// nel corso delle versioni l'id è stato salvato in diversi modi
|
||||
// per evitare problemi, cerchiamo di convertirlo in modi diveri
|
||||
if let userId = userId as? String {
|
||||
return userId
|
||||
} else if let userId = userId as? Int {
|
||||
return "\(userId)"
|
||||
} else if let userId = userId as? NSNumber {
|
||||
return userId.stringValue
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@objc
|
||||
func saveUserId(_ userId: String) {
|
||||
UserDefaults.standard.set(userId, forKey: UserDefaults.UserDataUserId)
|
||||
}
|
||||
|
||||
// MARK: - Last location
|
||||
|
||||
@objc
|
||||
var lastLocation: CLLocation? {
|
||||
guard let encodedLocation = UserDefaults.appGroup?.object(forKey: UserDefaults.UserDataLastLocation) as? Data else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let location = try? NSKeyedUnarchiver.unarchivedObject(ofClass: CLLocation.self, from: encodedLocation)
|
||||
return location
|
||||
}
|
||||
|
||||
@objc
|
||||
func saveLastLocation(_ location: CLLocation) {
|
||||
guard let encodedLocation = try? NSKeyedArchiver.archivedData(withRootObject: location, requiringSecureCoding: false) else {
|
||||
return
|
||||
}
|
||||
UserDefaults.appGroup?.set(encodedLocation, forKey: UserDefaults.UserDataLastLocation)
|
||||
}
|
||||
|
||||
// MARK: - Seismic Networks
|
||||
|
||||
@objc
|
||||
func seismicNetworksSelected() -> [String] {
|
||||
if let savedNetworks = UserDefaults.standard.object(forKey: UserDefaults.UserDataSelectedSeismicNetworks) as? [String] {
|
||||
return savedNetworks
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
func saveSelectedSeismicNetworks(_ networks: [String]) {
|
||||
UserDefaults.standard.set(networks, forKey: UserDefaults.UserDataSelectedSeismicNetworks)
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
@objc
|
||||
func removeAllData() {
|
||||
UserDefaults.standard.removeObject(forKey: UserDefaults.UserDataFirebaseToken)
|
||||
UserDefaults.standard.removeObject(forKey: UserDefaults.UserDataUserId)
|
||||
UserDefaults.appGroup?.removeObject(forKey: UserDefaults.UserDataLastLocation)
|
||||
}
|
||||
}
|
||||
@@ -25,17 +25,13 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// Creates a string for a given time interval.
|
||||
/// Some examples: 1 hour ago, 25 minutes ago, 2 days ago
|
||||
/// @param timeDifference Time differnce
|
||||
/// @param timeDifference Time difference, in minutes
|
||||
+ (NSString *)formattedStringForTimeDifference:(NSInteger)timeDifference;
|
||||
|
||||
/// Clear a given string from unwanted characters
|
||||
/// @param messaggio Cleaned string
|
||||
+ (NSString *)clearStringMessaggi:(NSString *)messaggio;
|
||||
|
||||
/// Calculate time difference (in minutes) between the given date and the current timestamp
|
||||
/// @param date Difference (in minutes)
|
||||
+ (NSInteger)getDifferenceMinute:(NSDate *)date;
|
||||
|
||||
/// Store an array of custom objects to NSUserDefaults
|
||||
/// @param array Array to store
|
||||
/// @param keyName Key of NSUserDefault to use
|
||||
@@ -46,15 +42,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/// @param keyName Key of NSUserDefault to retrieve
|
||||
+ (nullable NSArray *)loadArrayOfClass:(Class)class fromUserDefaultsForKey:(NSString* )keyName;
|
||||
|
||||
/// Store a dictionary to NSUserDefaults
|
||||
/// @param dictionary Dictionary to store
|
||||
/// @param keyName Key of NSUserDefault to use
|
||||
+ (void)storeDictionary:(NSDictionary *)dictionary toUserDefaultForKey:(NSString *)keyName;
|
||||
|
||||
/// Retrieve a saved dictionary from NSUserDefaults.
|
||||
/// @param keyName Key of NSUserDefault to retrieve
|
||||
+ (nullable NSDictionary *)loadDictionaryFromUserDefaultsForKey:(NSString *)keyName;
|
||||
|
||||
/// Calculate impact time from a received push notification
|
||||
/// @param info Payload of a received notification
|
||||
/// @return Impact time
|
||||
|
||||
@@ -8,9 +8,11 @@
|
||||
|
||||
#import "EQNUtility.h"
|
||||
#import "EQNSegnalazione.h"
|
||||
#import "EQNSegnalazione.h"
|
||||
#import "EQNPastquakes.h"
|
||||
#import "EQNSisma.h"
|
||||
#if NOTIFICATION_CONTENT
|
||||
#import "EQNNotificationContent-Swift.h"
|
||||
#endif
|
||||
|
||||
@implementation EQNUtility
|
||||
|
||||
@@ -27,25 +29,18 @@
|
||||
NSString *format = @"";
|
||||
NSInteger finalValue = timeDifference;
|
||||
|
||||
if (timeDifference == 1) {
|
||||
format = NSLocalizedString(@"minutes_one", comment: nil);
|
||||
} else if (timeDifference < 60) {
|
||||
format = NSLocalizedString(@"minutes_other", comment: nil);
|
||||
} else if (timeDifference > 60 && timeDifference < 120) {
|
||||
// check for minutes, hours or days
|
||||
if (timeDifference < 60) {
|
||||
format = NSLocalizedString(@"manual_minutes_ago", nil);
|
||||
} else if (timeDifference < 1440) {
|
||||
finalValue = timeDifference / 60.0;
|
||||
format = NSLocalizedString(@"hours_one", comment: nil);
|
||||
} else if (timeDifference > 60 && timeDifference < 1440) {
|
||||
finalValue = timeDifference / 60.0;
|
||||
format = NSLocalizedString(@"hours_other", comment: nil);
|
||||
} else if (timeDifference > 1400 && timeDifference < 2800) {
|
||||
finalValue = timeDifference / 1400.0;
|
||||
format = NSLocalizedString(@"days_one", comment: nil);
|
||||
format = NSLocalizedString(@"manual_hours_ago", nil);
|
||||
} else {
|
||||
finalValue = timeDifference / 1400.0;
|
||||
format = NSLocalizedString(@"days_other", comment: nil);
|
||||
finalValue = timeDifference / 1440.0;
|
||||
format = NSLocalizedString(@"manual_days_ago", nil);
|
||||
}
|
||||
|
||||
return [NSString stringWithFormat:format, (long)finalValue];
|
||||
return [NSString localizedStringWithFormat:format, finalValue];
|
||||
}
|
||||
|
||||
+ (NSString *)clearStringMessaggi:(NSString *)messaggio
|
||||
@@ -61,18 +56,6 @@
|
||||
return clearString;
|
||||
}
|
||||
|
||||
+ (NSInteger)getDifferenceMinute:(NSDate *)date
|
||||
{
|
||||
NSDate *now = [NSDate date];
|
||||
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
|
||||
NSDateComponents *components = [calendar components:NSCalendarUnitMinute
|
||||
fromDate:date
|
||||
toDate:now
|
||||
options:0];
|
||||
|
||||
return components.minute;
|
||||
}
|
||||
|
||||
#pragma mark - Store/load data
|
||||
|
||||
+ (void)storeArray:(NSArray *)array toUserDefaultForKey:(NSString *)keyName
|
||||
@@ -80,11 +63,6 @@
|
||||
[self storeRootObject:array toUserDefaultForKey:keyName];
|
||||
}
|
||||
|
||||
+ (void)storeDictionary:(NSDictionary *)dictionary toUserDefaultForKey:(NSString *)keyName
|
||||
{
|
||||
[self storeRootObject:dictionary toUserDefaultForKey:keyName];
|
||||
}
|
||||
|
||||
+ (void)storeRootObject:(id)rootObject toUserDefaultForKey:(NSString *)keyName
|
||||
{
|
||||
NSError *error;
|
||||
@@ -119,27 +97,6 @@
|
||||
return array;
|
||||
}
|
||||
|
||||
+ (NSDictionary *)loadDictionaryFromUserDefaultsForKey:(NSString *)keyName
|
||||
{
|
||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||
NSData *data = [defaults objectForKey:keyName];
|
||||
if (!data) {
|
||||
NSLog(@"[EQNUtility] No dictionary saved for key '%@'", keyName);
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSError *error;
|
||||
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingFromData:data error:&error];
|
||||
unarchiver.requiresSecureCoding = NO;
|
||||
NSDictionary *dictionary = [unarchiver decodeObjectOfClass:[NSDictionary class] forKey:NSKeyedArchiveRootObjectKey];
|
||||
|
||||
if (error) {
|
||||
NSLog(@"[EQNUtility] Unable to unarchive object for key '%@' (error: %@)", keyName, error.localizedDescription);
|
||||
return nil;
|
||||
}
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
#pragma mark - Notifications
|
||||
|
||||
+ (nullable NSDate *)calculateUserSeismicTimestampFromUserInfo:(NSDictionary *)info
|
||||
@@ -151,6 +108,9 @@
|
||||
// ultima posizione nota dell'utente
|
||||
CLLocationManager *manager = [[CLLocationManager alloc] init];
|
||||
CLLocation *lastUserLocation = manager.location;
|
||||
if (lastUserLocation == nil) {
|
||||
lastUserLocation = EQNUserData.sharedData.lastLocation;
|
||||
}
|
||||
if (!lastUserLocation) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ class EQNMapAnnotationSeismic: NSObject, MKAnnotation {
|
||||
case "IGN": icon = "square"
|
||||
case "UASD", "BDTIM", "NCS": icon = "thick_star"
|
||||
case "RSPR": icon = "star6f"
|
||||
case "UOA": icon = "triangle"
|
||||
default: icon = ""
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
import Foundation
|
||||
import MapKit
|
||||
import Shogun
|
||||
|
||||
|
||||
@objc
|
||||
@@ -15,36 +16,63 @@ class EQNMapAnnotationUserReport: NSObject, MKAnnotation {
|
||||
|
||||
@objc var coordinate: CLLocationCoordinate2D
|
||||
@objc var title: String?
|
||||
@objc var image: UIImage?
|
||||
@objc var subtitle: String?
|
||||
@objc var timeDifference: String?
|
||||
|
||||
private let magnitude: Int
|
||||
var report: EQNSegnalazione?
|
||||
|
||||
// MARK: - Init
|
||||
|
||||
convenience init(report: EQNSegnalazione) {
|
||||
let coordinate = CLLocationCoordinate2D(latitude: report.coordinate.coordinate.latitude, longitude: report.coordinate.coordinate.longitude)
|
||||
let magnitude = report.intensity.rawValue
|
||||
let title = EQNUtility.formattedTimeDifference(from: report.date)
|
||||
let coordinate = report.coordinate.coordinate
|
||||
let magnitude = report.intensity
|
||||
|
||||
self.init(title: title, coordinate: coordinate, magnitude: magnitude)
|
||||
self.init(magnitude: magnitude, coordinate: coordinate)
|
||||
self.report = report
|
||||
self.timeDifference = EQNUtility.formattedTimeDifference(from: report.date)
|
||||
}
|
||||
|
||||
@objc init(title: String, coordinate: CLLocationCoordinate2D, magnitude: Int) {
|
||||
self.title = title
|
||||
self.image = Self.image(for: magnitude)
|
||||
@objc init(
|
||||
magnitude: Int,
|
||||
coordinate: CLLocationCoordinate2D
|
||||
) {
|
||||
self.magnitude = magnitude
|
||||
self.coordinate = coordinate
|
||||
super.init()
|
||||
self.title = Self.title(for: magnitude)
|
||||
self.subtitle = Self.subtitle(for: magnitude)
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
// MARK: - Helper
|
||||
|
||||
private static func image(for magnitute: Int) -> UIImage? {
|
||||
switch magnitute {
|
||||
case 1: return UIImage(named: "star_report_green")
|
||||
case 2: return UIImage(named: "star_report_yellow")
|
||||
case 3: return UIImage(named: "star_report_red")
|
||||
default: return nil
|
||||
@objc(imageWithHeight:)
|
||||
func image(
|
||||
with height: CGFloat
|
||||
) -> UIImage? {
|
||||
guard let color = UIColor(named: "Mercalli \(magnitude)") else {
|
||||
print("[EQNMapAnnotationUserReport] Unable to get a color for magnitude: \(magnitude)")
|
||||
return nil
|
||||
}
|
||||
|
||||
return UIImage.circle(diameter: height, color: color, borderWidth: 1.0, borderColor: .black)
|
||||
}
|
||||
|
||||
private static func title(for magnitude: Int) -> String {
|
||||
let grade = magnitude / 10
|
||||
let roman = grade.romanNumber()
|
||||
return String(format: NSLocalizedString("mercalli_intensity", comment: ""), roman)
|
||||
}
|
||||
|
||||
private static func subtitle(for magnitude: Int) -> String {
|
||||
let grade = magnitude / 10
|
||||
let roman = grade.romanNumber()
|
||||
let string = NSLocalizedString("mercalli_\(roman)", comment: "")
|
||||
// strings are in the format like "II - Appena percepito"
|
||||
// so we are going to remove the "II" from the string
|
||||
let components = string.components(separatedBy: " - ")
|
||||
if components.count > 1 {
|
||||
return components[1]
|
||||
}
|
||||
return string
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,16 +31,16 @@
|
||||
self = [super init];
|
||||
if (self) {
|
||||
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
|
||||
self.isAbilitato = [userDefaults boolForKey:NOTIFICHE_ALLERA_SISMICA_ABILITATO];
|
||||
self.sismiDaNotificare = [userDefaults objectForKey:NOTIFICHE_ALLERA_SISMICA_SISMI_DA_NOTIFICARE];
|
||||
self.raggioSismiLievi = [userDefaults objectForKey:NOTIFICHE_ALLERA_SISMICA_RAGGIO_SISMI_LIEVI];
|
||||
self.raggioSismiForti = [userDefaults objectForKey:NOTIFICHE_ALLERA_SISMICA_RAGGIO_SISMI_FORTI];
|
||||
self.isintervalloAllarme = [userDefaults boolForKey:NOTIFICHE_ALLERA_SISMICA_ABILITA_INTERVALLO];
|
||||
self.oraioInizio = [userDefaults objectForKey:NOTIFICHE_ALLERA_SISMICA_ORA_INIZIO];
|
||||
self.orarioFine = [userDefaults objectForKey:NOTIFICHE_ALLERA_SISMICA_ORA_FINE];
|
||||
self.isAbilitato = [userDefaults boolForKey:NSUserDefaults.AllertaSismicaAbilitato];
|
||||
self.sismiDaNotificare = [userDefaults objectForKey:NSUserDefaults.AllertaSismicaSismiDaNotificare];
|
||||
self.raggioSismiLievi = [userDefaults objectForKey:NSUserDefaults.AllertaSismicaRaggioSismiLievi];
|
||||
self.raggioSismiForti = [userDefaults objectForKey:NSUserDefaults.AllertaSismicaRaggioSismiForti];
|
||||
self.isintervalloAllarme = [userDefaults boolForKey:NSUserDefaults.AllertaSismicaAbilitaIntervallo];
|
||||
self.oraioInizio = [userDefaults objectForKey:NSUserDefaults.AllertaSismicaOraInizio];
|
||||
self.orarioFine = [userDefaults objectForKey:NSUserDefaults.AllertaSismicaOraFine];
|
||||
|
||||
NSUserDefaults *sharedDefaults = [[NSUserDefaults alloc] initWithSuiteName:EQNUserDefaultAppGroupSuite];
|
||||
self.isCriticalAlertsEnabled = [sharedDefaults boolForKey:NOTIFICHE_ALLERA_SISMICA_CRITICAL_ALERTS];
|
||||
NSUserDefaults *sharedDefaults = [NSUserDefaults appGroupUserDefaults];
|
||||
self.isCriticalAlertsEnabled = [sharedDefaults boolForKey:NSUserDefaults.AllertaSismicaCriticalAlerts];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -50,16 +50,16 @@
|
||||
- (void)saveUserInfo
|
||||
{
|
||||
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
|
||||
[userDefaults setBool:self.isAbilitato forKey:NOTIFICHE_ALLERA_SISMICA_ABILITATO];
|
||||
[userDefaults setObject:self.sismiDaNotificare forKey:NOTIFICHE_ALLERA_SISMICA_SISMI_DA_NOTIFICARE];
|
||||
[userDefaults setObject:self.raggioSismiLievi forKey:NOTIFICHE_ALLERA_SISMICA_RAGGIO_SISMI_LIEVI];
|
||||
[userDefaults setObject:self.raggioSismiForti forKey:NOTIFICHE_ALLERA_SISMICA_RAGGIO_SISMI_FORTI];
|
||||
[userDefaults setBool:self.isintervalloAllarme forKey:NOTIFICHE_ALLERA_SISMICA_ABILITA_INTERVALLO];
|
||||
[userDefaults setObject:self.oraioInizio forKey:NOTIFICHE_ALLERA_SISMICA_ORA_INIZIO];
|
||||
[userDefaults setObject:self.orarioFine forKey:NOTIFICHE_ALLERA_SISMICA_ORA_FINE];
|
||||
[userDefaults setBool:self.isAbilitato forKey:NSUserDefaults.AllertaSismicaAbilitato];
|
||||
[userDefaults setObject:self.sismiDaNotificare forKey:NSUserDefaults.AllertaSismicaSismiDaNotificare];
|
||||
[userDefaults setObject:self.raggioSismiLievi forKey:NSUserDefaults.AllertaSismicaRaggioSismiLievi];
|
||||
[userDefaults setObject:self.raggioSismiForti forKey:NSUserDefaults.AllertaSismicaRaggioSismiForti];
|
||||
[userDefaults setBool:self.isintervalloAllarme forKey:NSUserDefaults.AllertaSismicaAbilitaIntervallo];
|
||||
[userDefaults setObject:self.oraioInizio forKey:NSUserDefaults.AllertaSismicaOraInizio];
|
||||
[userDefaults setObject:self.orarioFine forKey:NSUserDefaults.AllertaSismicaOraFine];
|
||||
|
||||
NSUserDefaults *sharedDefaults = [[NSUserDefaults alloc] initWithSuiteName:EQNUserDefaultAppGroupSuite];
|
||||
[sharedDefaults setBool:self.isCriticalAlertsEnabled forKey:NOTIFICHE_ALLERA_SISMICA_CRITICAL_ALERTS];
|
||||
NSUserDefaults *sharedDefaults = [NSUserDefaults appGroupUserDefaults];
|
||||
[sharedDefaults setBool:self.isCriticalAlertsEnabled forKey:NSUserDefaults.AllertaSismicaCriticalAlerts];
|
||||
}
|
||||
|
||||
#pragma mark - Class
|
||||
|
||||
@@ -31,13 +31,13 @@
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.isAbilitato = [[NSUserDefaults standardUserDefaults] boolForKey:NOTIFICHE_ATTIVA_RETI_SISMICHE];
|
||||
self.isAbilitaVicini = [[NSUserDefaults standardUserDefaults] boolForKey:NOTIFICHE_ATTIVA_RETI_SISMICHE_VICINE];
|
||||
self.isTerremortiForti = [[NSUserDefaults standardUserDefaults] boolForKey:NOTIFICHE_ATTIVA_RETI_TERREMOTI_FORTI];
|
||||
self.distanzaPosizione = [[NSUserDefaults standardUserDefaults] objectForKey:NOTIFICHE_DISTANZA_POSIZIONE_RETI_SISMICHE];
|
||||
self.energiaSisma = [[NSUserDefaults standardUserDefaults] objectForKey:NOTIFICHE_ATTIVA_RETI_ENERGIA_SISMI];
|
||||
self.energiaTerremotiForti = [[NSUserDefaults standardUserDefaults] objectForKey:NOTIFICHE_ATTIVA_RETI_ENERGIA_FORTI];
|
||||
self.listaEnti = [EQNUtility loadArrayOfClass:[NSString class] fromUserDefaultsForKey:NOTIFICHE_ATTIVA_RETI_LISTA_ENTI];
|
||||
self.isAbilitato = [[NSUserDefaults standardUserDefaults] boolForKey:NSUserDefaults.NotificheRetiSismicheAbilitato];
|
||||
self.isAbilitaVicini = [[NSUserDefaults standardUserDefaults] boolForKey:NSUserDefaults.NotificheRetiSismicheViciniAbilitato];
|
||||
self.isTerremortiForti = [[NSUserDefaults standardUserDefaults] boolForKey:NSUserDefaults.NotificheRetiSismicheTerremotiFortiAbilitato];
|
||||
self.distanzaPosizione = [[NSUserDefaults standardUserDefaults] objectForKey:NSUserDefaults.NotificheRetiSismicheDistanzaPosizione];
|
||||
self.energiaSisma = [[NSUserDefaults standardUserDefaults] objectForKey:NSUserDefaults.NotificheRetiSismicheEnergiaSisma];
|
||||
self.energiaTerremotiForti = [[NSUserDefaults standardUserDefaults] objectForKey:NSUserDefaults.NotificheRetiSismicheEnergiaTerremotiForti];
|
||||
self.listaEnti = [EQNUtility loadArrayOfClass:[NSString class] fromUserDefaultsForKey:NSUserDefaults.NotificheRetiSismicheListaEnti];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -46,13 +46,13 @@
|
||||
|
||||
- (void)saveUserInfo
|
||||
{
|
||||
[[NSUserDefaults standardUserDefaults] setObject:self.distanzaPosizione forKey:NOTIFICHE_DISTANZA_POSIZIONE_RETI_SISMICHE];
|
||||
[[NSUserDefaults standardUserDefaults] setObject:self.energiaSisma forKey:NOTIFICHE_ATTIVA_RETI_ENERGIA_SISMI];
|
||||
[[NSUserDefaults standardUserDefaults] setObject:self.energiaTerremotiForti forKey:NOTIFICHE_ATTIVA_RETI_ENERGIA_FORTI];
|
||||
[[NSUserDefaults standardUserDefaults] setBool:self.isAbilitato forKey:NOTIFICHE_ATTIVA_RETI_SISMICHE];
|
||||
[[NSUserDefaults standardUserDefaults] setBool:self.isAbilitaVicini forKey:NOTIFICHE_ATTIVA_RETI_SISMICHE_VICINE];
|
||||
[[NSUserDefaults standardUserDefaults] setBool:self.isTerremortiForti forKey:NOTIFICHE_ATTIVA_RETI_TERREMOTI_FORTI];
|
||||
[EQNUtility storeArray:self.listaEnti toUserDefaultForKey:NOTIFICHE_ATTIVA_RETI_LISTA_ENTI];
|
||||
[[NSUserDefaults standardUserDefaults] setBool:self.isAbilitato forKey:NSUserDefaults.NotificheRetiSismicheAbilitato];
|
||||
[[NSUserDefaults standardUserDefaults] setBool:self.isAbilitaVicini forKey:NSUserDefaults.NotificheRetiSismicheViciniAbilitato];
|
||||
[[NSUserDefaults standardUserDefaults] setBool:self.isTerremortiForti forKey:NSUserDefaults.NotificheRetiSismicheTerremotiFortiAbilitato];
|
||||
[[NSUserDefaults standardUserDefaults] setObject:self.distanzaPosizione forKey:NSUserDefaults.NotificheRetiSismicheDistanzaPosizione];
|
||||
[[NSUserDefaults standardUserDefaults] setObject:self.energiaSisma forKey:NSUserDefaults.NotificheRetiSismicheEnergiaSisma];
|
||||
[[NSUserDefaults standardUserDefaults] setObject:self.energiaTerremotiForti forKey:NSUserDefaults.NotificheRetiSismicheEnergiaTerremotiForti];
|
||||
[EQNUtility storeArray:self.listaEnti toUserDefaultForKey:NSUserDefaults.NotificheRetiSismicheListaEnti];
|
||||
[[NSUserDefaults standardUserDefaults] synchronize];
|
||||
}
|
||||
|
||||
|
||||
@@ -29,8 +29,8 @@
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.isAbilitato = [[NSUserDefaults standardUserDefaults] boolForKey:NOTIFICHE_SU_ATTIVA_SEGNALAZIONE_UTENTE];
|
||||
self.distanzaPosizione = [[NSUserDefaults standardUserDefaults] objectForKey:NOTIFICHE_SU_DISTANZA_POSIZIONE];
|
||||
self.isAbilitato = [[NSUserDefaults standardUserDefaults] boolForKey:NSUserDefaults.NotificheSegnalazioniUtenteAbilitato];
|
||||
self.distanzaPosizione = [[NSUserDefaults standardUserDefaults] objectForKey:NSUserDefaults.NotificheSegnalazioniUtenteDistanzaPosizione];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -39,8 +39,8 @@
|
||||
|
||||
- (void)saveUserInfo
|
||||
{
|
||||
[[NSUserDefaults standardUserDefaults] setObject:self.distanzaPosizione forKey:NOTIFICHE_SU_DISTANZA_POSIZIONE];
|
||||
[[NSUserDefaults standardUserDefaults] setBool:self.isAbilitato forKey:NOTIFICHE_SU_ATTIVA_SEGNALAZIONE_UTENTE];
|
||||
[[NSUserDefaults standardUserDefaults] setBool:self.isAbilitato forKey:NSUserDefaults.NotificheSegnalazioniUtenteAbilitato];
|
||||
[[NSUserDefaults standardUserDefaults] setObject:self.distanzaPosizione forKey:NSUserDefaults.NotificheSegnalazioniUtenteDistanzaPosizione];
|
||||
[[NSUserDefaults standardUserDefaults] synchronize];
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -5,9 +5,9 @@
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "250",
|
||||
"green" : "192",
|
||||
"red" : "129"
|
||||
"blue" : "194",
|
||||
"green" : "194",
|
||||
"red" : "194"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
+3
-3
@@ -5,9 +5,9 @@
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xCA",
|
||||
"green" : "0xCA",
|
||||
"red" : "0xCA"
|
||||
"blue" : "130",
|
||||
"green" : "212",
|
||||
"red" : "148"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
+3
-3
@@ -5,9 +5,9 @@
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xAA",
|
||||
"green" : "0xAA",
|
||||
"red" : "0xAA"
|
||||
"blue" : "173",
|
||||
"green" : "160",
|
||||
"red" : "254"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
+3
-3
@@ -5,9 +5,9 @@
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x51",
|
||||
"green" : "0x90",
|
||||
"red" : "0x00"
|
||||
"blue" : "177",
|
||||
"green" : "231",
|
||||
"red" : "255"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.792",
|
||||
"green" : "0.792",
|
||||
"red" : "0.792"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.667",
|
||||
"green" : "0.667",
|
||||
"red" : "0.667"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user