Compare commits
136 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bdfcb7a5c4 | |||
| 40fcb4707f | |||
| 37f9a856b1 | |||
| f42b9f1b53 | |||
| 4fd9966435 | |||
| df2c0a94a4 | |||
| cdd1a8d875 | |||
| 31f1cb5f35 | |||
| d426f15c8e | |||
| 037a74061d | |||
| ea6172226d | |||
| ec94db29b9 | |||
| 91a9bce03c | |||
| f54f4a2312 | |||
| a959df7cd9 | |||
| e95a93ff2c | |||
| b7c1f7379d | |||
| 0f71e0fea9 | |||
| 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 | |||
| 73caf9647c | |||
| 8700e200f9 | |||
| e99845ff1b | |||
| a0161e8f4c | |||
| 1b9944a7ca | |||
| 4c00e4ef6a | |||
| 63592e6cfb | |||
| 2877dff23c | |||
| f2386a1abb | |||
| 5e4a500f03 | |||
| 2b8f2db7c5 | |||
| 11d994696d | |||
| cd6e20c1b2 | |||
| af5371571c | |||
| 6291b22df0 | |||
| 2a7cfd3079 |
@@ -1,3 +1,6 @@
|
||||
# MacOS files
|
||||
.DS_Store
|
||||
|
||||
# Exclude Pods
|
||||
Sources/Pods
|
||||
|
||||
|
||||
@@ -1,5 +1,61 @@
|
||||
# Changelog
|
||||
|
||||
## Versione 5.7
|
||||
- Aumentato target ad iOS 13
|
||||
- Disattivata logica calibrazione/monitoraggio
|
||||
- Aggiunto invio posizione in background
|
||||
|
||||
## 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
|
||||
|
||||
## Versione 5.1
|
||||
|
||||
### Build (91)
|
||||
- Corretto problema con selezioni reti EMSC
|
||||
|
||||
### Build (90)
|
||||
- Rimozione notifiche dello stesso tipo
|
||||
|
||||
## Versione 5.0.3
|
||||
|
||||
### Build (89)
|
||||
|
||||
@@ -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,20 +14,21 @@
|
||||
"sound": "alert_star_trek.wav"
|
||||
},
|
||||
"counter": 10,
|
||||
"datetime": "2021-04-15 21:34:50",
|
||||
"datetime": "2022-06-23 10:10:00",
|
||||
"delay": 4,
|
||||
"detection_latitude": "37.983810",
|
||||
"detection_longitude": "23.727539",
|
||||
"gcm.message_id": "1614708857742608",
|
||||
"google.c.a.e": 1,
|
||||
"google.c.sender.id": "899482329945",
|
||||
"intensity": 2,
|
||||
"latitude": "37.683810",
|
||||
"location": "150 km (Test)",
|
||||
"longitude": "23.327539",
|
||||
"peak": "-1",
|
||||
"randcode": 0,
|
||||
"test": 1,
|
||||
"latitude": "38.19",
|
||||
"location": "15 χλμ από το Αίγιο",
|
||||
"longitude": "22.26",
|
||||
"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,19 +14,20 @@
|
||||
"sound": "alert_star_trek.wav"
|
||||
},
|
||||
"counter": 10,
|
||||
"datetime": "2021-04-16 12:41:20",
|
||||
"datetime": "2022-06-23 07:55:00",
|
||||
"delay": 4,
|
||||
"detection_latitude": "37.3229978",
|
||||
"detection_longitude": "-122.0321823",
|
||||
"gcm.message_id": "1614708857742608",
|
||||
"google.c.a.e": 1,
|
||||
"google.c.sender.id": "899482329945",
|
||||
"intensity": 2,
|
||||
"latitude": "37.9229978",
|
||||
"location": "150 km (Test)",
|
||||
"longitude": "-121.0321823",
|
||||
"peak": "-1",
|
||||
"randcode": 0,
|
||||
"test": 1,
|
||||
"latitude": "35.15",
|
||||
"location": "14 km from California City",
|
||||
"longitude": "-117.78",
|
||||
"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,19 +14,20 @@
|
||||
"sound": "alert_star_trek.wav"
|
||||
},
|
||||
"counter": 10,
|
||||
"datetime": "2021-04-15 20:39:00",
|
||||
"datetime": "2022-06-23 10:19:00",
|
||||
"delay": 4,
|
||||
"detection_latitude": "-34.603722",
|
||||
"detection_longitude": "-58.381592",
|
||||
"gcm.message_id": "1614708857742608",
|
||||
"google.c.a.e": 1,
|
||||
"google.c.sender.id": "899482329945",
|
||||
"intensity": 2,
|
||||
"latitude": "-34.103722",
|
||||
"location": "150 km (Test)",
|
||||
"longitude": "-58.781592",
|
||||
"peak": "-1",
|
||||
"randcode": 0,
|
||||
"test": 1,
|
||||
"latitude": "-32.57",
|
||||
"location": "5 km de Las Cujas",
|
||||
"longitude": "-71.46",
|
||||
"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,19 +14,20 @@
|
||||
"sound": "alert_star_trek.wav"
|
||||
},
|
||||
"counter": 10,
|
||||
"datetime": "2021-04-15 21:37:40",
|
||||
"datetime": "2022-06-23 10:07:00",
|
||||
"delay": 4,
|
||||
"detection_latitude": "48.856614",
|
||||
"detection_longitude": "2.8522219",
|
||||
"gcm.message_id": "1614708857742608",
|
||||
"google.c.a.e": 1,
|
||||
"google.c.sender.id": "899482329945",
|
||||
"intensity": 2,
|
||||
"latitude": "48.456614",
|
||||
"location": "150 km (Test)",
|
||||
"longitude": "2.3522219",
|
||||
"peak": "-1",
|
||||
"randcode": 0,
|
||||
"test": 1,
|
||||
"latitude": "49.77",
|
||||
"location": "15 km de Dieppe",
|
||||
"longitude": "1.05",
|
||||
"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,19 +14,20 @@
|
||||
"sound": "alert_star_trek.wav"
|
||||
},
|
||||
"counter": 10,
|
||||
"datetime": "2021-04-15 21:39:50",
|
||||
"datetime": "2022-06-23 10:02:00",
|
||||
"delay": 4,
|
||||
"detection_latitude": "45.813177",
|
||||
"detection_longitude": "15.977048",
|
||||
"gcm.message_id": "1614708857742608",
|
||||
"google.c.a.e": 1,
|
||||
"google.c.sender.id": "899482329945",
|
||||
"intensity": 2,
|
||||
"latitude": "45.413177",
|
||||
"location": "150 km (Test)",
|
||||
"longitude": "15.677048",
|
||||
"peak": "-1",
|
||||
"randcode": 0,
|
||||
"test": 1,
|
||||
"latitude": "45.09",
|
||||
"location": "5 km od Novog",
|
||||
"longitude": "14.87",
|
||||
"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,19 +14,20 @@
|
||||
"sound": "alert_star_trek.wav"
|
||||
},
|
||||
"counter": 10,
|
||||
"datetime": "2021-04-15 21:42:40",
|
||||
"datetime": "2022-06-23 10:13:00",
|
||||
"delay": 4,
|
||||
"detection_latitude": "-2.548926",
|
||||
"detection_longitude": "118.0148634",
|
||||
"gcm.message_id": "1614708857742608",
|
||||
"google.c.a.e": 1,
|
||||
"google.c.sender.id": "899482329945",
|
||||
"intensity": 2,
|
||||
"latitude": "-2.948926",
|
||||
"location": "150 km (Test)",
|
||||
"longitude": "118.6148634",
|
||||
"peak": "-1",
|
||||
"randcode": 0,
|
||||
"test": 1,
|
||||
"latitude": "-7.38",
|
||||
"location": "35 km dari Sindanbarang",
|
||||
"longitude": "106.83",
|
||||
"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,19 +14,20 @@
|
||||
"sound": "alert_star_trek.wav"
|
||||
},
|
||||
"counter": 10,
|
||||
"datetime": "2021-04-15 20:32:20",
|
||||
"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",
|
||||
"intensity": 2,
|
||||
"latitude": "45.164664",
|
||||
"location": "150 km (Test)",
|
||||
"longitude": "9.788540",
|
||||
"peak": "-1",
|
||||
"randcode": 0,
|
||||
"test": 1,
|
||||
"latitude": "42.958",
|
||||
"location": "2 km da Foligno",
|
||||
"longitude": "12.702",
|
||||
"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,19 +14,20 @@
|
||||
"sound": "alert_star_trek.wav"
|
||||
},
|
||||
"counter": 10,
|
||||
"datetime": "2021-04-16 12:39:40",
|
||||
"datetime": "2022-06-23 10:16:00",
|
||||
"delay": 4,
|
||||
"detection_latitude": "19.432608",
|
||||
"detection_longitude": "-99.133209",
|
||||
"gcm.message_id": "1614708857742608",
|
||||
"google.c.a.e": 1,
|
||||
"google.c.sender.id": "899482329945",
|
||||
"intensity": 2,
|
||||
"latitude": "19.932608",
|
||||
"location": "150 km (Test)",
|
||||
"longitude": "-98.033209",
|
||||
"peak": "-1",
|
||||
"randcode": 0,
|
||||
"test": 1,
|
||||
"latitude": "18.72",
|
||||
"location": "45 km de Puebla",
|
||||
"longitude": "-97.90",
|
||||
"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,20 +14,21 @@
|
||||
"sound": "alert_star_trek.wav"
|
||||
},
|
||||
"counter": 10,
|
||||
"datetime": "2021-04-15 21:44:30",
|
||||
"datetime": "2022-06-23 10:22:00",
|
||||
"delay": 4,
|
||||
"detection_latitude": "41.008238",
|
||||
"detection_longitude": "28.978359",
|
||||
"gcm.message_id": "1614708857742608",
|
||||
"google.c.a.e": 1,
|
||||
"google.c.sender.id": "899482329945",
|
||||
"intensity": 2,
|
||||
"latitude": "41.608238",
|
||||
"location": "150 km (Test)",
|
||||
"longitude": "28.678359",
|
||||
"peak": "-1",
|
||||
"randcode": 0,
|
||||
"test": 1,
|
||||
"latitude": "40.33",
|
||||
"location": "Bandırma'ya 25 km",
|
||||
"longitude": "27.66",
|
||||
"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,42 +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)
|
||||
|
||||
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" : "c164595fdd5d0771a6a24cbff85a7582f0f07311",
|
||||
"version" : "1.3.0"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 2
|
||||
}
|
||||
@@ -8,21 +8,22 @@
|
||||
|
||||
#import "AppDelegate.h"
|
||||
#import "Costanti.h"
|
||||
#import "ServerRequest.h"
|
||||
#import "EQNGeneratoreURLServer.h"
|
||||
#import "EQNUser.h"
|
||||
#import "EQNAccelerometroManager.h"
|
||||
#import "EQNManager.h"
|
||||
#import "EQNUtility.h"
|
||||
#import "EQNAllertaSismica.h"
|
||||
#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,29 +44,25 @@
|
||||
}];
|
||||
#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];
|
||||
// schedule background tasks
|
||||
[BackgroundTaskManager.shared registerTasks];
|
||||
[BackgroundTaskManager.shared scheduleUpdateServerPosition];
|
||||
|
||||
// add some generic logs for Crashlytics
|
||||
NSString *language = [[NSLocale preferredLanguages] firstObject];
|
||||
[[FIRCrashlytics crashlytics] setCustomValue:language forKey:@"lang"];
|
||||
|
||||
[self configurePushNotifications];
|
||||
|
||||
[application setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
|
||||
[application registerForRemoteNotifications];
|
||||
|
||||
return YES;
|
||||
@@ -86,12 +83,14 @@
|
||||
- (void)applicationDidEnterBackground:(UIApplication *)application {
|
||||
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
|
||||
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
|
||||
[[EQNManager defaultManager] avviaManager];
|
||||
[[EQNAccelerometroManager sharedInstance] startUpdatingLocationBackground];
|
||||
|
||||
NSUInteger counter = [[NSUserDefaults standardUserDefaults] integerForKey:EQNUserDefaultProDiscountOpenCounter];
|
||||
// disabilitiamo logica calibrazione/monitoraggio perchè attualmente non utilizzata dal server
|
||||
//[[EQNManager defaultManager] avviaManager];
|
||||
//[[EQNAccelerometroManager sharedInstance] startUpdatingLocationBackground];
|
||||
|
||||
NSUInteger counter = [[NSUserDefaults standardUserDefaults] integerForKey:NSUserDefaults.UserDataProDiscountOpenCounter];
|
||||
counter += 1;
|
||||
[[NSUserDefaults standardUserDefaults] setInteger:counter forKey:EQNUserDefaultProDiscountOpenCounter];
|
||||
[[NSUserDefaults standardUserDefaults] setInteger:counter forKey:NSUserDefaults.UserDataProDiscountOpenCounter];
|
||||
}
|
||||
|
||||
|
||||
@@ -125,8 +124,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 +148,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,37 +158,35 @@
|
||||
// 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();
|
||||
}
|
||||
|
||||
- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
|
||||
{
|
||||
NSURL *url = [EQNGeneratoreURLServer urlPosizione];
|
||||
[[ServerRequest defaultServerConnectionSingleton] inviaInformazioniAlServerWithURL:url richiesta:EQNTipoChiamataPosizione success:^(id result) {
|
||||
completionHandler(UIBackgroundFetchResultNewData);
|
||||
} failure:^(NSError *error) {
|
||||
completionHandler(UIBackgroundFetchResultFailed);
|
||||
}];
|
||||
}
|
||||
|
||||
#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:NOTIFICHE_RETE_SMARTPHONE_DATA_NOTIFICA];
|
||||
|
||||
[EQNUtility storeDictionary:userInfo toUserDefaultForKey:NOTIFICHE_RETE_SMARTPHONE_DIZIONARIO_NOTIFICA];
|
||||
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 +194,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,98 @@
|
||||
//
|
||||
// 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"
|
||||
/// Se `true` visualizza il tempo nelle annotazioni della mappa segnalazioni utente
|
||||
static let UserReportExpandedView = "EQNData.UserReportExpandedView"
|
||||
/// Se `true` visualizza le opzioni nella singole card in reti sismiche
|
||||
static let AlertsShowCardOptions = "EQNetwork.AlertsShowAllCards"
|
||||
|
||||
// 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"
|
||||
|
||||
// Filtri sezioni reti sismiche
|
||||
static let SeismicMagnitudoMinima = "EQN_MAGNITUDO_MINIMA"
|
||||
static let SeismicDistanzaMassima = "EQN_DISTANZA_MASSIMA"
|
||||
static let SeismicEtaMassima = "EQN_ETA_MASSIMA"
|
||||
static let SeismicSismiFortiAbilitati = "EQN_SISMI_FORTI_ABILITATI"
|
||||
static let SeismicSismiForti = "EQN_SISMI_FORTI"
|
||||
static let SeismicSismiQualsiasiMagnitudo = "EQN_SISMI_QUALSIASI_MAGNITUDO"
|
||||
static let SeismicModificaImpostazioni = "EQN_SISMI_MODIFICA_IMPOSTAZIONI"
|
||||
}
|
||||
|
||||
extension UserDefaults {
|
||||
|
||||
/// Get a generic stored values
|
||||
/// - Parameters:
|
||||
/// - key: A key in the current user‘s defaults database.
|
||||
/// - defaultValue: Default value to return if the key is not found
|
||||
/// - Returns: The object associated with the specified key, or `defaultValue` if the key was not found.
|
||||
func object<T>(forKey key: String, or defaultValue: T) -> T {
|
||||
if let value = UserDefaults.standard.object(forKey: key) as? T {
|
||||
return value
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -106,6 +104,10 @@ typedef NS_ENUM(NSInteger, AllerteTableRow) {
|
||||
self.title = [NSLocalizedString(@"tab_network", nil) capitalizedString];
|
||||
self.tableView.estimatedRowHeight = 200.0;
|
||||
self.tableView.rowHeight = UITableViewAutomaticDimension;
|
||||
|
||||
if (EQNBackgroundPositionDebugHelper.shared.isEnabled) {
|
||||
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemBookmarks target:self action:@selector(backgroundPositionDebugTapped:)];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)refreshUI
|
||||
@@ -113,18 +115,22 @@ 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"];
|
||||
|
||||
NSDate *date = [[NSUserDefaults standardUserDefaults] objectForKey:NOTIFICHE_RETE_SMARTPHONE_DATA_NOTIFICA];
|
||||
if (date) {
|
||||
if ([EQNUtility getDifferenceMinute:date] < TEMPO_VISUALIZZAZIONE_NOTIFICA)
|
||||
self.isNotificaAttiva = YES;
|
||||
else{
|
||||
self.isNotificaAttiva = NO;
|
||||
[[NSUserDefaults standardUserDefaults] removeObjectForKey:NOTIFICHE_RETE_SMARTPHONE_DATA_NOTIFICA];
|
||||
[[NSUserDefaults standardUserDefaults] synchronize];
|
||||
// controlliamo se c'è una notifica in tempo reale da mostrare
|
||||
EQNRealtimePushNotification *notification = [EQNRealtimePushNotification storedNotification];
|
||||
if (notification) {
|
||||
self.isNotificaAttiva = YES;
|
||||
|
||||
// mostriamo la schermata solo se il countdown non è a zero
|
||||
if (![notification isCountdownExpired]) {
|
||||
RealtimeAlertViewController *controller = [[RealtimeAlertViewController alloc] initWithNotification:notification];
|
||||
controller.modalInPresentation = YES;
|
||||
[self presentViewController:controller animated:YES completion:nil];
|
||||
}
|
||||
} else {
|
||||
[self resetRealtimeAlert];
|
||||
}
|
||||
|
||||
[self.tableItems removeAllObjects];
|
||||
@@ -152,6 +158,12 @@ typedef NS_ENUM(NSInteger, AllerteTableRow) {
|
||||
[self.tableView reloadData];
|
||||
}
|
||||
|
||||
- (void)resetRealtimeAlert
|
||||
{
|
||||
[EQNRealtimePushNotification removeStoredNotification];
|
||||
self.isNotificaAttiva = NO;
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
|
||||
- (IBAction)refreshDataTapped:(id)sender
|
||||
@@ -162,20 +174,21 @@ 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];
|
||||
}
|
||||
|
||||
- (IBAction)backgroundPositionDebugTapped:(id)sender
|
||||
{
|
||||
EQNBackgroundPositionDebugViewController *controller = [[EQNBackgroundPositionDebugViewController alloc] init];
|
||||
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:controller];
|
||||
[self presentViewController:navController animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)actionCloseNotification
|
||||
{
|
||||
[[NSUserDefaults standardUserDefaults] removeObjectForKey:NOTIFICHE_RETE_SMARTPHONE_DATA_NOTIFICA];
|
||||
[[NSUserDefaults standardUserDefaults] removeObjectForKey:NOTIFICHE_RETE_SMARTPHONE_DIZIONARIO_NOTIFICA];
|
||||
[[NSUserDefaults standardUserDefaults] synchronize];
|
||||
self.isNotificaAttiva = NO;
|
||||
|
||||
[self resetRealtimeAlert];
|
||||
[self.tableView reloadData];
|
||||
}
|
||||
|
||||
@@ -258,8 +271,8 @@ typedef NS_ENUM(NSInteger, AllerteTableRow) {
|
||||
} else if (tableRow == AllerteTableRowSismiRilevati) {
|
||||
if (self.isNotificaAttiva) {
|
||||
AlertsSeismicNotificationExpandedTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"SeismicNotificationExpandedCell" forIndexPath:indexPath];
|
||||
NSDictionary *info = [EQNUtility loadDictionaryFromUserDefaultsForKey:NOTIFICHE_RETE_SMARTPHONE_DIZIONARIO_NOTIFICA];
|
||||
cell.notification = info;
|
||||
EQNRealtimePushNotification *notification = [EQNRealtimePushNotification storedNotification];
|
||||
cell.notification = notification;
|
||||
|
||||
__weak AllerteViewController *weakSelf = self;
|
||||
cell.onTapClose = ^{
|
||||
@@ -318,10 +331,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;
|
||||
@@ -340,9 +349,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: "")
|
||||
}
|
||||
}
|
||||
+40
-67
@@ -8,14 +8,14 @@
|
||||
|
||||
import UIKit
|
||||
import MapKit
|
||||
import Shogun
|
||||
|
||||
class AlertsSeismicNotificationExpandedTableViewCell: EQNBaseTableViewCell, MKMapViewDelegate {
|
||||
|
||||
typealias DefaultCompletion = () -> Void
|
||||
|
||||
@objc var notification: [String: Any]? {
|
||||
@objc var notification: EQNRealtimePushNotification? {
|
||||
didSet {
|
||||
startCountdown()
|
||||
updateUI()
|
||||
}
|
||||
}
|
||||
@@ -29,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 {
|
||||
@@ -52,6 +53,7 @@ class AlertsSeismicNotificationExpandedTableViewCell: EQNBaseTableViewCell, MKMa
|
||||
super.awakeFromNib()
|
||||
|
||||
localizeUI()
|
||||
setUI()
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
@@ -64,15 +66,11 @@ class AlertsSeismicNotificationExpandedTableViewCell: EQNBaseTableViewCell, MKMa
|
||||
descriptionLabel.text = NSLocalizedString("map_smartphone_magnitude", comment: "")
|
||||
}
|
||||
|
||||
private func startCountdown() {
|
||||
guard let notification = notification else { return }
|
||||
|
||||
// calculate the impact timestamp and start a timer for the countdown label
|
||||
if let impactTimestamp = EQNUtility.calculateUserSeismicTimestamp(fromUserInfo: notification) {
|
||||
self.impactTimestamp = impactTimestamp
|
||||
countdownTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(countdownTimerFired(_:)), userInfo: nil, repeats: true)
|
||||
countdownTimer?.fire()
|
||||
}
|
||||
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() {
|
||||
@@ -81,50 +79,33 @@ class AlertsSeismicNotificationExpandedTableViewCell: EQNBaseTableViewCell, MKMa
|
||||
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? {
|
||||
@@ -137,32 +118,9 @@ 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
|
||||
|
||||
@objc private func countdownTimerFired(_ sender: Timer) {
|
||||
guard let impactTimestamp = impactTimestamp else { return }
|
||||
|
||||
let now = Date()
|
||||
let difference = lround(max(impactTimestamp.timeIntervalSince(now), 0))
|
||||
waveTimeLabel.text = String(format: NSLocalizedString("alert_wave", comment: ""), difference)
|
||||
|
||||
if difference <= 0 {
|
||||
// stop the countdown
|
||||
countdownTimer?.invalidate()
|
||||
countdownTimer = nil
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction private func shareAppTapped(_ sender: UIButton) {
|
||||
onTapShareApp?()
|
||||
}
|
||||
@@ -178,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
|
||||
}
|
||||
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
//
|
||||
// EQNBackgrounPositionDebugViewController.swift
|
||||
// Earthquake Network
|
||||
//
|
||||
// Created by Andrea Busi on 14/08/23.
|
||||
// Copyright © 2023 Earthquake Network. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import CoreLocation
|
||||
|
||||
|
||||
class EQNBackgroundPositionDebugViewController: UITableViewController {
|
||||
|
||||
private var positions = [EQNBackgroundPosition]()
|
||||
private let formatter: DateFormatter = {
|
||||
let formatter = DateFormatter()
|
||||
formatter.timeStyle = .medium
|
||||
formatter.dateStyle = .medium
|
||||
return formatter
|
||||
}()
|
||||
private let helper = EQNBackgroundPositionDebugHelper()
|
||||
|
||||
|
||||
// MARK: - Init
|
||||
|
||||
convenience init() {
|
||||
self.init(style: .insetGrouped)
|
||||
}
|
||||
|
||||
// MARK: - View Lifecycle
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
configureUI()
|
||||
loadData()
|
||||
}
|
||||
|
||||
private func configureUI() {
|
||||
tableView.rowHeight = UITableView.automaticDimension
|
||||
tableView.estimatedRowHeight = 60.0
|
||||
|
||||
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .trash, target: self, action: #selector(onTapDeleteButton(_:)))
|
||||
}
|
||||
|
||||
private func loadData() {
|
||||
positions = helper.loadPosition()
|
||||
tableView.reloadData()
|
||||
}
|
||||
|
||||
// MARK: - Table view data source
|
||||
|
||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
positions.count
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let position = positions[indexPath.row]
|
||||
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "PositionCell")
|
||||
cell.textLabel?.text = formatter.string(from: position.date)
|
||||
cell.detailTextLabel?.text = String(format: "Lat: %.4f - Lon: %.4f", position.coordinate.latitude, position.coordinate.longitude)
|
||||
var imageView: UIImageView?
|
||||
switch position.request {
|
||||
case .none:
|
||||
imageView = nil
|
||||
case .some(true):
|
||||
imageView = .init(image: .init(systemName: "checkmark.circle.fill"))
|
||||
imageView?.tintColor = AppTheme.Colors.green
|
||||
case .some(false):
|
||||
imageView = .init(image: .init(systemName: "x.circle.fill"))
|
||||
imageView?.tintColor = AppTheme.Colors.red
|
||||
}
|
||||
cell.accessoryView = imageView
|
||||
return cell
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
@objc private func onTapDeleteButton(_ sender: UIBarButtonItem) {
|
||||
helper.resetPositions()
|
||||
loadData()
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -88,12 +92,9 @@
|
||||
}
|
||||
|
||||
// Determine the view width to use for the ad width.
|
||||
CGRect frame = self.view.frame;
|
||||
// Here safe area is taken into account, hence the view frame is used after
|
||||
// the view has been laid out.
|
||||
if (@available(iOS 11.0, *)) {
|
||||
frame = UIEdgeInsetsInsetRect(self.view.frame, self.view.safeAreaInsets);
|
||||
}
|
||||
CGRect frame = UIEdgeInsetsInsetRect(self.view.frame, self.view.safeAreaInsets);
|
||||
CGFloat viewWidth = frame.size.width;
|
||||
|
||||
// Step 3 - Get Adaptive GADAdSize and set the ad view.
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
//
|
||||
// RealtimeAlertView.swift
|
||||
// Earthquake Network
|
||||
//
|
||||
// Created by Andrea Busi on 15/06/22.
|
||||
// Copyright © 2022 Earthquake Network. All rights reserved.
|
||||
//
|
||||
|
||||
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 = {
|
||||
let label = UILabel()
|
||||
label.translatesAutoresizingMaskIntoConstraints = false
|
||||
label.font = .preferredFont(forTextStyle: .title2)
|
||||
label.text = NSLocalizedString("app_name", comment: "")
|
||||
return label
|
||||
}()
|
||||
|
||||
let descriptionLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
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: .title2)
|
||||
label.textColor = AppTheme.Colors.red
|
||||
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
|
||||
map.delegate = self
|
||||
map.register(EQNCustomAnnotationView.self, forAnnotationViewWithReuseIdentifier: EQNCustomAnnotationView.SingleLineIdentifier)
|
||||
map.showsUserLocation = true
|
||||
return map
|
||||
}()
|
||||
|
||||
let closeButton: EQNBlurredCloseButton = {
|
||||
let button = EQNBlurredCloseButton()
|
||||
button.translatesAutoresizingMaskIntoConstraints = false
|
||||
button.setTitleColor(.darkGray, for: .normal)
|
||||
return button
|
||||
}()
|
||||
|
||||
// MARK: - Init
|
||||
|
||||
convenience init() {
|
||||
self.init(frame: .zero)
|
||||
configureUI()
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func configureUI() {
|
||||
backgroundColor = AppTheme.Colors.lightGray
|
||||
|
||||
addSubview(closeButton)
|
||||
addSubview(titleLabel)
|
||||
addSubview(descriptionLabel)
|
||||
addSubview(mapView)
|
||||
|
||||
closeButton.addDefaultConstraint(to: self)
|
||||
|
||||
titleLabel.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor).isActive = true
|
||||
titleLabel.trailingAnchor.constraint(equalTo: closeButton.leadingAnchor, constant: -10.0).isActive = true
|
||||
titleLabel.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor, constant: 10.0).isActive = true
|
||||
|
||||
descriptionLabel.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor).isActive = true
|
||||
descriptionLabel.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor).isActive = true
|
||||
descriptionLabel.topAnchor.constraint(equalTo: titleLabel.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: stackView.bottomAnchor, constant: 20.0).isActive = true
|
||||
mapView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func addMapCircle(
|
||||
center: CLLocationCoordinate2D,
|
||||
radius: CLLocationDistance,
|
||||
overlayId: String
|
||||
) {
|
||||
// remove any other existing overlays
|
||||
let overlays = mapView.overlays.filter { $0.title == overlayId }
|
||||
mapView.removeOverlays(overlays)
|
||||
|
||||
// add new overlay
|
||||
let circle = MKCircle(center: center, radius: radius)
|
||||
circle.title = overlayId
|
||||
mapView.addOverlay(circle)
|
||||
}
|
||||
|
||||
func addMapLine(
|
||||
coordinates: [CLLocationCoordinate2D]
|
||||
) {
|
||||
let polyline = MKPolyline(coordinates: coordinates, count: coordinates.count)
|
||||
mapView.addOverlay(polyline)
|
||||
}
|
||||
|
||||
func addMapAnnotation(
|
||||
title: String = "",
|
||||
center: CLLocationCoordinate2D,
|
||||
intensity: Int
|
||||
) {
|
||||
let annotation = EQNMapAnnotationPastquake(title: title, coordinate: center, intensity: intensity)
|
||||
mapView.addAnnotation(annotation)
|
||||
}
|
||||
}
|
||||
|
||||
extension RealtimeAlertView: MKMapViewDelegate {
|
||||
|
||||
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
|
||||
switch overlay {
|
||||
case let circle as MKCircle:
|
||||
let circleRenderer = MKCircleRenderer(overlay: circle)
|
||||
circleRenderer.strokeColor = AppTheme.Colors.red
|
||||
circleRenderer.fillColor = AppTheme.Colors.red.withAlphaComponent(0.2)
|
||||
circleRenderer.lineWidth = 3.0
|
||||
return circleRenderer
|
||||
case let polyline as MKPolyline:
|
||||
let polylineRenderer = MKPolylineRenderer(polyline: polyline)
|
||||
polylineRenderer.strokeColor = .blue
|
||||
polylineRenderer.lineWidth = 2.0
|
||||
return polylineRenderer
|
||||
default:
|
||||
return MKOverlayRenderer(overlay: overlay)
|
||||
}
|
||||
}
|
||||
|
||||
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
|
||||
guard let annotation = annotation as? EQNMapAnnotationPastquake else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: EQNCustomAnnotationView.SingleLineIdentifier, for: annotation) as! EQNCustomAnnotationView
|
||||
annotationView.image = annotation.image
|
||||
annotationView.title = annotation.title
|
||||
return annotationView
|
||||
}
|
||||
}
|
||||
+196
@@ -0,0 +1,196 @@
|
||||
//
|
||||
// RealtimeAlertViewController.swift
|
||||
// Earthquake Network
|
||||
//
|
||||
// Created by Andrea Busi on 15/06/22.
|
||||
// Copyright © 2022 Earthquake Network. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import MapKit
|
||||
|
||||
|
||||
class RealtimeAlertViewController: UIViewController, MKMapViewDelegate {
|
||||
|
||||
@objc var onClose: () -> Void = {}
|
||||
|
||||
// MARK: - Internal
|
||||
|
||||
private let containerView = RealtimeAlertContainerView()
|
||||
private var notificationView: RealtimeAlertView {
|
||||
containerView.alertView
|
||||
}
|
||||
/// Alert to display
|
||||
private let realtimeAlert: EQNRealtimePushNotification
|
||||
/// Timer to constantly update countdown label
|
||||
private var countdownTimer: Timer?
|
||||
/// Refresh time for wave animation
|
||||
private let waveAnimationRefreshRate = 0.1
|
||||
/// Current radius of the wave animation on the map
|
||||
private var waveAnimationCurrentRadius: CLLocationDistance = 0
|
||||
private var waveAnimationVelocity: Double = 1_000
|
||||
/// Timer to simulate animation for the wave
|
||||
private var waveAnimationTimer: Timer?
|
||||
|
||||
// MARK: - Init
|
||||
|
||||
@objc
|
||||
init(notification: EQNRealtimePushNotification) {
|
||||
self.realtimeAlert = notification
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
|
||||
self.waveAnimationCurrentRadius = currentWavePosition()
|
||||
self.waveAnimationVelocity = evaluateWaveAnimationVelocity()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
deinit {
|
||||
// importante togliere il delegato, altrimenti causa crash
|
||||
notificationView.mapView.delegate = nil
|
||||
}
|
||||
|
||||
// MARK: - View Lifecycle
|
||||
|
||||
override func loadView() {
|
||||
view = containerView
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
configureUI()
|
||||
updateUI()
|
||||
|
||||
startCountdown()
|
||||
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() {
|
||||
notificationView.descriptionLabel.text = realtimeAlert.title
|
||||
|
||||
// update title with distance from earthquake
|
||||
let distanceRound = Int(round(realtimeAlert.distanceFromUser() / 1_000))
|
||||
notificationView.descriptionLabel.text = (notificationView.descriptionLabel.text ?? "")
|
||||
+ ".\n"
|
||||
+ 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)
|
||||
let region = MKCoordinateRegion(center: realtimeAlert.coordinate.coordinate, span: span)
|
||||
notificationView.mapView.setCenter(realtimeAlert.coordinate.coordinate, animated: false)
|
||||
notificationView.mapView.setRegion(region, animated: true)
|
||||
|
||||
// aggiungiamo annotation con epicentro sisma
|
||||
notificationView.addMapAnnotation(center: realtimeAlert.coordinate.coordinate, intensity: realtimeAlert.intensity)
|
||||
|
||||
// simuliamo animazione dell'onda sismica
|
||||
notificationView.addMapCircle(center: realtimeAlert.coordinate.coordinate, radius: waveAnimationCurrentRadius, overlayId: "wave_animation")
|
||||
|
||||
// aggiungiamo un segmento tra la posizione del sisma e quella dell'utente
|
||||
if let lastPosition = EQNUser.default().lastPosition {
|
||||
notificationView.addMapLine(coordinates: [realtimeAlert.coordinate.coordinate, lastPosition.coordinate])
|
||||
}
|
||||
}
|
||||
|
||||
private func startCountdown() {
|
||||
// show countdown only if time is less than 300 seconds
|
||||
if realtimeAlert.currentCountdown() < 300 {
|
||||
// start a timer for the countdown label
|
||||
notificationView.waveTimeLabel.isHidden = false
|
||||
countdownTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(countdownTimerFired(_:)), userInfo: nil, repeats: true)
|
||||
countdownTimer?.fire()
|
||||
}
|
||||
}
|
||||
|
||||
private func startWaveAnimation() {
|
||||
waveAnimationTimer = Timer.scheduledTimer(timeInterval: waveAnimationRefreshRate, target: self, selector: #selector(mapWaveAnimationFired(_:)), userInfo: nil, repeats: true)
|
||||
waveAnimationTimer?.fire()
|
||||
}
|
||||
|
||||
// MARK: - Action
|
||||
|
||||
@objc private func onTapClose(_ sender: UIButton) {
|
||||
// invalidiamo i timer, altri
|
||||
countdownTimer?.invalidate()
|
||||
countdownTimer = nil
|
||||
waveAnimationTimer?.invalidate()
|
||||
waveAnimationTimer = nil
|
||||
|
||||
onClose()
|
||||
dismiss(animated: true)
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Timer
|
||||
|
||||
@objc private func countdownTimerFired(_ sender: Timer) {
|
||||
let countdown = realtimeAlert.currentCountdown()
|
||||
notificationView.waveTimeLabel.text = String.localizedStringWithFormat(NSLocalizedString("alert_wave", comment: ""), countdown)
|
||||
notificationView.waveTimeLabel.textColor = waveTimeTextColor(for: countdown)
|
||||
|
||||
if countdown <= 0 {
|
||||
// stop the countdown
|
||||
countdownTimer?.invalidate()
|
||||
countdownTimer = nil
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func mapWaveAnimationFired(_ sender: Timer) {
|
||||
waveAnimationCurrentRadius += waveAnimationVelocity
|
||||
notificationView.addMapCircle(center: realtimeAlert.coordinate.coordinate, radius: waveAnimationCurrentRadius, overlayId: "wave_animation")
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
/// Evaluate current position for the wave
|
||||
/// Used to define initial position for the wave circle
|
||||
/// - Returns: Distance of the wave from the original earthquake point
|
||||
private func currentWavePosition() -> Double {
|
||||
// distanza tra utente e terremoto
|
||||
let distance = realtimeAlert.distanceFromUser()
|
||||
|
||||
// calcoliamo la distanza rimanente da mostrare, perchè la schermata potrebbe anche essere aperta in ritardo
|
||||
let remainingDistance = realtimeAlert.waveSpeed * Double(realtimeAlert.currentCountdown())
|
||||
return distance - remainingDistance
|
||||
}
|
||||
|
||||
/// Evaluate wave velocity based on push notification data
|
||||
/// - Returns: Wave velocity, used for animation
|
||||
private func evaluateWaveAnimationVelocity() -> Double {
|
||||
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
|
||||
|
||||
+8
-31
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -402,7 +377,8 @@ extension SeismicNetworksViewController: SeismicFiltersViewControllerDelegate {
|
||||
|
||||
extension SeismicNetworksViewController: SeismicSettingsViewControllerDelegate {
|
||||
func seismicSettingsControllerDidComplete(_ controller: SeismicSettingsViewController) {
|
||||
refreshUI()
|
||||
// riscarichiamo i dati, le reti selezionate potrebbero essere cambiate
|
||||
loadData(forced: true)
|
||||
}
|
||||
|
||||
func seismicSettingsControllerWillOpenProviders(_ controller: SeismicSettingsViewController) {
|
||||
@@ -412,7 +388,8 @@ extension SeismicNetworksViewController: SeismicSettingsViewControllerDelegate {
|
||||
|
||||
extension SeismicNetworksViewController: SeismicSettingsNetworksViewControllerDelegate {
|
||||
func seismicSettingsNetworksControllerDidComplete(_ controller: SeismicSettingsNetworksViewController) {
|
||||
refreshUI()
|
||||
// riscarichiamo i dati, le reti selezionate potrebbero essere cambiate
|
||||
loadData(forced: true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+9
-3
@@ -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()
|
||||
@@ -86,7 +87,12 @@ class SeismicSettingsNetworksViewController: UITableViewController {
|
||||
savedNetworks.append(network.acronym)
|
||||
}
|
||||
|
||||
tableView.reloadRows(at: [indexPath], with: .automatic)
|
||||
// reload all rows with the given acronym
|
||||
let indexes = networks
|
||||
.enumerated()
|
||||
.filter { $0.element.acronym == network.acronym }
|
||||
.map { IndexPath(row: $0.offset, section: 0) }
|
||||
tableView.reloadRows(at: indexes, with: .automatic)
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
@@ -97,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,23 +64,9 @@ 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";
|
||||
/// 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";
|
||||
|
||||
#pragma mark - NSNotification
|
||||
|
||||
@@ -102,10 +88,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 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
|
||||
@@ -182,53 +168,7 @@ 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"
|
||||
|
||||
// NOTIFICHE RETE SMARTPHONE
|
||||
#define NOTIFICHE_RETE_SMARTPHONE_DATA_NOTIFICA @"NOTIFICHE_RETE_SMARTPHONE_DATA_NOTIFICA"
|
||||
#define NOTIFICHE_RETE_SMARTPHONE_DIZIONARIO_NOTIFICA @"NOTIFICHE_RETE_SMARTPHONE_DIZIONARIO_NOTIFICA"
|
||||
#define TEMPO_VISUALIZZAZIONE_NOTIFICA 10800
|
||||
|
||||
|
||||
// FILTRO ENTI
|
||||
#define EQN_MAGNITUDO_MINIMA @"EQN_MAGNITUDO_MINIMA"
|
||||
#define EQN_DISTANZA_MASSIMA @"EQN_DISTANZA_MASSIMA"
|
||||
#define EQN_ETA_MASSIMA @"EQN_ETA_MASSIMA"
|
||||
#define EQN_SISMI_FORTI_ABILITATI @"EQN_SISMI_FORTI_ABILITATI"
|
||||
#define EQN_SISMI_FORTI @"EQN_SISMI_FORTI"
|
||||
#define EQN_SISMI_QUALSIASI_MAGNITUDO @"EQN_SISMI_QUALSIASI_MAGNITUDO"
|
||||
#define EQN_SISMI_MODIFICA_IMPOSTAZIONI @"EQN_SISMI_MODIFICA_IMPOSTAZIONI"
|
||||
|
||||
#endif /* Costanti_h */
|
||||
|
||||
@@ -8,6 +8,6 @@
|
||||
|
||||
#ifdef __OBJC__
|
||||
|
||||
#import "Earthquake_Network-Swift.h"
|
||||
#import <Earthquake_Network-Swift.h>
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>BGTaskSchedulerPermittedIdentifiers</key>
|
||||
<array>
|
||||
<string>com.finazzi.distquake.update_server_position</string>
|
||||
</array>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
@@ -18,16 +22,35 @@
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>fb1444404982546319</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>FacebookAdvertiserIDCollectionEnabled</key>
|
||||
<true/>
|
||||
<key>FacebookAppID</key>
|
||||
<string>1444404982546319</string>
|
||||
<key>FacebookAutoLogAppEventsEnabled</key>
|
||||
<true/>
|
||||
<key>FacebookClientToken</key>
|
||||
<string>46c7a338b2bbd2186b2f1c12865b4004</string>
|
||||
<key>FacebookDisplayName</key>
|
||||
<string>Earthquake Network</string>
|
||||
<key>GADApplicationIdentifier</key>
|
||||
<string>ca-app-pub-0053870219990922~2021960172</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LSApplicationQueriesSchemes</key>
|
||||
<array>
|
||||
<string>googlechromes</string>
|
||||
<string>comgooglemaps</string>
|
||||
<string>fbapi</string>
|
||||
<string>fb-messenger-share-api</string>
|
||||
</array>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
@@ -43,6 +66,8 @@
|
||||
<string> Ci occorre la tua posizione per inviare messaggi precisi in caso di terremoto</string>
|
||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||
<string>L'accesso alla libreria è richiesto per poter salvare le immagini generate dall'app</string>
|
||||
<key>NSUserTrackingUsageDescription</key>
|
||||
<string>Il tracciamento serve a capire se la pubblicità dell'app è efficace</string>
|
||||
<key>SKAdNetworkItems</key>
|
||||
<array>
|
||||
<dict>
|
||||
@@ -55,6 +80,7 @@
|
||||
<string>audio</string>
|
||||
<string>fetch</string>
|
||||
<string>location</string>
|
||||
<string>processing</string>
|
||||
<string>remote-notification</string>
|
||||
</array>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
|
||||
@@ -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,32 @@
|
||||
//
|
||||
// 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: UserDefaults.UserReportExpandedView) }
|
||||
set { UserDefaults.standard.set(newValue, forKey: UserDefaults.UserReportExpandedView) }
|
||||
}
|
||||
|
||||
/// Defines if options has to be shown on seismic cards
|
||||
@objc
|
||||
var alertsShowAllCards: Bool {
|
||||
get { UserDefaults.standard.bool(forKey: UserDefaults.AlertsShowCardOptions) }
|
||||
set { UserDefaults.standard.set(newValue, forKey: UserDefaults.AlertsShowCardOptions) }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// BackgroundTaskIdentifiable.swift
|
||||
// Earthquake Network
|
||||
//
|
||||
// Created by Andrea Busi on 11/09/23.
|
||||
// Copyright © 2023 Earthquake Network. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import BackgroundTasks
|
||||
|
||||
|
||||
protocol BackgroundTaskIdentifiable {
|
||||
typealias TaskCompletion = (_ success: Bool) -> Void
|
||||
|
||||
static var identifier: String { get }
|
||||
static var interval: TimeInterval { get }
|
||||
|
||||
init()
|
||||
|
||||
func handle(_ task: BGTask, completion: @escaping TaskCompletion)
|
||||
func exipration()
|
||||
}
|
||||
|
||||
extension BackgroundTaskIdentifiable {
|
||||
func exipration() { }
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
//
|
||||
// BackgroundTaskManager.swift
|
||||
// Earthquake Network
|
||||
//
|
||||
// Created by Andrea Busi on 16/08/23.
|
||||
// Copyright © 2023 Earthquake Network. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import BackgroundTasks
|
||||
|
||||
|
||||
@objc
|
||||
class BackgroundTaskManager: NSObject {
|
||||
|
||||
@objc
|
||||
static let shared = BackgroundTaskManager()
|
||||
|
||||
private let identifiers: [BackgroundTaskIdentifiable.Type] = [UpdateUserLocationTask.self]
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
@objc
|
||||
func registerTasks() {
|
||||
identifiers
|
||||
.forEach { taskIdentifiable in
|
||||
BGTaskScheduler.shared.register(forTaskWithIdentifier: taskIdentifiable.identifier, using: DispatchQueue.global()) { [weak self] task in
|
||||
guard let appTask = task as? BGAppRefreshTask else { return }
|
||||
|
||||
self?.handleTask(appTask, with: taskIdentifiable)
|
||||
self?.scheduleTaskRequest(for: taskIdentifiable)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
@objc
|
||||
func scheduleUpdateServerPosition() {
|
||||
scheduleTaskRequest(for: UpdateUserLocationTask.self)
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func scheduleTaskRequest(
|
||||
for taskIdentifiable: BackgroundTaskIdentifiable.Type
|
||||
) {
|
||||
let request = BGAppRefreshTaskRequest(identifier: taskIdentifiable.identifier)
|
||||
// Fetch no earlier than X minutes from now
|
||||
request.earliestBeginDate = Date(timeIntervalSinceNow: taskIdentifiable.interval)
|
||||
//request.requiresNetworkConnectivity = true
|
||||
|
||||
do {
|
||||
try BGTaskScheduler.shared.submit(request)
|
||||
print("[BackgroundTaskManager] Background task scheduler submitted")
|
||||
} catch {
|
||||
print("[BackgroundTaskManager] Could not schedule background taksk. Error: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
private func handleTask(
|
||||
_ appTask: BGAppRefreshTask,
|
||||
with taskIdentifiable: BackgroundTaskIdentifiable.Type
|
||||
) {
|
||||
// create a task
|
||||
let task = taskIdentifiable.init()
|
||||
|
||||
// Provide the background task with an expiration handler that cancels the operation.
|
||||
appTask.expirationHandler = {
|
||||
task.exipration()
|
||||
}
|
||||
|
||||
// Handle workload
|
||||
task.handle(appTask) { success in
|
||||
// Inform the system that the background task is complete
|
||||
// when the operation completes.
|
||||
appTask.setTaskCompleted(success: success)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// EQNBackgroundPosition.swift
|
||||
// Earthquake Network
|
||||
//
|
||||
// Created by Andrea Busi on 16/08/23.
|
||||
// Copyright © 2023 Earthquake Network. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreLocation
|
||||
|
||||
|
||||
struct EQNBackgroundPosition: Codable {
|
||||
let date: Date
|
||||
private let latitude: Double
|
||||
private let longitude: Double
|
||||
var coordinate: CLLocationCoordinate2D {
|
||||
.init(latitude: latitude, longitude: longitude)
|
||||
}
|
||||
let request: Bool?
|
||||
|
||||
init(
|
||||
date: Date,
|
||||
coordinate: CLLocationCoordinate2D,
|
||||
request: Bool?
|
||||
) {
|
||||
self.date = date
|
||||
self.latitude = coordinate.latitude
|
||||
self.longitude = coordinate.longitude
|
||||
self.request = request
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
//
|
||||
// EQNDebugHelper.swift
|
||||
// Earthquake Network
|
||||
//
|
||||
// Created by Andrea Busi on 14/07/23.
|
||||
// Copyright © 2023 Earthquake Network. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
@objc
|
||||
class EQNBackgroundPositionDebugHelper: NSObject {
|
||||
|
||||
@objc
|
||||
static let shared = EQNBackgroundPositionDebugHelper()
|
||||
|
||||
private var timers: [String: Timer] = [:]
|
||||
|
||||
@objc
|
||||
var isEnabled: Bool { false }
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// MARK: - Class
|
||||
|
||||
func savePosition(
|
||||
coordinate: CLLocationCoordinate2D,
|
||||
requestSuccess: Bool
|
||||
) {
|
||||
var positions = loadPosition()
|
||||
positions.append(.init(date: Date(), coordinate: coordinate, request: requestSuccess))
|
||||
|
||||
if let data = try? JSONEncoder().encode(positions) {
|
||||
UserDefaults.standard.set(data, forKey: "BackgroundPositions")
|
||||
}
|
||||
}
|
||||
|
||||
func resetPositions() {
|
||||
UserDefaults.standard.removeObject(forKey: "BackgroundPositions")
|
||||
}
|
||||
|
||||
func loadPosition() -> [EQNBackgroundPosition] {
|
||||
guard let data = UserDefaults.standard.object(forKey: "BackgroundPositions") as? Data else {
|
||||
return []
|
||||
}
|
||||
|
||||
let positions = try? JSONDecoder().decode([EQNBackgroundPosition].self, from: data)
|
||||
return positions ?? []
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
//
|
||||
// UpdateUserLocationTask.swift
|
||||
// Earthquake Network
|
||||
//
|
||||
// Created by Andrea Busi on 16/08/23.
|
||||
// Copyright © 2023 Earthquake Network. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import BackgroundTasks
|
||||
import CoreLocation
|
||||
|
||||
|
||||
final class UpdateUserLocationTask: NSObject, BackgroundTaskIdentifiable {
|
||||
|
||||
static var identifier: String {
|
||||
"com.finazzi.distquake.update_server_position"
|
||||
}
|
||||
|
||||
static var interval: TimeInterval {
|
||||
5 * 60
|
||||
}
|
||||
|
||||
static let shared = UpdateUserLocationTask()
|
||||
private let debugHelper = EQNBackgroundPositionDebugHelper()
|
||||
|
||||
// MARK: - Internal
|
||||
|
||||
private lazy var locationManager: CLLocationManager = {
|
||||
let manager = CLLocationManager()
|
||||
manager.delegate = self
|
||||
manager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
|
||||
manager.allowsBackgroundLocationUpdates = true
|
||||
manager.pausesLocationUpdatesAutomatically = false
|
||||
return manager
|
||||
}()
|
||||
|
||||
var appTaskCompletion: BackgroundTaskIdentifiable.TaskCompletion?
|
||||
|
||||
func handle(_ task: BGTask, completion: @escaping (_ success: Bool) -> Void) {
|
||||
self.appTaskCompletion = completion
|
||||
|
||||
// ricaviamo la posizione corrente dell'utente
|
||||
if let location = locationManager.location {
|
||||
complete(with: location)
|
||||
} else {
|
||||
locationManager.requestLocation()
|
||||
}
|
||||
}
|
||||
|
||||
func exipration() {
|
||||
locationManager.stopUpdatingLocation()
|
||||
failed()
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func complete(with location: CLLocation) {
|
||||
// send position to cloud
|
||||
let url = EQNGeneratoreURLServer.urlPosizione(withLocation: location.coordinate)
|
||||
ServerRequest.default().inviaInformazioniAlServer(with: url, richiesta: .posizione) { result in
|
||||
if self.debugHelper.isEnabled {
|
||||
self.debugHelper.savePosition(coordinate: location.coordinate, requestSuccess: true)
|
||||
}
|
||||
self.appTaskCompletion?(true)
|
||||
} failure: { error in
|
||||
if self.debugHelper.isEnabled {
|
||||
self.debugHelper.savePosition(coordinate: location.coordinate, requestSuccess: false)
|
||||
}
|
||||
self.appTaskCompletion?(false)
|
||||
}
|
||||
}
|
||||
|
||||
private func failed() {
|
||||
appTaskCompletion?(false)
|
||||
}
|
||||
}
|
||||
|
||||
extension UpdateUserLocationTask: CLLocationManagerDelegate {
|
||||
|
||||
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
|
||||
if let location = locations.first {
|
||||
complete(with: location)
|
||||
} else {
|
||||
failed()
|
||||
}
|
||||
}
|
||||
|
||||
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
|
||||
print("[UpdateUserLocationTask] Location manager failed. Error: \(error.localizedDescription)")
|
||||
|
||||
// nope, but mandatory
|
||||
failed()
|
||||
}
|
||||
}
|
||||
@@ -24,30 +24,21 @@ public class EQNAppearanceCommand: EQNCommandProtocol {
|
||||
private func applyAppearance() {
|
||||
// UINavigationBar
|
||||
let proxyNavBar = UINavigationBar.appearance(whenContainedInInstancesOf: [UINavigationController.self])
|
||||
if #available(iOS 13.0, *) {
|
||||
let navAppearance = UINavigationBarAppearance()
|
||||
navAppearance.configureWithOpaqueBackground()
|
||||
navAppearance.titleTextAttributes = [
|
||||
NSAttributedString.Key.foregroundColor: AppTheme.Colors.darkGray
|
||||
]
|
||||
navAppearance.largeTitleTextAttributes = [
|
||||
NSAttributedString.Key.foregroundColor: AppTheme.Colors.darkGray
|
||||
]
|
||||
navAppearance.backgroundColor = AppTheme.Colors.primary
|
||||
navAppearance.shadowColor = UIColor.clear
|
||||
let navAppearance = UINavigationBarAppearance()
|
||||
navAppearance.configureWithOpaqueBackground()
|
||||
navAppearance.titleTextAttributes = [
|
||||
NSAttributedString.Key.foregroundColor: AppTheme.Colors.darkGray
|
||||
]
|
||||
navAppearance.largeTitleTextAttributes = [
|
||||
NSAttributedString.Key.foregroundColor: AppTheme.Colors.darkGray
|
||||
]
|
||||
navAppearance.backgroundColor = AppTheme.Colors.primary
|
||||
navAppearance.shadowColor = UIColor.clear
|
||||
|
||||
proxyNavBar.isTranslucent = false
|
||||
proxyNavBar.tintColor = AppTheme.Colors.darkGray
|
||||
proxyNavBar.standardAppearance = navAppearance
|
||||
proxyNavBar.scrollEdgeAppearance = navAppearance
|
||||
} else {
|
||||
proxyNavBar.tintColor = AppTheme.Colors.darkGray
|
||||
proxyNavBar.isTranslucent = false
|
||||
proxyNavBar.barTintColor = AppTheme.Colors.primary
|
||||
proxyNavBar.titleTextAttributes = [
|
||||
NSAttributedString.Key.foregroundColor: UIColor.white
|
||||
]
|
||||
}
|
||||
proxyNavBar.isTranslucent = false
|
||||
proxyNavBar.tintColor = AppTheme.Colors.darkGray
|
||||
proxyNavBar.standardAppearance = navAppearance
|
||||
proxyNavBar.scrollEdgeAppearance = navAppearance
|
||||
|
||||
let proxyTabBar = UITabBar.appearance()
|
||||
proxyTabBar.tintColor = AppTheme.Colors.red
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -27,14 +27,15 @@ import Foundation
|
||||
override init() {
|
||||
Self.migrateOldDistanza()
|
||||
Self.migrateOldPeriodo()
|
||||
|
||||
magnitudoMinima = Self.userDefaults(for: EQN_MAGNITUDO_MINIMA, or: EQNData.DefaultMagitudoDebole.value)
|
||||
distanzaMassima = Self.userDefaults(for: EQN_DISTANZA_MASSIMA, or: EQNData.DefaultRaggioSisma.value)
|
||||
periodoTemporale = Self.userDefaults(for: EQN_ETA_MASSIMA, or: EQNData.DefaultPeriodoTemporale.value)
|
||||
sismiFortiAbilitati = Self.userDefaults(for: EQN_SISMI_FORTI_ABILITATI, or: false)
|
||||
sismiFortiMagnitudo = Self.userDefaults(for: EQN_SISMI_FORTI, or: EQNData.DefaultMagitudoForte.value)
|
||||
sismiQualsiasiAbilitati = Self.userDefaults(for: EQN_SISMI_QUALSIASI_MAGNITUDO, or: false)
|
||||
modificaImpostazioniAbilitato = Self.userDefaults(for: EQN_SISMI_MODIFICA_IMPOSTAZIONI, or: false)
|
||||
|
||||
let defaults = UserDefaults.standard
|
||||
magnitudoMinima = defaults.object(forKey: UserDefaults.SeismicMagnitudoMinima, or: EQNData.DefaultMagitudoDebole.value)
|
||||
distanzaMassima = defaults.object(forKey: UserDefaults.SeismicDistanzaMassima, or: EQNData.DefaultRaggioSisma.value)
|
||||
periodoTemporale = defaults.object(forKey: UserDefaults.SeismicEtaMassima, or: EQNData.DefaultPeriodoTemporale.value)
|
||||
sismiFortiAbilitati = defaults.object(forKey: UserDefaults.SeismicSismiFortiAbilitati, or: false)
|
||||
sismiFortiMagnitudo = defaults.object(forKey: UserDefaults.SeismicSismiForti, or: EQNData.DefaultMagitudoForte.value)
|
||||
sismiQualsiasiAbilitati = defaults.object(forKey: UserDefaults.SeismicSismiQualsiasiMagnitudo, or: false)
|
||||
modificaImpostazioniAbilitato = defaults.object(forKey: UserDefaults.SeismicModificaImpostazioni, or: true)
|
||||
|
||||
super.init()
|
||||
}
|
||||
@@ -43,33 +44,26 @@ import Foundation
|
||||
// MARK: - Public
|
||||
|
||||
public func saveFilters() {
|
||||
UserDefaults.standard.set(magnitudoMinima, forKey:EQN_MAGNITUDO_MINIMA)
|
||||
UserDefaults.standard.set(distanzaMassima, forKey:EQN_DISTANZA_MASSIMA)
|
||||
UserDefaults.standard.set(periodoTemporale, forKey:EQN_ETA_MASSIMA)
|
||||
UserDefaults.standard.set(sismiFortiMagnitudo, forKey:EQN_SISMI_FORTI)
|
||||
UserDefaults.standard.set(sismiFortiAbilitati, forKey:EQN_SISMI_FORTI_ABILITATI)
|
||||
UserDefaults.standard.set(sismiQualsiasiAbilitati, forKey:EQN_SISMI_QUALSIASI_MAGNITUDO)
|
||||
UserDefaults.standard.set(modificaImpostazioniAbilitato, forKey:EQN_SISMI_MODIFICA_IMPOSTAZIONI)
|
||||
UserDefaults.standard.set(magnitudoMinima, forKey: UserDefaults.SeismicMagnitudoMinima)
|
||||
UserDefaults.standard.set(distanzaMassima, forKey: UserDefaults.SeismicDistanzaMassima)
|
||||
UserDefaults.standard.set(periodoTemporale, forKey: UserDefaults.SeismicEtaMassima)
|
||||
UserDefaults.standard.set(sismiFortiMagnitudo, forKey: UserDefaults.SeismicSismiForti)
|
||||
UserDefaults.standard.set(sismiFortiAbilitati, forKey: UserDefaults.SeismicSismiFortiAbilitati)
|
||||
UserDefaults.standard.set(sismiQualsiasiAbilitati, forKey: UserDefaults.SeismicSismiQualsiasiMagnitudo)
|
||||
UserDefaults.standard.set(modificaImpostazioniAbilitato, forKey: UserDefaults.SeismicModificaImpostazioni)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private static func userDefaults<T>(for key: String, or defaultValue: T) -> T {
|
||||
if let value = UserDefaults.standard.object(forKey: key) as? T {
|
||||
return value
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
private static func migrateOldDistanza() {
|
||||
guard let savedValue = UserDefaults.standard.object(forKey: EQN_DISTANZA_MASSIMA) as? String else {
|
||||
guard let savedValue = UserDefaults.standard.object(forKey: UserDefaults.SeismicDistanzaMassima) as? String else {
|
||||
print("[EQNSeismic] Distanza massima: nessun valore da convertire")
|
||||
return
|
||||
}
|
||||
|
||||
if savedValue.lowercased() == NSLocalizedString("radius_any_distance", comment: "").lowercased() {
|
||||
print("[EQNSeismic] Distanza massima: trovato qualsiasi distanza, salvo valore")
|
||||
UserDefaults.standard.set("100000", forKey: EQN_DISTANZA_MASSIMA)
|
||||
UserDefaults.standard.set("100000", forKey: UserDefaults.SeismicDistanzaMassima)
|
||||
} else {
|
||||
print("[EQNSeismic] Distanza massima: valore da non convertire (value: \(savedValue))")
|
||||
|
||||
@@ -77,7 +71,7 @@ import Foundation
|
||||
}
|
||||
|
||||
private static func migrateOldPeriodo() {
|
||||
guard let savedValue = UserDefaults.standard.object(forKey: EQN_ETA_MASSIMA) as? String else {
|
||||
guard let savedValue = UserDefaults.standard.object(forKey: UserDefaults.SeismicEtaMassima) as? String else {
|
||||
print("[EQNSeismic] Età massima: nessun valore da convertire");
|
||||
return
|
||||
}
|
||||
@@ -99,7 +93,7 @@ import Foundation
|
||||
|
||||
if let convertedValue = convertedValue {
|
||||
print("[EQNSeismic] Età massima: salvo valore convertito (old: \(savedValue) - new: \(convertedValue)")
|
||||
UserDefaults.standard.set(convertedValue, forKey: EQN_ETA_MASSIMA)
|
||||
UserDefaults.standard.set(convertedValue, forKey: UserDefaults.SeismicEtaMassima)
|
||||
} else {
|
||||
print("[EQNSeismic] Età massima: valore già convertito")
|
||||
}
|
||||
@@ -109,10 +103,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"
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user