Compare commits

...

123 Commits

Author SHA1 Message Date
Andrea Busi 465d3e8013 release: Increase version for release 2024-10-17 18:48:19 +02:00
Andrea Busi a7e88b43f5 fix: Add LSMinimumSystemVersion for macOS compatibility 2024-10-17 18:47:37 +02:00
Andrea Busi 57ef877846 release: Increase version for release 2024-10-17 17:35:57 +02:00
Andrea Busi c44d97b9fb feat: Disable critical alerts setting if permission is not granted 2024-10-17 17:22:41 +02:00
Andrea Busi fd4ed7f66f fix: Load existing Firebase Token 2024-10-17 16:05:17 +02:00
Andrea Busi ef5db97854 refactor: Store first app start using a user default 2024-10-17 16:04:56 +02:00
Andrea Busi ce0e17a0c5 refactor: Remove old migrations 2024-10-17 15:21:18 +02:00
Andrea Busi 2a46f1d2d6 release: Increase version for release 2024-10-17 09:21:44 +02:00
Andrea Busi 93871f0358 chore: Add IDE file 2024-10-17 09:19:10 +02:00
Andrea Busi 3e8fe0680d fix: Solve missing critical alert permission request 2024-10-17 09:18:52 +02:00
Andrea Busi 6be5f72360 release: Increase version for release 2024-07-16 11:45:52 +02:00
Andrea Busi ccd1b9de59 dependency: Update Firebase 2024-07-16 11:45:43 +02:00
Andrea Busi 5737eb5b02 feat: Sort user subscriptions (top10k first) 2024-07-16 11:43:58 +02:00
Andrea Busi c549bb6ea5 fix: Solve wrong localized AR string 2024-07-16 09:23:39 +02:00
Andrea Busi ff80905033 fix: Solve crash due to wrong string format 2024-07-16 09:09:57 +02:00
Andrea Busi dad2bc5648 release: Increase version for release 2024-07-08 15:04:01 +02:00
Andrea Busi 10c74e278e refactor: Rework layout for restore subscriptions 2024-07-08 13:51:54 +02:00
Andrea Busi 96dbf960d2 refactor: Change tab official translations 2024-07-08 13:51:39 +02:00
Andrea Busi 81bfdd02a6 release: Increase version for release 2024-07-05 11:51:52 +02:00
Andrea Busi 2ab3267981 dependency: SPM 2024-07-05 11:45:29 +02:00
Andrea Busi 48b6941ed5 feat: Change nav bar color 2024-07-05 11:45:23 +02:00
Andrea Busi 669cb3c4f3 fix: Improve translation 2024-07-05 11:40:52 +02:00
Andrea Busi 638d819d35 refactor: Improve log 2024-07-05 09:03:48 +02:00
Andrea Busi a9884d8a8d release: Increase version for release 2024-07-04 15:22:58 +02:00
Andrea Busi 2ef3560011 feat: Scroll to opened seismic 2024-07-04 15:20:05 +02:00
Andrea Busi 05093bb7a4 chore: Update push payloads 2024-07-04 15:19:55 +02:00
Andrea Busi 55f84ab46d feat: Add new string for notification body 2024-07-04 15:19:45 +02:00
Andrea Busi 03b4d0ddd6 feat: Show right arrow to priority cell 2024-07-03 11:18:50 +02:00
Andrea Busi 3c5f26bc94 fix: Set background color to the proper container 2024-07-03 11:18:41 +02:00
Andrea Busi 8c79d45b19 release: Increase version for release 2024-07-03 10:10:37 +02:00
Andrea Busi 931d04c5e1 refactor: Reorganize files 2024-07-03 10:10:04 +02:00
Andrea Busi 4d62fbbbd3 fix: Solve wrong distance in filter evaluation 2024-07-03 10:00:55 +02:00
Andrea Busi 1c7065ece7 release: Increase version for release 2024-07-02 20:59:37 +02:00
Andrea Busi 6dfa51e013 refactor: Bigger fonts 2024-07-02 19:13:10 +02:00
Andrea Busi b8b21d1458 fix: Remove separator from table view 2024-07-02 18:15:03 +02:00
Andrea Busi 88317f79e8 fix: Missing rounded corners 2024-07-02 17:57:01 +02:00
Andrea Busi 4e1147e782 refactor: Remove no longer used class 2024-07-02 17:56:55 +02:00
Andrea Busi 579969d507 fix: Missing callback 2024-07-02 17:55:25 +02:00
Andrea Busi 4d991d9a10 refactor: Recreate expanded notification cell via code and change some UI elements 2024-07-02 17:55:19 +02:00
Andrea Busi 41491b5ee7 refactor: Change colors as per specifications 2024-07-02 12:25:18 +02:00
Andrea Busi 197b375c28 refactor: Remove setting for filter type in seismic notifications 2024-07-01 11:18:47 +02:00
Andrea Busi f41e6b50ec refactor: Create UI in code to properly manage filter view 2024-07-01 10:40:50 +02:00
Andrea Busi 796e4b5895 release: Increase version for release 2024-07-01 10:40:26 +02:00
Andrea Busi e43a93979d fix: Solve layout issue with gradient background 2024-06-29 16:16:47 +02:00
Andrea Busi ef1aaa7d71 refactor: Use extension for view rounded corners and shadow 2024-06-29 16:16:47 +02:00
Andrea Busi 22d78baa8a feat: Highlight seismic card title for push notification
Resolves: https://gitlab.steamware.net/eqn/eqn.ios/-/issues/76
2024-06-29 16:16:47 +02:00
Andrea Busi e4588aa731 chore: Update payload for official push notification 2024-06-29 16:16:47 +02:00
Andrea Busi 07764f91ed feat: Add logic to update filter when push is opened
Resolves: https://gitlab.steamware.net/eqn/eqn.ios/-/issues/76
2024-06-29 16:16:47 +02:00
Andrea Busi a0a238e384 refactor" Don't show image for official notifications 2024-06-27 18:19:30 +02:00
Andrea Busi e61a45f78f fix: Resolve deprecation 2024-06-27 17:04:02 +02:00
Andrea Busi 0fdc60b938 chore: Fix push payload sample 2024-06-27 17:04:02 +02:00
Andrea Busi 5f02e2b8bb dependency: Update Firebase 2024-06-27 17:04:02 +02:00
Andrea Busi b17a57b98e refactor: Remove unused strings 2024-06-24 17:47:54 +02:00
Andrea Busi 2379077272 release: Increase version for release 2024-06-24 09:02:50 +02:00
Andrea Busi 78f0cfb2fa feat: Add gradient background in seismic cards
Resolves: https://gitlab.steamware.net/eqn/eqn.ios/-/issues/74
2024-06-24 09:00:49 +02:00
Andrea Busi f6bfe3fca0 dependency: Update Shogun 2024-06-24 09:00:16 +02:00
Andrea Busi d5ab49b807 release: Increase version for release 2024-06-23 16:33:19 +02:00
Andrea Busi b8bd547d65 refactor: Minor changes to new map 2024-06-23 16:24:31 +02:00
Andrea Busi 547c503726 release: Increase version for release 2024-06-22 11:06:07 +02:00
Andrea Busi 234622bcfd feat: Share screenshot feature in base map controller 2024-06-22 11:04:27 +02:00
Andrea Busi 589466c8c6 refactor: Minor changes in subscription page 2024-06-22 11:04:27 +02:00
Andrea Busi 2e7742951e feat: Change map annotation layout in seismic map
Resolves: https://gitlab.steamware.net/eqn/eqn.ios/-/issues/71
2024-06-22 11:04:27 +02:00
Andrea Busi 3ed77ff1af feat: Support zPriority in base map 2024-06-21 17:42:15 +02:00
Andrea Busi c98530fc54 release: Increase version for release 2024-06-21 15:59:57 +02:00
Andrea Busi 98cc7e7c4c feat: Complete subscription page refactor 2024-06-21 15:56:55 +02:00
Andrea Busi 4db0bb6316 feat: Base cell can show a right chevron 2024-06-20 16:26:19 +02:00
Andrea Busi 8c3f2dad6d refactor: Rework model for in app products 2024-06-20 16:26:19 +02:00
Andrea Busi e0f346a4dc refactor: Create subscriptions controller from code 2024-06-20 16:26:19 +02:00
Andrea Busi eac0f8249e feat: New layout for subscription details 2024-06-20 16:26:19 +02:00
Andrea Busi d7c691101c refactor: Use methods from Shogun 2024-06-18 23:43:34 +02:00
Andrea Busi 49edbe1a14 refactor: Don't use static constants for fonts 2024-06-18 15:33:58 +02:00
Andrea Busi c5b3750ee7 release: Increase version for release 2024-06-15 15:21:31 +02:00
Andrea Busi 98fb65a640 refactor: Remove some unused strings 2024-06-15 15:20:18 +02:00
Andrea Busi c20041127b feat: Don't open filters from map and change displayed label
https://gitlab.steamware.net/eqn/eqn.ios/-/issues/71
2024-06-15 15:16:24 +02:00
Andrea Busi 3995c29b22 refactor: Migrate Segnalazioni cells to code 2024-06-15 15:15:42 +02:00
Andrea Busi dfa07d0d10 fix: Solve wrong seismic download filter 2024-06-14 21:57:39 +02:00
Andrea Busi ce6fbb24ff release: Increase version for release 2024-06-14 16:30:07 +02:00
Andrea Busi 382dcfa794 refactor: Migrate subscription products cell to code 2024-06-14 16:15:26 +02:00
Andrea Busi d46a2e1559 refactor: Create extensions for padding constants 2024-06-14 16:12:21 +02:00
Andrea Busi b0d1cde42b refactor: Migarate active subscription and description to code 2024-06-14 16:03:00 +02:00
Andrea Busi d8612e33a3 refactor: Migrate AlertsPositionDataTableViewCell to code 2024-06-14 16:03:00 +02:00
Andrea Busi 73826d7520 refactor: Migrate AlertsSeismicNotificationCompactTableViewCell to code 2024-06-14 16:03:00 +02:00
Andrea Busi 3f57ac9b96 refactor: Migrate AlertsPastEartquakesTableViewCell to code 2024-06-14 16:03:00 +02:00
Andrea Busi 54c78aac0f refactor: Migrate AlertsNoLocationTableViewCell to code 2024-06-14 16:03:00 +02:00
Andrea Busi 975f5ed5bc refactor: Migrate AlertsPriorityServiceTableViewCell to code 2024-06-14 16:03:00 +02:00
Andrea Busi 52142486cf refactor: Migrate AlertsSmartphoneNetworkTableViewCell to code 2024-06-14 16:03:00 +02:00
Andrea Busi b4b676ca8d refactor: Add extension to create EQNRoundedButton 2024-06-14 13:09:48 +02:00
Andrea Busi dd9ef878e2 feat: Support multiline titles in EQNRoundedButton 2024-06-14 12:11:48 +02:00
Andrea Busi 5b978e535c release: Increase version for release 2024-06-12 22:23:09 +02:00
Andrea Busi 242c15ba58 feat: Support dynamic font in some views 2024-06-12 22:22:35 +02:00
Andrea Busi a224837dcb fix: Some UI fixes 2024-06-11 17:40:33 +02:00
Andrea Busi a21c16a01c release: Increase version for release 2024-06-11 11:17:47 +02:00
Andrea Busi 1496f25251 feat: Manage missing location in Seismic list and filters 2024-06-11 11:13:40 +02:00
Andrea Busi ad6eb6619c refactor: Remove no longer needed ObjC bridging 2024-06-11 10:56:31 +02:00
Andrea Busi f9a8dffad5 feat: Minor UI change for depth
Resolves: https://gitlab.steamware.net/eqn/eqn.ios/-/issues/69
2024-06-10 22:20:14 +02:00
Andrea Busi a708a0f79a refactor: Migrate EQNSettingRealTimeAlert to Swift 2024-06-10 22:14:01 +02:00
Andrea Busi 49431a760c feat: Align user report to new values and recreate in Swift 2024-06-10 22:14:01 +02:00
Andrea Busi a57e883409 refactor: Align names for "seismic network notifications" settings 2024-06-10 15:01:23 +02:00
Andrea Busi b373dc1d60 refactor: Migrate SettingsRealTimeAlertsViewController to Swift 2024-06-10 15:01:23 +02:00
Andrea Busi 01f1df9c01 feat: Force notification settings upload on first app start after migration
Resolves: https://gitlab.steamware.net/eqn/eqn.ios/-/issues/68
2024-06-10 13:25:34 +02:00
Andrea Busi 72441d0532 refactor: Create Swift version of SettingsBaseViewController 2024-06-10 13:25:34 +02:00
Andrea Busi a4afb84e6d feat: Add sort feature in Seismic list 2024-06-10 08:55:25 +02:00
Andrea Busi 45a59e30ba feat: Rework seismic filters with new specifications
Resolves: https://gitlab.steamware.net/eqn/eqn.ios/-/issues/70
2024-06-09 16:15:34 +02:00
Andrea Busi dac13acb9e refactor: Update parameters for upload settings
Resolves: https://gitlab.steamware.net/eqn/eqn.ios/-/issues/68
2024-06-09 16:15:34 +02:00
Andrea Busi a9e264d666 feat: Rework notification settings for network alerts
Resolves: https://gitlab.steamware.net/eqn/eqn.ios/-/issues/66
2024-06-09 16:15:34 +02:00
Andrea Busi e64aaf2469 refactor: Move network label in card
Resolves: https://gitlab.steamware.net/eqn/eqn.ios/-/issues/69
2024-06-09 16:15:34 +02:00
Andrea Busi 30c7536d4c refactor: Remove bell icon in Seismic Networks
Resolves: https://gitlab.steamware.net/eqn/eqn.ios/-/issues/69
2024-06-09 16:15:34 +02:00
Andrea Busi 70e82a67b1 refactor: Remove networks selection in Seismic Networks
Resolves: https://gitlab.steamware.net/eqn/eqn.ios/-/issues/69
2024-06-09 16:15:34 +02:00
Andrea Busi f020ac70a1 feat: Increase minimum target to iOS 14 2024-06-09 16:15:12 +02:00
Andrea Busi dc4ccd796d dependency: Update packages 2024-06-09 16:15:05 +02:00
Andrea Busi f66d6558b5 refactor: Move getDeltaMinute to EQNUtility 2024-06-07 17:03:13 +02:00
Andrea Busi 536ed32fb9 refactor: Remove unused constants 2024-06-07 17:03:08 +02:00
Andrea Busi 2e1a2a8e04 feat: Add method to retrieve enum from user defaults 2024-06-06 14:50:32 +02:00
Andrea Busi 527132b7eb dependency: Update Shogun 2024-06-06 14:46:53 +02:00
Andrea Busi 8cf69a9d12 fix: Don't perform user registration if Firebase token is null
Resolves: https://gitlab.steamware.net/eqn/eqn.ios/-/issues/65
2024-06-06 10:54:31 +02:00
Andrea Busi 6cba42994d refactor: Remove CocoaPods 2024-06-06 10:41:57 +02:00
Andrea Busi fd7821c083 dependency: Migrate Firebase to SPM 2024-06-06 10:41:57 +02:00
Andrea Busi 5fab419d0e refactor: Use new property from Facebook SDK 2024-04-29 14:25:34 +02:00
Andrea Busi befe46465b dependency: Migrate FBSDKCoreKit to SPM 2024-04-29 14:25:34 +02:00
Andrea Busi 5e6ee892ce dependency: Migrate Google-Mobile-Ads-SDK to SPM 2024-04-29 14:25:34 +02:00
Andrea Busi 79d4b3b3bd refactor: Change nullability 2024-04-29 14:25:34 +02:00
Andrea Busi 357bdd47e3 dependency: Migrate DZNEmptyDataSet to SPM 2024-04-29 14:25:34 +02:00
Andrea Busi 1e4dd507da dependency: Migrate Solar as SPM 2024-04-29 14:25:34 +02:00
118 changed files with 4869 additions and 5489 deletions
+11
View File
@@ -1,5 +1,16 @@
# Changelog
## Versione 5.8.1
- Corrette traduzioni errate (causavano crash)
- Aggiunto ordinamento in sottoscrizioni utente (prima Top10k)
## Versione 5.8
- Modifica algoritmo filtro lista (issue #70)
- Modifica impostazioni "Notifiche da reti sismiche" (issue #66)
- Modifica invio impostazioni app per notifiche (issue #68)
- Modifica impostazioni "Notifiche segnalazioni utente" (issue #67)
- Modifica tab Reti Sismiche (issue #69)
## Versione 5.7
- Aumentato target ad iOS 13
- Disattivata logica calibrazione/monitoraggio
+34
View File
@@ -0,0 +1,34 @@
{
"magnitude_range" : "0",
"google.c.a.e" : "1",
"provider" : "INGV",
"google.c.fid" : "fFjFx_Em8E-op_zHYXZpSr",
"preliminary" : "0",
"longitude" : "15.2917",
"gcm.message_id" : "1719991146578422",
"latitude" : "40.7738",
"google.c.sender.id" : "899482329945",
"type" : "official",
"magnitude" : "1.4",
"difference" : "13",
"depth" : "14.6",
"aps" : {
"mutable-content" : 1,
"alert" : {
"body" : "Sisma rilevato a",
"title-loc-key" : "Segnalazione da rete sismica",
"title" : "Segnalazione da rete sismica",
"loc-key" : "Sisma rilevato a",
"action-loc-key" : "",
"loc-args" : [
"2 km SW Laviano (SA) - M1.4"
]
},
"content-available" : 1,
"sound" : "default"
},
"data" : "2024-07-03 09:05:52",
"magnitude_type" : "ML",
"place" : "2 km SW Laviano (SA)",
"pop100" : "6824"
}
-31
View File
@@ -1,31 +0,0 @@
{
"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"
}
@@ -5,7 +5,7 @@
"loc-args": [
"2 km da Foligno"
],
"loc-key": "Rilevato sisma forte a",
"loc-key": "Sisma segnalato da utente a",
"title-loc-key": "Allerta sismica in tempo reale"
},
"category": "notifica_con_mappa",
@@ -97,23 +97,8 @@ class NotificationService: UNNotificationServiceExtension {
// 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)
// don't show any images
break
default:
break
}
@@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 52;
objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
@@ -13,10 +13,10 @@
650247122A61832F001AC512 /* EQNBackgroundPositionDebugHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 650247112A61832F001AC512 /* EQNBackgroundPositionDebugHelper.swift */; };
650247152A618D7F001AC512 /* Foundation+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 650247142A618D7F001AC512 /* Foundation+Extensions.swift */; };
650B23AB2632CCD3007AE752 /* UIView+EQNExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 650B23AA2632CCD3007AE752 /* UIView+EQNExtensions.swift */; };
65172F532C25C496006D2A5C /* EQNSeismicAnnotationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65172F522C25C496006D2A5C /* EQNSeismicAnnotationView.swift */; };
651901B925F5358700CAFF20 /* EQNMapAnnotationSeismic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 651901B825F5358700CAFF20 /* EQNMapAnnotationSeismic.swift */; };
6525A82625E13FD4008FE0D0 /* SeismicNetworkAdvertiseTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6525A82525E13FD4008FE0D0 /* SeismicNetworkAdvertiseTableViewCell.swift */; };
652A3C6B2A8A757800719796 /* EQNBackgrounPositionDebugViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 652A3C6A2A8A757800719796 /* EQNBackgrounPositionDebugViewController.swift */; };
652C37BD26092B3C0068EC3B /* FiltersViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 652C37BC26092B3C0068EC3B /* FiltersViewModel.swift */; };
65355FFF25F38D3300BB57D2 /* SegnalazioniMapViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65355FFE25F38D3300BB57D2 /* SegnalazioniMapViewController.swift */; };
653604E9262348FA00B2B651 /* EQNBaseMapFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 653604E8262348FA00B2B651 /* EQNBaseMapFilter.swift */; };
653C67E225F3CC2E00FE52AC /* EQNCustomAnnotationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 653C67E125F3CC2E00FE52AC /* EQNCustomAnnotationView.swift */; };
@@ -36,13 +36,23 @@
65583A05261B83BE00ECA9F9 /* UIKit+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65583A04261B83BE00ECA9F9 /* UIKit+Extensions.swift */; };
6562C80725FFA6B100C85273 /* SeismicNetworkViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6562C80625FFA6B100C85273 /* SeismicNetworkViewModel.swift */; };
6563DAA42AAF515F0072D309 /* BackgroundTaskIdentifiable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6563DAA32AAF515F0072D309 /* BackgroundTaskIdentifiable.swift */; };
656D138F2C2225560094F597 /* SubscriptionDetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 656D138E2C2225560094F597 /* SubscriptionDetailsViewController.swift */; };
656D13912C22371F0094F597 /* SubscriptionDetailsTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 656D13902C22371F0094F597 /* SubscriptionDetailsTableViewCell.swift */; };
656E02162C1C4DF2008D0E92 /* EQNBaseContainerTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 656E02152C1C4DF2008D0E92 /* EQNBaseContainerTableViewCell.swift */; };
656EB9362A15FD16009DADF3 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 656EB9382A15FD16009DADF3 /* Localizable.stringsdict */; };
656EB9412A16288A009DADF3 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 656EB9382A15FD16009DADF3 /* Localizable.stringsdict */; };
6586971125F44C26009C0182 /* EQNBlurredCloseButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6586971025F44C26009C0182 /* EQNBlurredCloseButton.swift */; };
658BAB7B25FE67930015C454 /* EQNBaseMapRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 658BAB7A25FE67930015C454 /* EQNBaseMapRepresentable.swift */; };
658BC0292859A456009EECAA /* RealtimeAlertViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 658BC0282859A456009EECAA /* RealtimeAlertViewController.swift */; };
658BC02B2859A4D3009EECAA /* RealtimeAlertView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 658BC02A2859A4D3009EECAA /* RealtimeAlertView.swift */; };
6590EFF82C3004EA00F41420 /* EQNOfficialPushNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6590EFF72C3004EA00F41420 /* EQNOfficialPushNotification.swift */; };
65AB4A952C11BED400950DF7 /* SettingsSeismicNetworkNotificationsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65AB4A942C11BED400950DF7 /* SettingsSeismicNetworkNotificationsViewController.swift */; };
65AB4A992C11DFC200950DF7 /* EQNSettingSeismicNetworkNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65AB4A982C11DFC200950DF7 /* EQNSettingSeismicNetworkNotification.swift */; };
65AD23CE261B03D400E3B57C /* SubscriptionsDescriptionTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65AD23CD261B03D400E3B57C /* SubscriptionsDescriptionTableViewCell.swift */; };
65B16E1E2BDFA88D0020527E /* Solar in Frameworks */ = {isa = PBXBuildFile; productRef = 65B16E1D2BDFA88D0020527E /* Solar */; };
65B16E222BDFA8ED0020527E /* DZNEmptyDataSet in Frameworks */ = {isa = PBXBuildFile; productRef = 65B16E212BDFA8ED0020527E /* DZNEmptyDataSet */; };
65B16E262BDFB2A40020527E /* GoogleMobileAds in Frameworks */ = {isa = PBXBuildFile; productRef = 65B16E252BDFB2A40020527E /* GoogleMobileAds */; };
65B16E2A2BDFB39B0020527E /* FacebookCore in Frameworks */ = {isa = PBXBuildFile; productRef = 65B16E292BDFB39B0020527E /* FacebookCore */; };
65BBB22C26064BE6005D6CDF /* SegnalazioniLast24HoursCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65BBB22B26064BE6005D6CDF /* SegnalazioniLast24HoursCell.swift */; };
65CB83432915720400EE1E35 /* EQNUserData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65CB83422915720400EE1E35 /* EQNUserData.swift */; };
65D409942619BA34008CF356 /* SegnalazioniSendReportCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65D409932619BA34008CF356 /* SegnalazioniSendReportCell.swift */; };
@@ -50,10 +60,17 @@
65D507DC2A85227F005BDC57 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6502470C2A612E4A001AC512 /* Constants.swift */; };
65D9938A29219DEC00F2B0EB /* UIKit+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65583A04261B83BE00ECA9F9 /* UIKit+Extensions.swift */; };
65D9938C2922647800F2B0EB /* AppTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC52B8A424FCCD6900ABEBA6 /* AppTheme.swift */; };
65DB60F02C16F5B100164366 /* SettingsBaseTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65DB60EF2C16F5B100164366 /* SettingsBaseTableViewController.swift */; };
65DB60F22C16FA3600164366 /* SettingsRealTimeAlertsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65DB60F12C16FA3600164366 /* SettingsRealTimeAlertsViewController.swift */; };
65DB60F82C1714E100164366 /* EQNSettingUserReportNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65DB60F72C1714E100164366 /* EQNSettingUserReportNotification.swift */; };
65DB60FA2C17158D00164366 /* SettingsUserReportNotificationsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65DB60F92C17158D00164366 /* SettingsUserReportNotificationsViewController.swift */; };
65DB60FD2C172C4A00164366 /* EQNSettingRealTimeAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65DB60FC2C172C4A00164366 /* EQNSettingRealTimeAlert.swift */; };
65DBFB4B25E29DD60041CBA6 /* SeismicNetworksMapDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65DBFB4A25E29DD60041CBA6 /* SeismicNetworksMapDetailViewController.swift */; };
65DBFB7425E2BBF20041CBA6 /* GADTMediumTemplateView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 65DBFB6F25E2BBF20041CBA6 /* GADTMediumTemplateView.xib */; };
65DBFB7525E2BBF20041CBA6 /* GADTMediumTemplateView.m in Sources */ = {isa = PBXBuildFile; fileRef = 65DBFB7125E2BBF20041CBA6 /* GADTMediumTemplateView.m */; };
65DBFB7625E2BBF20041CBA6 /* GADTTemplateView.m in Sources */ = {isa = PBXBuildFile; fileRef = 65DBFB7225E2BBF20041CBA6 /* GADTTemplateView.m */; };
65E6AC6E2C2DB3B60073F8FE /* FirebaseCrashlytics in Frameworks */ = {isa = PBXBuildFile; productRef = 65E6AC6D2C2DB3B60073F8FE /* FirebaseCrashlytics */; };
65E6AC702C2DB3B60073F8FE /* FirebaseMessaging in Frameworks */ = {isa = PBXBuildFile; productRef = 65E6AC6F2C2DB3B60073F8FE /* FirebaseMessaging */; };
65EA58802A60269C0038EE9D /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 8C10B0BD2281FE7F00125C9F /* Localizable.strings */; };
65EA58822A60360D0038EE9D /* EQNRealtimePushNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65EA58812A60360D0038EE9D /* EQNRealtimePushNotification.swift */; };
65F9B49C2A8CA22800F13260 /* BackgroundTaskManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65F9B49B2A8CA22800F13260 /* BackgroundTaskManager.swift */; };
@@ -167,13 +184,11 @@
65FFDD6C292F70F700EA821B /* alert_sound.wav in Resources */ = {isa = PBXBuildFile; fileRef = DC958D662535788E00D73D4A /* alert_sound.wav */; };
8C10B0B92281FE7F00125C9F /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 8C10B0BD2281FE7F00125C9F /* Localizable.strings */; };
8C10B0BB2281FE7F00125C9F /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 8C10B0BD2281FE7F00125C9F /* Localizable.strings */; };
8C14113121ED3E5B00A59729 /* SettingsRealTimeAlertsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C14113021ED3E5B00A59729 /* SettingsRealTimeAlertsViewController.m */; };
8C14113721EE502800A59729 /* EQNAllertaSismica.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C14113621EE502800A59729 /* EQNAllertaSismica.m */; };
8C465D9A21F653AB00F04673 /* Assets.xcassets in Frameworks */ = {isa = PBXBuildFile; fileRef = 8CBD3DD12149B9AD0070C963 /* Assets.xcassets */; };
8C465D9F21F7BE0600F04673 /* Assets.xcassets in Sources */ = {isa = PBXBuildFile; fileRef = 8CBD3DD12149B9AD0070C963 /* Assets.xcassets */; };
8C483CAE21FDA53B00259FD2 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8C483CAD21FDA53B00259FD2 /* StoreKit.framework */; };
8C483CB821FDACD300259FD2 /* IAPHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C483CB721FDACD300259FD2 /* IAPHelper.swift */; };
8C483CBC21FDACE500259FD2 /* VersioneProProducts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C483CBB21FDACE500259FD2 /* VersioneProProducts.swift */; };
8C483CBC21FDACE500259FD2 /* EQNInAppProducts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C483CBB21FDACE500259FD2 /* EQNInAppProducts.swift */; };
8C4DD4F9228237E000AE77ED /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 8C4DD4FB228237E000AE77ED /* InfoPlist.strings */; };
8C4E343F215012FA008B0D2A /* EQNManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C4E343E215012FA008B0D2A /* EQNManager.m */; };
8C4E34422152B5E8008B0D2A /* EQNRilevamento.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C4E34412152B5E8008B0D2A /* EQNRilevamento.m */; };
@@ -194,10 +209,6 @@
8CBD3DD52149B9AD0070C963 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8CBD3DD32149B9AD0070C963 /* LaunchScreen.storyboard */; };
8CBD3DD82149B9AD0070C963 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CBD3DD72149B9AD0070C963 /* main.m */; };
8CC2B44F214AC7F8002ED1B2 /* CoreMotion.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8CC2B44E214AC7F8002ED1B2 /* CoreMotion.framework */; };
8CCE164E21E7BACE00173CD9 /* EQNNotificheSegnalazioniUtente.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CCE164D21E7BACE00173CD9 /* EQNNotificheSegnalazioniUtente.m */; };
8CCE165121E7BAEC00173CD9 /* EQNNotificheReteSismiche.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CCE165021E7BAEC00173CD9 /* EQNNotificheReteSismiche.m */; };
8CCE165521EA378800173CD9 /* SettingsUserReportAlertsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CCE165421EA378800173CD9 /* SettingsUserReportAlertsViewController.m */; };
8CCE165821EB1E0000173CD9 /* SettingsSeismicNetworkAlertsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CCE165721EB1E0000173CD9 /* SettingsSeismicNetworkAlertsViewController.m */; };
8CF05B57218C93BA0055012B /* EQNUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF05B56218C93BA0055012B /* EQNUtility.m */; };
8CF12CD321DE49B600613AC5 /* UserNotifications.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8CF12CD221DE49B600613AC5 /* UserNotifications.framework */; };
8CF12CD521DE49B600613AC5 /* UserNotificationsUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8CF12CD421DE49B600613AC5 /* UserNotificationsUI.framework */; };
@@ -210,7 +221,6 @@
8CF66053214C12DC009F4314 /* EQNMath.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF66052214C12DC009F4314 /* EQNMath.m */; };
8CF66058214C566B009F4314 /* ServerRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF66055214C566A009F4314 /* ServerRequest.m */; };
8CF66059214C566B009F4314 /* Reachability.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF66056214C566A009F4314 /* Reachability.m */; };
C89115902FEA7A0A31514912 /* Pods_Earthquake_Network.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 25A8BFFE29D46740E8A8A7A3 /* Pods_Earthquake_Network.framework */; };
DC03BEAB250BC0A60084769B /* EQNRoundedButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC03BEAA250BC0A60084769B /* EQNRoundedButton.swift */; };
DC08803F24F5A89000186D97 /* SettingEnableTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC08803E24F5A89000186D97 /* SettingEnableTableViewCell.swift */; };
DC08804124F5B41400186D97 /* SettingSliderTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC08804024F5B41400186D97 /* SettingSliderTableViewCell.swift */; };
@@ -218,7 +228,6 @@
DC0AE1B92538204100111307 /* EQNSegnalazione.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C593E89217BA2470008B260 /* EQNSegnalazione.m */; };
DC0AE1BA2538204100111307 /* EQNPastquakes.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF4F4DA216D44930057110B /* EQNPastquakes.m */; };
DC0E551324F8063300D54270 /* SettingSegmentedTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC0E551224F8063300D54270 /* SettingSegmentedTableViewCell.swift */; };
DC27EB2F24F6EBE000ACBFE0 /* SettingsSeismicNetworksViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC27EB2E24F6EBE000ACBFE0 /* SettingsSeismicNetworksViewController.swift */; };
DC2814302519C24400C1AFF7 /* SeismicNetworkTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC28142F2519C24400C1AFF7 /* SeismicNetworkTableViewCell.swift */; };
DC2814382519C56100C1AFF7 /* SeismicNetworksViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC2814372519C56100C1AFF7 /* SeismicNetworksViewController.swift */; };
DC3B5429257FCBCA00C0B6A5 /* EQNReteSmartphone.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC3B5428257FCBCA00C0B6A5 /* EQNReteSmartphone.swift */; };
@@ -226,11 +235,9 @@
DC3CE50A250EB7A8005A7DD5 /* EQNGenericPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC3CE509250EB7A8005A7DD5 /* EQNGenericPickerViewController.swift */; };
DC47D1BC252A0C2B004119F6 /* AlertsPastEartquakesTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC47D1BB252A0C2B004119F6 /* AlertsPastEartquakesTableViewCell.swift */; };
DC4B67612517833F00634277 /* EQNSeismic.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC4B67602517833F00634277 /* EQNSeismic.swift */; };
DC52B8A224FC145500ABEBA6 /* SettingsBaseViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DC52B8A124FC145500ABEBA6 /* SettingsBaseViewController.m */; };
DC52B8A524FCCD6900ABEBA6 /* AppTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC52B8A424FCCD6900ABEBA6 /* AppTheme.swift */; };
DC646F27252B694A000AA5FD /* AlertsSeismicNotificationExpandedTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC646F26252B694A000AA5FD /* AlertsSeismicNotificationExpandedTableViewCell.swift */; };
DC646F32252B698B000AA5FD /* AlertsSeismicNotificationCompactTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC646F31252B698B000AA5FD /* AlertsSeismicNotificationCompactTableViewCell.swift */; };
DC65B391250F243E00251693 /* SeismicSettingsNetworksViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC65B390250F243E00251693 /* SeismicSettingsNetworksViewController.swift */; };
DC7EEE4A252A11C9004B4A2A /* AlertsSmartphoneNetworkTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC7EEE49252A11C9004B4A2A /* AlertsSmartphoneNetworkTableViewCell.swift */; };
DC7EEE4F252A1634004B4A2A /* AlertsPriorityServiceTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC7EEE4E252A1634004B4A2A /* AlertsPriorityServiceTableViewCell.swift */; };
DC886A5D24E92D5500F7A5D3 /* EQNBaseViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DC886A5C24E92D5500F7A5D3 /* EQNBaseViewController.m */; };
@@ -239,14 +246,11 @@
DC99A50324E66E270071BC9F /* EQNCommandProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC99A50224E66E270071BC9F /* EQNCommandProtocol.swift */; };
DC99A50524E66E430071BC9F /* EQNAppearanceCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC99A50424E66E430071BC9F /* EQNAppearanceCommand.swift */; };
DC99A50724E66E5F0071BC9F /* EQNStartupCommandsBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC99A50624E66E5F0071BC9F /* EQNStartupCommandsBuilder.swift */; };
DCA5B6E7252E4BD8002AEC96 /* EQNBaseTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCA5B6E6252E4BD8002AEC96 /* EQNBaseTableViewCell.swift */; };
DCAA913F24F68A1D00145A3D /* SettingMultivaluesTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCAA913E24F68A1D00145A3D /* SettingMultivaluesTableViewCell.swift */; };
DCB28CEE24FB8400001F557E /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCB28CED24FB8400001F557E /* SettingsViewController.swift */; };
DCB45BC8250E86E100DB2D0C /* SeismicSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCB45BC7250E86E100DB2D0C /* SeismicSettingsViewController.swift */; };
DCB528212560161C005288E5 /* AlertSimulatorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCB528202560161C005288E5 /* AlertSimulatorViewController.swift */; };
DCBB267A24D1E7F500F04559 /* SubscriptionsHeaderTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCBB267924D1E7F500F04559 /* SubscriptionsHeaderTableViewCell.swift */; };
DCBB267E24D1EA2000F04559 /* SubscriptionProductTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCBB267D24D1EA2000F04559 /* SubscriptionProductTableViewCell.swift */; };
DCBB268024D1ECE200F04559 /* SubscriptionDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCBB267F24D1ECE200F04559 /* SubscriptionDetailViewController.swift */; };
DCBB84F0252CFC4600F12633 /* AlertsNoLocationTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCBB84EF252CFC4600F12633 /* AlertsNoLocationTableViewCell.swift */; };
DCC23DEC24D281CE003A2404 /* SubscriptionsActiveTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCC23DEB24D281CE003A2404 /* SubscriptionsActiveTableViewCell.swift */; };
DCC23DEF24D28F58003A2404 /* EQNEdgeInsetLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCC23DEE24D28F58003A2404 /* EQNEdgeInsetLabel.swift */; };
@@ -299,16 +303,14 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
25A8BFFE29D46740E8A8A7A3 /* Pods_Earthquake_Network.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Earthquake_Network.framework; sourceTree = BUILT_PRODUCTS_DIR; };
40CD2E5581CF2FA3D52F392D /* Pods-Earthquake Network.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Earthquake Network.release.xcconfig"; path = "Pods/Target Support Files/Pods-Earthquake Network/Pods-Earthquake Network.release.xcconfig"; sourceTree = "<group>"; };
6502470C2A612E4A001AC512 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
650247112A61832F001AC512 /* EQNBackgroundPositionDebugHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNBackgroundPositionDebugHelper.swift; sourceTree = "<group>"; };
650247142A618D7F001AC512 /* Foundation+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Foundation+Extensions.swift"; sourceTree = "<group>"; };
650B23AA2632CCD3007AE752 /* UIView+EQNExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+EQNExtensions.swift"; sourceTree = "<group>"; };
65172F522C25C496006D2A5C /* EQNSeismicAnnotationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNSeismicAnnotationView.swift; sourceTree = "<group>"; };
651901B825F5358700CAFF20 /* EQNMapAnnotationSeismic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNMapAnnotationSeismic.swift; sourceTree = "<group>"; };
6525A82525E13FD4008FE0D0 /* SeismicNetworkAdvertiseTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeismicNetworkAdvertiseTableViewCell.swift; sourceTree = "<group>"; };
652A3C6A2A8A757800719796 /* EQNBackgrounPositionDebugViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNBackgrounPositionDebugViewController.swift; sourceTree = "<group>"; };
652C37BC26092B3C0068EC3B /* FiltersViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FiltersViewModel.swift; sourceTree = "<group>"; };
65355FFE25F38D3300BB57D2 /* SegnalazioniMapViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SegnalazioniMapViewController.swift; sourceTree = "<group>"; };
653604E8262348FA00B2B651 /* EQNBaseMapFilter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNBaseMapFilter.swift; sourceTree = "<group>"; };
653B791F2934B6DA00E8FFFB /* EQNNotificationService.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = EQNNotificationService.entitlements; sourceTree = "<group>"; };
@@ -324,6 +326,9 @@
65583A04261B83BE00ECA9F9 /* UIKit+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIKit+Extensions.swift"; sourceTree = "<group>"; };
6562C80625FFA6B100C85273 /* SeismicNetworkViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeismicNetworkViewModel.swift; sourceTree = "<group>"; };
6563DAA32AAF515F0072D309 /* BackgroundTaskIdentifiable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundTaskIdentifiable.swift; sourceTree = "<group>"; };
656D138E2C2225560094F597 /* SubscriptionDetailsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionDetailsViewController.swift; sourceTree = "<group>"; };
656D13902C22371F0094F597 /* SubscriptionDetailsTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionDetailsTableViewCell.swift; sourceTree = "<group>"; };
656E02152C1C4DF2008D0E92 /* EQNBaseContainerTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNBaseContainerTableViewCell.swift; sourceTree = "<group>"; };
656EB9372A15FD16009DADF3 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
656EB9392A15FD19009DADF3 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ar; path = ar.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
656EB93A2A15FD1B009DADF3 /* hr-HR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "hr-HR"; path = "hr-HR.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
@@ -339,6 +344,7 @@
658BC02A2859A4D3009EECAA /* RealtimeAlertView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RealtimeAlertView.swift; sourceTree = "<group>"; };
658F19692A0D1F8F00BECC05 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/InfoPlist.strings; sourceTree = "<group>"; };
658F196A2A0D1F8F00BECC05 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Localizable.strings; sourceTree = "<group>"; };
6590EFF72C3004EA00F41420 /* EQNOfficialPushNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNOfficialPushNotification.swift; sourceTree = "<group>"; };
65A4D5AA26280A24003918E0 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/InfoPlist.strings; sourceTree = "<group>"; };
65A4D5AB26280A24003918E0 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = "<group>"; };
65A4D5AC26280A56003918E0 /* el-GR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "el-GR"; path = "el-GR.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
@@ -349,10 +355,17 @@
65A4D5B126280B61003918E0 /* tr-TR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "tr-TR"; path = "tr-TR.lproj/Localizable.strings"; sourceTree = "<group>"; };
65A4D5B526281126003918E0 /* id-ID */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "id-ID"; path = "id-ID.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
65A4D5B626281126003918E0 /* id-ID */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "id-ID"; path = "id-ID.lproj/Localizable.strings"; sourceTree = "<group>"; };
65AB4A942C11BED400950DF7 /* SettingsSeismicNetworkNotificationsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSeismicNetworkNotificationsViewController.swift; sourceTree = "<group>"; };
65AB4A982C11DFC200950DF7 /* EQNSettingSeismicNetworkNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNSettingSeismicNetworkNotification.swift; sourceTree = "<group>"; };
65AD23CD261B03D400E3B57C /* SubscriptionsDescriptionTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionsDescriptionTableViewCell.swift; sourceTree = "<group>"; };
65BBB22B26064BE6005D6CDF /* SegnalazioniLast24HoursCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SegnalazioniLast24HoursCell.swift; sourceTree = "<group>"; };
65CB83422915720400EE1E35 /* EQNUserData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNUserData.swift; sourceTree = "<group>"; };
65D409932619BA34008CF356 /* SegnalazioniSendReportCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SegnalazioniSendReportCell.swift; sourceTree = "<group>"; };
65DB60EF2C16F5B100164366 /* SettingsBaseTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsBaseTableViewController.swift; sourceTree = "<group>"; };
65DB60F12C16FA3600164366 /* SettingsRealTimeAlertsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsRealTimeAlertsViewController.swift; sourceTree = "<group>"; };
65DB60F72C1714E100164366 /* EQNSettingUserReportNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNSettingUserReportNotification.swift; sourceTree = "<group>"; };
65DB60F92C17158D00164366 /* SettingsUserReportNotificationsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsUserReportNotificationsViewController.swift; sourceTree = "<group>"; };
65DB60FC2C172C4A00164366 /* EQNSettingRealTimeAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNSettingRealTimeAlert.swift; sourceTree = "<group>"; };
65DBFB4A25E29DD60041CBA6 /* SeismicNetworksMapDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeismicNetworksMapDetailViewController.swift; sourceTree = "<group>"; };
65DBFB6F25E2BBF20041CBA6 /* GADTMediumTemplateView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = GADTMediumTemplateView.xib; sourceTree = "<group>"; };
65DBFB7025E2BBF20041CBA6 /* GADTTemplateView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GADTTemplateView.h; sourceTree = "<group>"; };
@@ -472,16 +485,12 @@
8C10B0BE2281FE9E00125C9F /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
8C10B0BF2281FEA000125C9F /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
8C10B0C42282360900125C9F /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
8C14112F21ED3E5B00A59729 /* SettingsRealTimeAlertsViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SettingsRealTimeAlertsViewController.h; sourceTree = "<group>"; };
8C14113021ED3E5B00A59729 /* SettingsRealTimeAlertsViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SettingsRealTimeAlertsViewController.m; sourceTree = "<group>"; };
8C14113521EE502800A59729 /* EQNAllertaSismica.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EQNAllertaSismica.h; sourceTree = "<group>"; };
8C14113621EE502800A59729 /* EQNAllertaSismica.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EQNAllertaSismica.m; sourceTree = "<group>"; };
8C465D9721F6539700F04673 /* Earthquake Network.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = "Earthquake Network.xcodeproj"; sourceTree = "<group>"; };
8C483CAD21FDA53B00259FD2 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; };
8C483CB021FDA8C700259FD2 /* Earthquake Network-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Earthquake Network-Bridging-Header.h"; sourceTree = "<group>"; };
8C483CB621FDACD100259FD2 /* EQNNotificationContent-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "EQNNotificationContent-Bridging-Header.h"; sourceTree = "<group>"; };
8C483CB721FDACD300259FD2 /* IAPHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IAPHelper.swift; sourceTree = "<group>"; };
8C483CBB21FDACE500259FD2 /* VersioneProProducts.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VersioneProProducts.swift; sourceTree = "<group>"; };
8C483CBB21FDACE500259FD2 /* EQNInAppProducts.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EQNInAppProducts.swift; sourceTree = "<group>"; };
8C4DD4FA228237E000AE77ED /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
8C4DD4FC228237E200AE77ED /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/InfoPlist.strings; sourceTree = "<group>"; };
8C4DD4FD228237E400AE77ED /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = "<group>"; };
@@ -518,14 +527,6 @@
8CBD3DD72149B9AD0070C963 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
8CBD3DDE2149BA300070C963 /* Earthquake Network.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Earthquake Network.entitlements"; sourceTree = "<group>"; };
8CC2B44E214AC7F8002ED1B2 /* CoreMotion.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMotion.framework; path = System/Library/Frameworks/CoreMotion.framework; sourceTree = SDKROOT; };
8CCE164C21E7BACE00173CD9 /* EQNNotificheSegnalazioniUtente.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EQNNotificheSegnalazioniUtente.h; sourceTree = "<group>"; };
8CCE164D21E7BACE00173CD9 /* EQNNotificheSegnalazioniUtente.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EQNNotificheSegnalazioniUtente.m; sourceTree = "<group>"; };
8CCE164F21E7BAEC00173CD9 /* EQNNotificheReteSismiche.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EQNNotificheReteSismiche.h; sourceTree = "<group>"; };
8CCE165021E7BAEC00173CD9 /* EQNNotificheReteSismiche.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EQNNotificheReteSismiche.m; sourceTree = "<group>"; };
8CCE165321EA378800173CD9 /* SettingsUserReportAlertsViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SettingsUserReportAlertsViewController.h; sourceTree = "<group>"; };
8CCE165421EA378800173CD9 /* SettingsUserReportAlertsViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SettingsUserReportAlertsViewController.m; sourceTree = "<group>"; };
8CCE165621EB1E0000173CD9 /* SettingsSeismicNetworkAlertsViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SettingsSeismicNetworkAlertsViewController.h; sourceTree = "<group>"; };
8CCE165721EB1E0000173CD9 /* SettingsSeismicNetworkAlertsViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SettingsSeismicNetworkAlertsViewController.m; sourceTree = "<group>"; };
8CF05B55218C93BA0055012B /* EQNUtility.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EQNUtility.h; sourceTree = "<group>"; };
8CF05B56218C93BA0055012B /* EQNUtility.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EQNUtility.m; sourceTree = "<group>"; };
8CF12CD121DE49B600613AC5 /* EQNNotificationContent.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = EQNNotificationContent.appex; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -548,12 +549,10 @@
8CF66055214C566A009F4314 /* ServerRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ServerRequest.m; sourceTree = "<group>"; };
8CF66056214C566A009F4314 /* Reachability.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Reachability.m; sourceTree = "<group>"; };
8CF66057214C566B009F4314 /* ServerRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ServerRequest.h; sourceTree = "<group>"; };
C4FB0D7EEA34F8222369E1BB /* Pods-Earthquake Network.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Earthquake Network.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Earthquake Network/Pods-Earthquake Network.debug.xcconfig"; sourceTree = "<group>"; };
DC03BEAA250BC0A60084769B /* EQNRoundedButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNRoundedButton.swift; sourceTree = "<group>"; };
DC08803E24F5A89000186D97 /* SettingEnableTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingEnableTableViewCell.swift; sourceTree = "<group>"; };
DC08804024F5B41400186D97 /* SettingSliderTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingSliderTableViewCell.swift; sourceTree = "<group>"; };
DC0E551224F8063300D54270 /* SettingSegmentedTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingSegmentedTableViewCell.swift; sourceTree = "<group>"; };
DC27EB2E24F6EBE000ACBFE0 /* SettingsSeismicNetworksViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSeismicNetworksViewController.swift; sourceTree = "<group>"; };
DC28142F2519C24400C1AFF7 /* SeismicNetworkTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeismicNetworkTableViewCell.swift; sourceTree = "<group>"; };
DC2814372519C56100C1AFF7 /* SeismicNetworksViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeismicNetworksViewController.swift; sourceTree = "<group>"; };
DC3B5428257FCBCA00C0B6A5 /* EQNReteSmartphone.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNReteSmartphone.swift; sourceTree = "<group>"; };
@@ -562,12 +561,9 @@
DC414C0024CDA09A008D9AE4 /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = CHANGELOG.md; path = ../CHANGELOG.md; sourceTree = "<group>"; };
DC47D1BB252A0C2B004119F6 /* AlertsPastEartquakesTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertsPastEartquakesTableViewCell.swift; sourceTree = "<group>"; };
DC4B67602517833F00634277 /* EQNSeismic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNSeismic.swift; sourceTree = "<group>"; };
DC52B8A024FC145500ABEBA6 /* SettingsBaseViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SettingsBaseViewController.h; sourceTree = "<group>"; };
DC52B8A124FC145500ABEBA6 /* SettingsBaseViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SettingsBaseViewController.m; sourceTree = "<group>"; };
DC52B8A424FCCD6900ABEBA6 /* AppTheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppTheme.swift; sourceTree = "<group>"; };
DC646F26252B694A000AA5FD /* AlertsSeismicNotificationExpandedTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertsSeismicNotificationExpandedTableViewCell.swift; sourceTree = "<group>"; };
DC646F31252B698B000AA5FD /* AlertsSeismicNotificationCompactTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertsSeismicNotificationCompactTableViewCell.swift; sourceTree = "<group>"; };
DC65B390250F243E00251693 /* SeismicSettingsNetworksViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeismicSettingsNetworksViewController.swift; sourceTree = "<group>"; };
DC7EEE49252A11C9004B4A2A /* AlertsSmartphoneNetworkTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertsSmartphoneNetworkTableViewCell.swift; sourceTree = "<group>"; };
DC7EEE4E252A1634004B4A2A /* AlertsPriorityServiceTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertsPriorityServiceTableViewCell.swift; sourceTree = "<group>"; };
DC886A5B24E92D5500F7A5D3 /* EQNBaseViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EQNBaseViewController.h; sourceTree = "<group>"; };
@@ -577,14 +573,11 @@
DC99A50224E66E270071BC9F /* EQNCommandProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNCommandProtocol.swift; sourceTree = "<group>"; };
DC99A50424E66E430071BC9F /* EQNAppearanceCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNAppearanceCommand.swift; sourceTree = "<group>"; };
DC99A50624E66E5F0071BC9F /* EQNStartupCommandsBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNStartupCommandsBuilder.swift; sourceTree = "<group>"; };
DCA5B6E6252E4BD8002AEC96 /* EQNBaseTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNBaseTableViewCell.swift; sourceTree = "<group>"; };
DCAA913E24F68A1D00145A3D /* SettingMultivaluesTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingMultivaluesTableViewCell.swift; sourceTree = "<group>"; };
DCB28CED24FB8400001F557E /* SettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = "<group>"; };
DCB45BC7250E86E100DB2D0C /* SeismicSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeismicSettingsViewController.swift; sourceTree = "<group>"; };
DCB528202560161C005288E5 /* AlertSimulatorViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertSimulatorViewController.swift; sourceTree = "<group>"; };
DCBB267924D1E7F500F04559 /* SubscriptionsHeaderTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionsHeaderTableViewCell.swift; sourceTree = "<group>"; };
DCBB267D24D1EA2000F04559 /* SubscriptionProductTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionProductTableViewCell.swift; sourceTree = "<group>"; };
DCBB267F24D1ECE200F04559 /* SubscriptionDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionDetailViewController.swift; sourceTree = "<group>"; };
DCBB84EF252CFC4600F12633 /* AlertsNoLocationTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertsNoLocationTableViewCell.swift; sourceTree = "<group>"; };
DCC23DEB24D281CE003A2404 /* SubscriptionsActiveTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionsActiveTableViewCell.swift; sourceTree = "<group>"; };
DCC23DEE24D28F58003A2404 /* EQNEdgeInsetLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNEdgeInsetLabel.swift; sourceTree = "<group>"; };
@@ -619,12 +612,17 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
65B16E222BDFA8ED0020527E /* DZNEmptyDataSet in Frameworks */,
65B16E2A2BDFB39B0020527E /* FacebookCore in Frameworks */,
65B16E1E2BDFA88D0020527E /* Solar in Frameworks */,
65506C052A8F950000AB6448 /* CoreLocation.framework in Frameworks */,
65B16E262BDFB2A40020527E /* GoogleMobileAds in Frameworks */,
6552C13829262119008E723C /* Shogun in Frameworks */,
8C5EA22D21763103002DC156 /* MapKit.framework in Frameworks */,
65E6AC6E2C2DB3B60073F8FE /* FirebaseCrashlytics in Frameworks */,
8CC2B44F214AC7F8002ED1B2 /* CoreMotion.framework in Frameworks */,
65E6AC702C2DB3B60073F8FE /* FirebaseMessaging in Frameworks */,
8C483CAE21FDA53B00259FD2 /* StoreKit.framework in Frameworks */,
C89115902FEA7A0A31514912 /* Pods_Earthquake_Network.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -688,7 +686,6 @@
isa = PBXGroup;
children = (
DC974AFE251748B300A139EC /* SeismicFiltersViewController.swift */,
652C37BC26092B3C0068EC3B /* FiltersViewModel.swift */,
);
path = Filters;
sourceTree = "<group>";
@@ -841,7 +838,6 @@
65FFDC93292F672B00EA821B /* EQNNotificationService */,
8CBD3DC32149B9AD0070C963 /* Products */,
8CC2B44D214AC7F8002ED1B2 /* Frameworks */,
A7982CE92BD5D51B8E2AA92F /* Pods */,
);
sourceTree = "<group>";
};
@@ -884,7 +880,6 @@
8CC2B44E214AC7F8002ED1B2 /* CoreMotion.framework */,
8CF12CD221DE49B600613AC5 /* UserNotifications.framework */,
8CF12CD421DE49B600613AC5 /* UserNotificationsUI.framework */,
25A8BFFE29D46740E8A8A7A3 /* Pods_Earthquake_Network.framework */,
);
name = Frameworks;
sourceTree = "<group>";
@@ -902,15 +897,6 @@
path = EQNNotificationContent;
sourceTree = "<group>";
};
A7982CE92BD5D51B8E2AA92F /* Pods */ = {
isa = PBXGroup;
children = (
C4FB0D7EEA34F8222369E1BB /* Pods-Earthquake Network.debug.xcconfig */,
40CD2E5581CF2FA3D52F392D /* Pods-Earthquake Network.release.xcconfig */,
);
name = Pods;
sourceTree = "<group>";
};
DC10563F251E7EC0002579BB /* Extensions */ = {
isa = PBXGroup;
children = (
@@ -929,8 +915,6 @@
6562C80625FFA6B100C85273 /* SeismicNetworkViewModel.swift */,
DC28142E2519C21400C1AFF7 /* Cells */,
65F0857E2609288E0002615C /* Filters */,
DCB45BC7250E86E100DB2D0C /* SeismicSettingsViewController.swift */,
DC65B390250F243E00251693 /* SeismicSettingsNetworksViewController.swift */,
DC2814372519C56100C1AFF7 /* SeismicNetworksViewController.swift */,
DCC76BD7251F56050005C4DC /* SeismicCardSettingsViewController.swift */,
65DBFB4A25E29DD60041CBA6 /* SeismicNetworksMapDetailViewController.swift */,
@@ -1104,7 +1088,7 @@
DC99A50124E66DFB0071BC9F /* Commands */,
DCEFF21824F587CC009D3FE1 /* Settings */,
65DBFB5225E2A2580041CBA6 /* Map annotation */,
8C483CBB21FDACE500259FD2 /* VersioneProProducts.swift */,
8C483CBB21FDACE500259FD2 /* EQNInAppProducts.swift */,
8C483CB721FDACD300259FD2 /* IAPHelper.swift */,
DCF10DC524D2B8C7009F34C3 /* EQNPurchaseUtility.swift */,
DCF10DCC24D2C935009F34C3 /* EQNPurchaseAvailability.swift */,
@@ -1128,6 +1112,7 @@
8CF4F4DA216D44930057110B /* EQNPastquakes.m */,
65EA58812A60360D0038EE9D /* EQNRealtimePushNotification.swift */,
6552C1452926DBA1008E723C /* AppPreferences.swift */,
6590EFF72C3004EA00F41420 /* EQNOfficialPushNotification.swift */,
);
path = Models;
sourceTree = "<group>";
@@ -1155,12 +1140,13 @@
DCC23DED24D28F41003A2404 /* UI */ = {
isa = PBXGroup;
children = (
DC52B8A424FCCD6900ABEBA6 /* AppTheme.swift */,
DCC23DEE24D28F58003A2404 /* EQNEdgeInsetLabel.swift */,
DC03BEAA250BC0A60084769B /* EQNRoundedButton.swift */,
DC52B8A424FCCD6900ABEBA6 /* AppTheme.swift */,
DCA5B6E6252E4BD8002AEC96 /* EQNBaseTableViewCell.swift */,
653C67E125F3CC2E00FE52AC /* EQNCustomAnnotationView.swift */,
6586971025F44C26009C0182 /* EQNBlurredCloseButton.swift */,
656E02152C1C4DF2008D0E92 /* EQNBaseContainerTableViewCell.swift */,
653C67E125F3CC2E00FE52AC /* EQNCustomAnnotationView.swift */,
65172F522C25C496006D2A5C /* EQNSeismicAnnotationView.swift */,
);
path = UI;
sourceTree = "<group>";
@@ -1170,10 +1156,11 @@
children = (
DC3BA11024D1A9C90062EE7F /* SubscriptionsViewController.swift */,
DCC23DEB24D281CE003A2404 /* SubscriptionsActiveTableViewCell.swift */,
DCBB267924D1E7F500F04559 /* SubscriptionsHeaderTableViewCell.swift */,
65AD23CD261B03D400E3B57C /* SubscriptionsDescriptionTableViewCell.swift */,
DCBB267924D1E7F500F04559 /* SubscriptionsHeaderTableViewCell.swift */,
DCBB267D24D1EA2000F04559 /* SubscriptionProductTableViewCell.swift */,
DCBB267F24D1ECE200F04559 /* SubscriptionDetailViewController.swift */,
656D138E2C2225560094F597 /* SubscriptionDetailsViewController.swift */,
656D13902C22371F0094F597 /* SubscriptionDetailsTableViewCell.swift */,
);
path = InApp;
sourceTree = "<group>";
@@ -1183,15 +1170,10 @@
children = (
DCEFF21124F581F9009D3FE1 /* Cells */,
DCB28CED24FB8400001F557E /* SettingsViewController.swift */,
DC52B8A024FC145500ABEBA6 /* SettingsBaseViewController.h */,
DC52B8A124FC145500ABEBA6 /* SettingsBaseViewController.m */,
8CCE165321EA378800173CD9 /* SettingsUserReportAlertsViewController.h */,
8CCE165421EA378800173CD9 /* SettingsUserReportAlertsViewController.m */,
8CCE165621EB1E0000173CD9 /* SettingsSeismicNetworkAlertsViewController.h */,
8CCE165721EB1E0000173CD9 /* SettingsSeismicNetworkAlertsViewController.m */,
DC27EB2E24F6EBE000ACBFE0 /* SettingsSeismicNetworksViewController.swift */,
8C14112F21ED3E5B00A59729 /* SettingsRealTimeAlertsViewController.h */,
8C14113021ED3E5B00A59729 /* SettingsRealTimeAlertsViewController.m */,
65DB60EF2C16F5B100164366 /* SettingsBaseTableViewController.swift */,
65DB60F92C17158D00164366 /* SettingsUserReportNotificationsViewController.swift */,
65AB4A942C11BED400950DF7 /* SettingsSeismicNetworkNotificationsViewController.swift */,
65DB60F12C16FA3600164366 /* SettingsRealTimeAlertsViewController.swift */,
);
path = Settings;
sourceTree = "<group>";
@@ -1214,12 +1196,9 @@
isa = PBXGroup;
children = (
DCEFF21924F587E3009D3FE1 /* SettingItem.swift */,
8CCE164C21E7BACE00173CD9 /* EQNNotificheSegnalazioniUtente.h */,
8CCE164D21E7BACE00173CD9 /* EQNNotificheSegnalazioniUtente.m */,
8CCE164F21E7BAEC00173CD9 /* EQNNotificheReteSismiche.h */,
8CCE165021E7BAEC00173CD9 /* EQNNotificheReteSismiche.m */,
8C14113521EE502800A59729 /* EQNAllertaSismica.h */,
8C14113621EE502800A59729 /* EQNAllertaSismica.m */,
65DB60F72C1714E100164366 /* EQNSettingUserReportNotification.swift */,
65AB4A982C11DFC200950DF7 /* EQNSettingSeismicNetworkNotification.swift */,
65DB60FC2C172C4A00164366 /* EQNSettingRealTimeAlert.swift */,
);
path = Settings;
sourceTree = "<group>";
@@ -1259,12 +1238,10 @@
isa = PBXNativeTarget;
buildConfigurationList = 8CBD3DDB2149B9AD0070C963 /* Build configuration list for PBXNativeTarget "Earthquake Network" */;
buildPhases = (
566F2B7433267D7429970520 /* [CP] Check Pods Manifest.lock */,
8CBD3DBE2149B9AD0070C963 /* Sources */,
8CBD3DBF2149B9AD0070C963 /* Frameworks */,
8CBD3DC02149B9AD0070C963 /* Resources */,
8CADAAA521B98C550044E256 /* Embed Foundation Extensions */,
213FE27D012BFA5BA1F850E2 /* [CP] Embed Pods Frameworks */,
DCF07F9824D40DB600DCCA63 /* ShellScript */,
);
buildRules = (
@@ -1276,6 +1253,12 @@
name = "Earthquake Network";
packageProductDependencies = (
6552C13729262119008E723C /* Shogun */,
65B16E1D2BDFA88D0020527E /* Solar */,
65B16E212BDFA8ED0020527E /* DZNEmptyDataSet */,
65B16E252BDFB2A40020527E /* GoogleMobileAds */,
65B16E292BDFB39B0020527E /* FacebookCore */,
65E6AC6D2C2DB3B60073F8FE /* FirebaseCrashlytics */,
65E6AC6F2C2DB3B60073F8FE /* FirebaseMessaging */,
);
productName = "Earthquake Network";
productReference = 8CBD3DC22149B9AD0070C963 /* Earthquake Network.app */;
@@ -1356,6 +1339,11 @@
mainGroup = 8CBD3DB92149B9AD0070C963;
packageReferences = (
6552C13629262119008E723C /* XCRemoteSwiftPackageReference "Shogun" */,
65B16E1C2BDFA88D0020527E /* XCRemoteSwiftPackageReference "Solar" */,
65B16E202BDFA8ED0020527E /* XCRemoteSwiftPackageReference "DZNEmptyDataSet" */,
65B16E242BDFB2A40020527E /* XCRemoteSwiftPackageReference "swift-package-manager-google-mobile-ads" */,
65B16E282BDFB39B0020527E /* XCRemoteSwiftPackageReference "facebook-ios-sdk" */,
65E6AC6C2C2DB3B60073F8FE /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */,
);
productRefGroup = 8CBD3DC32149B9AD0070C963 /* Products */;
projectDirPath = "";
@@ -1515,74 +1503,6 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
213FE27D012BFA5BA1F850E2 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Earthquake Network/Pods-Earthquake Network-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/DZNEmptyDataSet/DZNEmptyDataSet.framework",
"${BUILT_PRODUCTS_DIR}/FirebaseCore/FirebaseCore.framework",
"${BUILT_PRODUCTS_DIR}/FirebaseCoreExtension/FirebaseCoreExtension.framework",
"${BUILT_PRODUCTS_DIR}/FirebaseCoreInternal/FirebaseCoreInternal.framework",
"${BUILT_PRODUCTS_DIR}/FirebaseCrashlytics/FirebaseCrashlytics.framework",
"${BUILT_PRODUCTS_DIR}/FirebaseInstallations/FirebaseInstallations.framework",
"${BUILT_PRODUCTS_DIR}/FirebaseMessaging/FirebaseMessaging.framework",
"${BUILT_PRODUCTS_DIR}/FirebaseSessions/FirebaseSessions.framework",
"${BUILT_PRODUCTS_DIR}/GoogleDataTransport/GoogleDataTransport.framework",
"${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework",
"${BUILT_PRODUCTS_DIR}/PromisesObjC/FBLPromises.framework",
"${BUILT_PRODUCTS_DIR}/PromisesSwift/Promises.framework",
"${BUILT_PRODUCTS_DIR}/Solar/Solar.framework",
"${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/FBAEMKit/FBAEMKit.framework/FBAEMKit",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/FBSDKCoreKit/FBSDKCoreKit.framework/FBSDKCoreKit",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/FBSDKCoreKit_Basics/FBSDKCoreKit_Basics.framework/FBSDKCoreKit_Basics",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DZNEmptyDataSet.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCore.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCoreExtension.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCoreInternal.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCrashlytics.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseInstallations.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseMessaging.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseSessions.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleDataTransport.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBLPromises.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Promises.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Solar.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBAEMKit.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBSDKCoreKit.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBSDKCoreKit_Basics.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Earthquake Network/Pods-Earthquake Network-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
566F2B7433267D7429970520 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Earthquake Network-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
DCF07F9824D40DB600DCCA63 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -1600,7 +1520,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "# Type a script or drag a script file from your workspace to insert its path.\n\"${PODS_ROOT}/FirebaseCrashlytics/run\"\n";
shellScript = "${BUILD_DIR%Build/*}/SourcePackages/checkouts/firebase-ios-sdk/Crashlytics/run\n\n";
};
/* End PBXShellScriptBuildPhase section */
@@ -1624,13 +1544,8 @@
DC3B5429257FCBCA00C0B6A5 /* EQNReteSmartphone.swift in Sources */,
DCC23DEF24D28F58003A2404 /* EQNEdgeInsetLabel.swift in Sources */,
DCF0188F252F09C500C783F0 /* EQNUtility+Extensions.swift in Sources */,
8CCE165121E7BAEC00173CD9 /* EQNNotificheReteSismiche.m in Sources */,
DC52B8A224FC145500ABEBA6 /* SettingsBaseViewController.m in Sources */,
DC2814382519C56100C1AFF7 /* SeismicNetworksViewController.swift in Sources */,
8CF4F4DB216D44930057110B /* EQNPastquakes.m in Sources */,
8CCE165821EB1E0000173CD9 /* SettingsSeismicNetworkAlertsViewController.m in Sources */,
8CCE165521EA378800173CD9 /* SettingsUserReportAlertsViewController.m in Sources */,
DCBB268024D1ECE200F04559 /* SubscriptionDetailViewController.swift in Sources */,
DCC76BE4251F69FB0005C4DC /* EQNUserDefaultsCommand.swift in Sources */,
DC4B67612517833F00634277 /* EQNSeismic.swift in Sources */,
6544416B25E9599000C41714 /* EQNDebugViewController.swift in Sources */,
@@ -1643,30 +1558,26 @@
65DBFB7525E2BBF20041CBA6 /* GADTMediumTemplateView.m in Sources */,
DC03BEAB250BC0A60084769B /* EQNRoundedButton.swift in Sources */,
65AD23CE261B03D400E3B57C /* SubscriptionsDescriptionTableViewCell.swift in Sources */,
8CCE164E21E7BACE00173CD9 /* EQNNotificheSegnalazioniUtente.m in Sources */,
DCD4571C24F6CF0D00B58304 /* EQNGenericValue.swift in Sources */,
8C4E343F215012FA008B0D2A /* EQNManager.m in Sources */,
DCAA913F24F68A1D00145A3D /* SettingMultivaluesTableViewCell.swift in Sources */,
652C37BD26092B3C0068EC3B /* FiltersViewModel.swift in Sources */,
DCF9E14F24F6EA07002B6B1D /* EQNSeismicNetwork.swift in Sources */,
DC2814302519C24400C1AFF7 /* SeismicNetworkTableViewCell.swift in Sources */,
650247122A61832F001AC512 /* EQNBackgroundPositionDebugHelper.swift in Sources */,
65CB83432915720400EE1E35 /* EQNUserData.swift in Sources */,
654D18C425F93C0600BB6DB0 /* PasquakesMapViewController.swift in Sources */,
8C14113721EE502800A59729 /* EQNAllertaSismica.m in Sources */,
8C483CBC21FDACE500259FD2 /* VersioneProProducts.swift in Sources */,
8C483CBC21FDACE500259FD2 /* EQNInAppProducts.swift in Sources */,
8C483CB821FDACD300259FD2 /* IAPHelper.swift in Sources */,
6562C80725FFA6B100C85273 /* SeismicNetworkViewModel.swift in Sources */,
DCDE0BD924E58CCE00209778 /* EQNMainTabBarController.m in Sources */,
8C4E344B2152EE5B008B0D2A /* EQNGeneratoreURLServer.m in Sources */,
6590EFF82C3004EA00F41420 /* EQNOfficialPushNotification.swift in Sources */,
DC99A50724E66E5F0071BC9F /* EQNStartupCommandsBuilder.swift in Sources */,
DCA5B6E7252E4BD8002AEC96 /* EQNBaseTableViewCell.swift in Sources */,
8CF66058214C566B009F4314 /* ServerRequest.m in Sources */,
DCF9E14D24F6D1AA002B6B1D /* EQNData.swift in Sources */,
DC52B8A524FCCD6900ABEBA6 /* AppTheme.swift in Sources */,
653C680425F3DF8A00FE52AC /* EQNBaseMapViewController.swift in Sources */,
658BC02B2859A4D3009EECAA /* RealtimeAlertView.swift in Sources */,
DC27EB2F24F6EBE000ACBFE0 /* SettingsSeismicNetworksViewController.swift in Sources */,
8CF66059214C566B009F4314 /* Reachability.m in Sources */,
DC886A5D24E92D5500F7A5D3 /* EQNBaseViewController.m in Sources */,
8C593E8A217BA2470008B260 /* EQNSegnalazione.m in Sources */,
@@ -1676,6 +1587,8 @@
6552C1462926DBA1008E723C /* AppPreferences.swift in Sources */,
DC08803F24F5A89000186D97 /* SettingEnableTableViewCell.swift in Sources */,
652A3C6B2A8A757800719796 /* EQNBackgrounPositionDebugViewController.swift in Sources */,
65DB60F02C16F5B100164366 /* SettingsBaseTableViewController.swift in Sources */,
65AB4A952C11BED400950DF7 /* SettingsSeismicNetworkNotificationsViewController.swift in Sources */,
6586971125F44C26009C0182 /* EQNBlurredCloseButton.swift in Sources */,
8CAFD7C521825E4A00F8BD29 /* EQNSisma.m in Sources */,
DCC23DEC24D281CE003A2404 /* SubscriptionsActiveTableViewCell.swift in Sources */,
@@ -1685,7 +1598,6 @@
DCBB84F0252CFC4600F12633 /* AlertsNoLocationTableViewCell.swift in Sources */,
651901B925F5358700CAFF20 /* EQNMapAnnotationSeismic.swift in Sources */,
DC99A50324E66E270071BC9F /* EQNCommandProtocol.swift in Sources */,
DCB45BC8250E86E100DB2D0C /* SeismicSettingsViewController.swift in Sources */,
DCF10DC624D2B8C7009F34C3 /* EQNPurchaseUtility.swift in Sources */,
DC0E551324F8063300D54270 /* SettingSegmentedTableViewCell.swift in Sources */,
650247152A618D7F001AC512 /* Foundation+Extensions.swift in Sources */,
@@ -1693,9 +1605,8 @@
DC47D1BC252A0C2B004119F6 /* AlertsPastEartquakesTableViewCell.swift in Sources */,
654D18C925F93CD700BB6DB0 /* EQNMapAnnotationPastquake.swift in Sources */,
DCEFF21724F58569009D3FE1 /* SettingSectionHeaderView.swift in Sources */,
8C14113121ED3E5B00A59729 /* SettingsRealTimeAlertsViewController.m in Sources */,
65DB60FD2C172C4A00164366 /* EQNSettingRealTimeAlert.swift in Sources */,
DCBB267A24D1E7F500F04559 /* SubscriptionsHeaderTableViewCell.swift in Sources */,
DC65B391250F243E00251693 /* SeismicSettingsNetworksViewController.swift in Sources */,
65DBFB4B25E29DD60041CBA6 /* SeismicNetworksMapDetailViewController.swift in Sources */,
DC08804124F5B41400186D97 /* SettingSliderTableViewCell.swift in Sources */,
658BC0292859A456009EECAA /* RealtimeAlertViewController.swift in Sources */,
@@ -1704,23 +1615,31 @@
65F9B4A02A8CC58200F13260 /* UpdateUserLocationTask.swift in Sources */,
8C4E34422152B5E8008B0D2A /* EQNRilevamento.m in Sources */,
8C7A3B66225A5EA40045B266 /* NSDictionary+EQNExtensions.m in Sources */,
65AB4A992C11DFC200950DF7 /* EQNSettingSeismicNetworkNotification.swift in Sources */,
8CF66053214C12DC009F4314 /* EQNMath.m in Sources */,
DCF4A54524F8DB8300B17326 /* SettingDateTableViewCell.swift in Sources */,
65DB60FA2C17158D00164366 /* SettingsUserReportNotificationsViewController.swift in Sources */,
DC7EEE4A252A11C9004B4A2A /* AlertsSmartphoneNetworkTableViewCell.swift in Sources */,
DC7EEE4F252A1634004B4A2A /* AlertsPriorityServiceTableViewCell.swift in Sources */,
653604E9262348FA00B2B651 /* EQNBaseMapFilter.swift in Sources */,
65583A05261B83BE00ECA9F9 /* UIKit+Extensions.swift in Sources */,
65DBFB7625E2BBF20041CBA6 /* GADTTemplateView.m in Sources */,
8C5EA23D2177B51C002DC156 /* SegnalazioniViewController.m in Sources */,
656E02162C1C4DF2008D0E92 /* EQNBaseContainerTableViewCell.swift in Sources */,
656D138F2C2225560094F597 /* SubscriptionDetailsViewController.swift in Sources */,
653C67E225F3CC2E00FE52AC /* EQNCustomAnnotationView.swift in Sources */,
8CF4F4D8216D3A110057110B /* EQNAreaCheck.m in Sources */,
8C4E34452152B707008B0D2A /* EQNAccelerometroManager.m in Sources */,
65D409942619BA34008CF356 /* SegnalazioniSendReportCell.swift in Sources */,
65F9B49C2A8CA22800F13260 /* BackgroundTaskManager.swift in Sources */,
656D13912C22371F0094F597 /* SubscriptionDetailsTableViewCell.swift in Sources */,
8CBD3DC72149B9AD0070C963 /* AppDelegate.m in Sources */,
65172F532C25C496006D2A5C /* EQNSeismicAnnotationView.swift in Sources */,
DC974AFF251748B300A139EC /* SeismicFiltersViewController.swift in Sources */,
65DB60F22C16FA3600164366 /* SettingsRealTimeAlertsViewController.swift in Sources */,
DCB28CEE24FB8400001F557E /* SettingsViewController.swift in Sources */,
DCB528212560161C005288E5 /* AlertSimulatorViewController.swift in Sources */,
65DB60F82C1714E100164366 /* EQNSettingUserReportNotification.swift in Sources */,
6502470D2A612E4A001AC512 /* Constants.swift in Sources */,
DCC76BD8251F56050005C4DC /* SeismicCardSettingsViewController.swift in Sources */,
650B23AB2632CCD3007AE752 /* UIView+EQNExtensions.swift in Sources */,
@@ -1854,7 +1773,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 121;
CURRENT_PROJECT_VERSION = 139;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = WJA4MR4CPC;
GCC_PREPROCESSOR_DEFINITIONS = (
@@ -1865,13 +1784,13 @@
INFOPLIST_FILE = EQNNotificationService/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = EQNNotificationService;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2022 Earthquake Network. All rights reserved.";
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 5.7;
MARKETING_VERSION = 5.8.2;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.finazzi.distquake.notificationservice;
@@ -1896,20 +1815,20 @@
CODE_SIGN_IDENTITY = "Apple Distribution";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 121;
CURRENT_PROJECT_VERSION = 139;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = WJA4MR4CPC;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = EQNNotificationService/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = EQNNotificationService;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2022 Earthquake Network. All rights reserved.";
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 5.7;
MARKETING_VERSION = 5.8.2;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.finazzi.distquake.notificationservice;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1978,7 +1897,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@@ -2034,7 +1953,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
@@ -2044,7 +1963,6 @@
};
8CBD3DDC2149B9AD0070C963 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = C4FB0D7EEA34F8222369E1BB /* Pods-Earthquake Network.debug.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
@@ -2052,17 +1970,18 @@
CODE_SIGN_ENTITLEMENTS = "Earthquake Network/Earthquake Network.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 121;
CURRENT_PROJECT_VERSION = 139;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = WJA4MR4CPC;
GCC_PREFIX_HEADER = "Earthquake Network/Earthquake Network-Prefix.pch";
INFOPLIST_FILE = "Earthquake Network/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 5.7;
MARKETING_VERSION = 5.8.2;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
@@ -2084,28 +2003,10 @@
"-framework",
"\"CoreVideo\"",
"-framework",
"\"DZNEmptyDataSet\"",
"-framework",
"\"FBLPromises\"",
"-framework",
"\"FirebaseCore\"",
"-framework",
"\"FirebaseCrashlytics\"",
"-framework",
"\"FirebaseInstallations\"",
"-framework",
"\"FirebaseMessaging\"",
"-framework",
"\"Foundation\"",
"-framework",
"\"GoogleAppMeasurement\"",
"-framework",
"\"GoogleDataTransport\"",
"-framework",
"\"GoogleMobileAds\"",
"-framework",
"\"GoogleUtilities\"",
"-framework",
"\"MediaPlayer\"",
"-framework",
"\"MessageUI\"",
@@ -2116,8 +2017,6 @@
"-framework",
"\"Security\"",
"-framework",
"\"Solar\"",
"-framework",
"\"StoreKit\"",
"-framework",
"\"SystemConfiguration\"",
@@ -2127,8 +2026,6 @@
"\"UserMessagingPlatform\"",
"-framework",
"\"WebKit\"",
"-framework",
"\"nanopb\"",
"-weak_framework",
"\"AdSupport\"",
"-weak_framework",
@@ -2153,7 +2050,6 @@
};
8CBD3DDD2149B9AD0070C963 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 40CD2E5581CF2FA3D52F392D /* Pods-Earthquake Network.release.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
@@ -2161,16 +2057,17 @@
CODE_SIGN_ENTITLEMENTS = "Earthquake Network/Earthquake Network.entitlements";
CODE_SIGN_IDENTITY = "Apple Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 121;
CURRENT_PROJECT_VERSION = 139;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = WJA4MR4CPC;
GCC_PREFIX_HEADER = "Earthquake Network/Earthquake Network-Prefix.pch";
INFOPLIST_FILE = "Earthquake Network/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 5.7;
MARKETING_VERSION = 5.8.2;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
@@ -2192,28 +2089,10 @@
"-framework",
"\"CoreVideo\"",
"-framework",
"\"DZNEmptyDataSet\"",
"-framework",
"\"FBLPromises\"",
"-framework",
"\"FirebaseCore\"",
"-framework",
"\"FirebaseCrashlytics\"",
"-framework",
"\"FirebaseInstallations\"",
"-framework",
"\"FirebaseMessaging\"",
"-framework",
"\"Foundation\"",
"-framework",
"\"GoogleAppMeasurement\"",
"-framework",
"\"GoogleDataTransport\"",
"-framework",
"\"GoogleMobileAds\"",
"-framework",
"\"GoogleUtilities\"",
"-framework",
"\"MediaPlayer\"",
"-framework",
"\"MessageUI\"",
@@ -2224,8 +2103,6 @@
"-framework",
"\"Security\"",
"-framework",
"\"Solar\"",
"-framework",
"\"StoreKit\"",
"-framework",
"\"SystemConfiguration\"",
@@ -2235,8 +2112,6 @@
"\"UserMessagingPlatform\"",
"-framework",
"\"WebKit\"",
"-framework",
"\"nanopb\"",
"-weak_framework",
"\"AdSupport\"",
"-weak_framework",
@@ -2265,7 +2140,7 @@
CODE_SIGN_ENTITLEMENTS = EQNNotificationContent/EQNNotificationContent.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 121;
CURRENT_PROJECT_VERSION = 139;
DEVELOPMENT_TEAM = WJA4MR4CPC;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
@@ -2274,13 +2149,13 @@
"NOTIFICATION_CONTENT=1",
);
INFOPLIST_FILE = EQNNotificationContent/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 5.7;
MARKETING_VERSION = 5.8.2;
PRODUCT_BUNDLE_IDENTIFIER = com.finazzi.distquake.notificationcontent;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "EQNetwork Extension Content - Development";
@@ -2299,20 +2174,20 @@
CODE_SIGN_ENTITLEMENTS = EQNNotificationContent/EQNNotificationContent.entitlements;
CODE_SIGN_IDENTITY = "Apple Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 121;
CURRENT_PROJECT_VERSION = 139;
DEVELOPMENT_TEAM = WJA4MR4CPC;
GCC_PREPROCESSOR_DEFINITIONS = (
"ADS_ENABLED=0",
"NOTIFICATION_CONTENT=1",
);
INFOPLIST_FILE = EQNNotificationContent/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 5.7;
MARKETING_VERSION = 5.8.2;
PRODUCT_BUNDLE_IDENTIFIER = com.finazzi.distquake.notificationcontent;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "EQNetwork Extension Content - AppStore";
@@ -2373,6 +2248,46 @@
minimumVersion = 1.0.0;
};
};
65B16E1C2BDFA88D0020527E /* XCRemoteSwiftPackageReference "Solar" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/ceeK/Solar.git";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 3.0.1;
};
};
65B16E202BDFA8ED0020527E /* XCRemoteSwiftPackageReference "DZNEmptyDataSet" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/dzenbot/DZNEmptyDataSet";
requirement = {
branch = master;
kind = branch;
};
};
65B16E242BDFB2A40020527E /* XCRemoteSwiftPackageReference "swift-package-manager-google-mobile-ads" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/googleads/swift-package-manager-google-mobile-ads.git";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 11.3.0;
};
};
65B16E282BDFB39B0020527E /* XCRemoteSwiftPackageReference "facebook-ios-sdk" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/facebook/facebook-ios-sdk";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 14.1.0;
};
};
65E6AC6C2C2DB3B60073F8FE /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/firebase/firebase-ios-sdk.git";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 10.28.1;
};
};
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
@@ -2386,6 +2301,36 @@
package = 6552C13629262119008E723C /* XCRemoteSwiftPackageReference "Shogun" */;
productName = Shogun;
};
65B16E1D2BDFA88D0020527E /* Solar */ = {
isa = XCSwiftPackageProductDependency;
package = 65B16E1C2BDFA88D0020527E /* XCRemoteSwiftPackageReference "Solar" */;
productName = Solar;
};
65B16E212BDFA8ED0020527E /* DZNEmptyDataSet */ = {
isa = XCSwiftPackageProductDependency;
package = 65B16E202BDFA8ED0020527E /* XCRemoteSwiftPackageReference "DZNEmptyDataSet" */;
productName = DZNEmptyDataSet;
};
65B16E252BDFB2A40020527E /* GoogleMobileAds */ = {
isa = XCSwiftPackageProductDependency;
package = 65B16E242BDFB2A40020527E /* XCRemoteSwiftPackageReference "swift-package-manager-google-mobile-ads" */;
productName = GoogleMobileAds;
};
65B16E292BDFB39B0020527E /* FacebookCore */ = {
isa = XCSwiftPackageProductDependency;
package = 65B16E282BDFB39B0020527E /* XCRemoteSwiftPackageReference "facebook-ios-sdk" */;
productName = FacebookCore;
};
65E6AC6D2C2DB3B60073F8FE /* FirebaseCrashlytics */ = {
isa = XCSwiftPackageProductDependency;
package = 65E6AC6C2C2DB3B60073F8FE /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */;
productName = FirebaseCrashlytics;
};
65E6AC6F2C2DB3B60073F8FE /* FirebaseMessaging */ = {
isa = XCSwiftPackageProductDependency;
package = 65E6AC6C2C2DB3B60073F8FE /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */;
productName = FirebaseMessaging;
};
65FFDC9D292F682600EA821B /* Shogun */ = {
isa = XCSwiftPackageProductDependency;
package = 6552C13629262119008E723C /* XCRemoteSwiftPackageReference "Shogun" */;
@@ -2,6 +2,6 @@
<Workspace
version = "1.0">
<FileRef
location = "self:Earthquake Network.xcodeproj">
location = "self:">
</FileRef>
</Workspace>
@@ -1,5 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict/>
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
@@ -0,0 +1,177 @@
{
"originHash" : "c29b9b16ee6b4d1a6fec2debc59749097860256c8cbb2addc2abc08d3adba59d",
"pins" : [
{
"identity" : "abseil-cpp-binary",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/abseil-cpp-binary.git",
"state" : {
"revision" : "748c7837511d0e6a507737353af268484e1745e2",
"version" : "1.2024011601.1"
}
},
{
"identity" : "app-check",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/app-check.git",
"state" : {
"revision" : "3b62f154d00019ae29a71e9738800bb6f18b236d",
"version" : "10.19.2"
}
},
{
"identity" : "dznemptydataset",
"kind" : "remoteSourceControl",
"location" : "https://github.com/dzenbot/DZNEmptyDataSet",
"state" : {
"branch" : "master",
"revision" : "9bffa69a83a9fa58a14b3cf43cb6dd8a63774179"
}
},
{
"identity" : "facebook-ios-sdk",
"kind" : "remoteSourceControl",
"location" : "https://github.com/facebook/facebook-ios-sdk",
"state" : {
"revision" : "c19607d535864533523d1f437c84035e5fb101cf",
"version" : "14.1.0"
}
},
{
"identity" : "firebase-ios-sdk",
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/firebase-ios-sdk.git",
"state" : {
"revision" : "eca84fd638116dd6adb633b5a3f31cc7befcbb7d",
"version" : "10.29.0"
}
},
{
"identity" : "googleappmeasurement",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleAppMeasurement.git",
"state" : {
"revision" : "fe727587518729046fc1465625b9afd80b5ab361",
"version" : "10.28.0"
}
},
{
"identity" : "googledatatransport",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleDataTransport.git",
"state" : {
"revision" : "a637d318ae7ae246b02d7305121275bc75ed5565",
"version" : "9.4.0"
}
},
{
"identity" : "googleutilities",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleUtilities.git",
"state" : {
"revision" : "57a1d307f42df690fdef2637f3e5b776da02aad6",
"version" : "7.13.3"
}
},
{
"identity" : "grpc-binary",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/grpc-binary.git",
"state" : {
"revision" : "e9fad491d0673bdda7063a0341fb6b47a30c5359",
"version" : "1.62.2"
}
},
{
"identity" : "gtm-session-fetcher",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/gtm-session-fetcher.git",
"state" : {
"revision" : "a2ab612cb980066ee56d90d60d8462992c07f24b",
"version" : "3.5.0"
}
},
{
"identity" : "interop-ios-for-google-sdks",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/interop-ios-for-google-sdks.git",
"state" : {
"revision" : "2d12673670417654f08f5f90fdd62926dc3a2648",
"version" : "100.0.0"
}
},
{
"identity" : "leveldb",
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/leveldb.git",
"state" : {
"revision" : "a0bc79961d7be727d258d33d5a6b2f1023270ba1",
"version" : "1.22.5"
}
},
{
"identity" : "nanopb",
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/nanopb.git",
"state" : {
"revision" : "b7e1104502eca3a213b46303391ca4d3bc8ddec1",
"version" : "2.30910.0"
}
},
{
"identity" : "promises",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/promises.git",
"state" : {
"revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac",
"version" : "2.4.0"
}
},
{
"identity" : "shogun",
"kind" : "remoteSourceControl",
"location" : "https://github.com/andreabusi-it/Shogun",
"state" : {
"revision" : "ad890190d6be90f7712c2e56a38ef0937d9f8c1a",
"version" : "1.8.0"
}
},
{
"identity" : "solar",
"kind" : "remoteSourceControl",
"location" : "https://github.com/ceeK/Solar.git",
"state" : {
"revision" : "c2b96f2d5fb7f835b91cefac5e83101f54643901",
"version" : "3.0.1"
}
},
{
"identity" : "swift-package-manager-google-mobile-ads",
"kind" : "remoteSourceControl",
"location" : "https://github.com/googleads/swift-package-manager-google-mobile-ads.git",
"state" : {
"revision" : "9ab66e38f5f0c2d02f2b024b1babd880130f19bf",
"version" : "11.3.0"
}
},
{
"identity" : "swift-package-manager-google-user-messaging-platform",
"kind" : "remoteSourceControl",
"location" : "https://github.com/googleads/swift-package-manager-google-user-messaging-platform.git",
"state" : {
"revision" : "9b68aa69fb508f0274853e226c734151a973c7b7",
"version" : "2.4.0"
}
},
{
"identity" : "swift-protobuf",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-protobuf.git",
"state" : {
"revision" : "9f0c76544701845ad98716f3f6a774a892152bcb",
"version" : "1.26.0"
}
}
],
"version" : 3
}
@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Earthquake Network.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>
@@ -1,14 +0,0 @@
{
"pins" : [
{
"identity" : "shogun",
"kind" : "remoteSourceControl",
"location" : "https://github.com/andreabusi-it/Shogun",
"state" : {
"revision" : "c164595fdd5d0771a6a24cbff85a7582f0f07311",
"version" : "1.3.0"
}
}
],
"version" : 2
}
+18 -20
View File
@@ -11,9 +11,6 @@
#import "EQNUser.h"
#import "EQNAccelerometroManager.h"
#import "EQNManager.h"
#import "EQNAllertaSismica.h"
#import "EQNNotificheSegnalazioniUtente.h"
#import "EQNNotificheReteSismiche.h"
#import "EQNMainTabBarController.h"
#import "NSDictionary+EQNExtensions.h"
@@ -152,7 +149,7 @@
[self handlePushNotificationWithNotificationContent:content];
// Change this to your preferred presentation option
completionHandler(UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionSound);
completionHandler(UNNotificationPresentationOptionList | UNNotificationPresentationOptionBanner | UNNotificationPresentationOptionSound);
}
// Handle notification messages after display notification is tapped by the user.
@@ -173,20 +170,22 @@
{
NSString *type = content.userInfo[@"type"];
// 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
};
EQNTabBarSection section = EQNTabBarSectionAllerte;
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
};
if ([type isEqualToString:@"eqn"]) {
[EQNRealtimePushNotification storeNotificationWithPayload:notification];
section = EQNTabBarSectionAllerte;
} else if([type isEqualToString:@"manual"]) {
section = EQNTabBarSectionSegnalazioni;
} else if([type isEqualToString:@"official"]) {
[EQNOfficialPushNotification storeNotificationWithPayload:notification];
section = EQNTabBarSectionRetiSismiche;
}
@@ -213,14 +212,14 @@
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;
[FBSDKSettings.sharedSettings setAdvertiserTrackingEnabled:YES];
} else {
FBSDKSettings.sharedSettings.isAdvertiserTrackingEnabled = NO;
[FBSDKSettings.sharedSettings setAdvertiserTrackingEnabled:NO];
}
}];
});
} else {
FBSDKSettings.sharedSettings.isAdvertiserTrackingEnabled = YES;
[FBSDKSettings.sharedSettings setAdvertiserTrackingEnabled:YES];
}
}
@@ -233,8 +232,7 @@
- (void)configureFacebookSDKWithApplication:(UIApplication *)application andOptions:(NSDictionary *)launchOptions
{
[FBSDKApplicationDelegate.sharedInstance application:application didFinishLaunchingWithOptions:launchOptions];
[FBSDKSettings.sharedSettings setIsAdvertiserIDCollectionEnabled:YES];
[FBSDKSettings.sharedSettings setIsAdvertiserIDCollectionEnabled:YES];
[FBSDKSettings.sharedSettings setAdvertiserIDCollectionEnabled:YES];
}
#pragma mark - FIRMessagingDelegate
@@ -244,9 +242,9 @@
NSLog(@"[Firebase] fcmToken %@", fcmToken);
if (EQNUserData.sharedData.isFirstStart) {
// save default values for notification settings
[EQNAllertaSismica saveDefaultValues];
[EQNNotificheSegnalazioniUtente saveDefaultValues];
[EQNNotificheReteSismiche saveDefaultValues];
[EQNSettingRealTimeAlert saveDefaultValues];
[EQNSettingUserReportNotification saveDefaultValues];
[EQNSettingSeismicNetworkNotification saveDefaultValues];
}
[EQNUser.defaultUser registerUserIfNeededWithFirebaseToken:fcmToken];
+24 -16
View File
@@ -21,20 +21,12 @@ extension UserDefaults {
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"
static let NotificheRetiSismicheMagnitudoMinima = "NOTIFICHE_ATTIVA_RETI_ENERGIA_SISMI"
static let NotificheRetiSismicheDistanzaMassima = "NOTIFICHE_DISTANZA_POSIZIONE_RETI_SISMICHE"
static let NotificheRetiSismicheFiltroNotifiche = "NOTIFICHE_FILTRO_NOTIFICHE_RETI_SISMICHE"
// Impostazioni della sezione `Notifiche segnalazioni utente`
static let NotificheSegnalazioniUtenteAbilitato = "NOTIFICHE_SU_ATTIVA_SEGNALAZIONE_UTENTE"
@@ -45,6 +37,7 @@ extension UserDefaults {
static let UserReportCodeStatus = "CODE_MESSAGE_EQN"
// Proprietà e preferenze dell'utente
static let FirstAppStartExecuted = "EQNUserDefaultFirstAppStartExecuted"
/// Ultima posizione conosciuta dell'utente
static let UserDataLastLocation = "EQNLast_Location"
/// Token Firebase dell'utente corrente
@@ -67,19 +60,25 @@ extension UserDefaults {
// Migrazioni
static let AppMigrationV5_3 = "EQNUserDefaultMigrationV5_3"
static let AppMigrationV5_4 = "EQNUserDefaultMigrationV5_4"
static let AppMigrationV5_8 = "EQNUserDefaultMigrationV5_8"
static let AppMigrationV5_8_2 = "EQNUserDefaultMigrationV5_8_2"
static let SettingsSeismicNetworkNotificationMigrationV5_8 = "EQNUserDefaultSettingsSeismicNetworkNotificationMigrationV5_8"
static let SettingsUserReportNotificationMigrationV5_8 = "EQNUserDefaultSettingsUserReportNotificationMigrationV5_8"
static let SismicFiltersMigrationV5_8 = "EQNUserDefaultSismicFiltersMigrationV5_8"
static let SaveSettingsNotificationMigrationV5_8 = "EQNUserDefaultSaveSettingsNotificationMigrationV5_8"
// Notifica allerta salvata
static let RealTimeAlertPayload = "EQNData.RealtimePushNotificationPayload"
static let RealTimeAlertDate = "EQNData.RealtimeAlertDate"
// Notifica rete sismica aperta
static let OfficialAlertPayload = "EQNData.OfficialPushNotificationPayload"
// Filtri sezioni reti sismiche
static let SeismicFilterOption = "EQN_SISMI_TIPOLOGIA_FILTRO"
static let SeismicSort = "EQN_SISMI_TIPOLOGIA_ORDINAMENTO"
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 {
@@ -95,4 +94,13 @@ extension UserDefaults {
}
return defaultValue
}
func enumObject<T: RawRepresentable>(forKey key: String, or defaultValue: T) -> T {
if let rawValue = UserDefaults.standard.object(forKey: key) as? T.RawValue,
let value = T.init(rawValue: rawValue) {
return value
}
return defaultValue
}
}
@@ -26,8 +26,6 @@
@implementation AllerteViewController
static NSString * const SegueIdentifierPrioritySubscriptions = @"ShowPrioritySubscriptions";
/// Sections inside the app
typedef NS_ENUM(NSInteger, AllerteTableRow) {
AllerteTableRowLocationPermission = 0,
@@ -104,7 +102,14 @@ typedef NS_ENUM(NSInteger, AllerteTableRow) {
self.title = [NSLocalizedString(@"tab_network", nil) capitalizedString];
self.tableView.estimatedRowHeight = 200.0;
self.tableView.rowHeight = UITableViewAutomaticDimension;
[self.tableView registerClass:[AlertsSmartphoneNetworkTableViewCell class] forCellReuseIdentifier:@"SmartphoneNetworkCell"];
[self.tableView registerClass:[AlertsPriorityServiceTableViewCell class] forCellReuseIdentifier:@"PriorityCell"];
[self.tableView registerClass:[AlertsNoLocationTableViewCell class] forCellReuseIdentifier:@"NoLocationCell"];
[self.tableView registerClass:[AlertsPastEartquakesTableViewCell class] forCellReuseIdentifier:@"PastEarthquakesCell"];
[self.tableView registerClass:[AlertsSeismicNotificationCompactTableViewCell class] forCellReuseIdentifier:@"SeismicNotificationCompactCell"];
[self.tableView registerClass:[AlertsSeismicNotificationExpandedTableViewCell class] forCellReuseIdentifier:@"SeismicNotificationExpandedCell"];
[self.tableView registerClass:[AlertsPositionDataTableViewCell class] forCellReuseIdentifier:@"PositionDataCell"];
if (EQNBackgroundPositionDebugHelper.shared.isEnabled) {
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemBookmarks target:self action:@selector(backgroundPositionDebugTapped:)];
}
@@ -265,14 +270,16 @@ typedef NS_ENUM(NSInteger, AllerteTableRow) {
if (tableRow == AllerteTableRowLocationPermission) {
AlertsNoLocationTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"NoLocationCell" forIndexPath:indexPath];
cell.status = CLLocationManager.authorizationStatus;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
[cell updateWith:CLLocationManager.authorizationStatus];
return cell;
} else if (tableRow == AllerteTableRowSismiRilevati) {
if (self.isNotificaAttiva) {
AlertsSeismicNotificationExpandedTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"SeismicNotificationExpandedCell" forIndexPath:indexPath];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
EQNRealtimePushNotification *notification = [EQNRealtimePushNotification storedNotification];
cell.notification = notification;
[cell updateWith:notification];
__weak AllerteViewController *weakSelf = self;
cell.onTapClose = ^{
@@ -291,7 +298,8 @@ typedef NS_ENUM(NSInteger, AllerteTableRow) {
return cell;
}
AlertsSeismicNotificationCompactTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"SeismicNotificationCell" forIndexPath:indexPath];
AlertsSeismicNotificationCompactTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"SeismicNotificationCompactCell" forIndexPath:indexPath];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
__weak AllerteViewController *weakSelf = self;
cell.onTapAlertTest = ^{
@@ -311,8 +319,9 @@ typedef NS_ENUM(NSInteger, AllerteTableRow) {
} else if (tableRow == AllerteTableRowAllertePassate) {
AlertsPastEartquakesTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"PastEarthquakesCell" forIndexPath:indexPath];
cell.smartphoneNetwork = [EQNManager defaultManager].rete_smartphone;
cell.onTapMapButton = ^{
cell.selectionStyle = UITableViewCellSelectionStyleNone;
[cell updateWith:[EQNManager defaultManager].rete_smartphone];
cell.onTapMap = ^{
PasquakesMapViewController *controller = [[PasquakesMapViewController alloc] init];
[self presentViewController:controller animated:YES completion:nil];
};
@@ -320,7 +329,8 @@ typedef NS_ENUM(NSInteger, AllerteTableRow) {
} else if (tableRow == AllerteTableRowReteSmartphone) {
AlertsSmartphoneNetworkTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"SmartphoneNetworkCell" forIndexPath:indexPath];
cell.smartphoneNetwork = [EQNManager defaultManager].rete_smartphone;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
[cell updateWith:[EQNManager defaultManager].rete_smartphone];
cell.onTapButton = ^{
[self visualizzaCopertura];
};
@@ -328,12 +338,13 @@ typedef NS_ENUM(NSInteger, AllerteTableRow) {
} else if (tableRow == AllerteTableRowServizioPriorita) {
AlertsPriorityServiceTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"PriorityCell" forIndexPath:indexPath];
cell.smartphoneNetwork = [EQNManager defaultManager].rete_smartphone;
[cell updateWith:[EQNManager defaultManager].rete_smartphone];
return cell;
} else if (tableRow == AllerteTableRowDatiPosizione) {
AlertsPositionDataTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"PositionDataCell" forIndexPath:indexPath];
cell.position = [EQNUser defaultUser].lastPosition;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
[cell updateWith:[EQNUser defaultUser].lastPosition];
return cell;
}
@@ -346,9 +357,10 @@ typedef NS_ENUM(NSInteger, AllerteTableRow) {
AllerteTableRow tableRow = [self.tableItems[indexPath.row] integerValue];
switch (tableRow) {
case AllerteTableRowServizioPriorita:
[self performSegueWithIdentifier:SegueIdentifierPrioritySubscriptions sender:nil];
break;
case AllerteTableRowServizioPriorita: {
SubscriptionsViewController *controller = [[SubscriptionsViewController alloc] init];
[self.navigationController pushViewController:controller animated:YES];
}; break;
default:
break;
}
@@ -9,41 +9,70 @@
import UIKit
import CoreLocation
class AlertsNoLocationTableViewCell: EQNBaseTableViewCell {
@objc var status: CLAuthorizationStatus = .notDetermined {
didSet {
updateUI()
}
@objc
class AlertsNoLocationTableViewCell: EQNBaseContainerTableViewCell {
override var isHeaderVisible: Bool { false }
// MARK: - UI
private lazy var messageLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.textColor = AppTheme.Colors.red
label.font = .preferredFont(forTextStyle: .body)
return label
}()
private lazy var actionButton: UIButton = {
let button = EQNRoundedButton.make(title: NSLocalizedString("permission_location_no_background_solve", comment: ""), target: self, action: #selector(solveTapped(_:)))
return button
}()
// MARK: - Internal
override func setupUI() {
super.setupUI()
containerView.addSubview(messageLabel)
containerView.addSubview(actionButton)
messageLabel.topAnchor.constraint(equalTo: topView.bottomAnchor, constant: .cardVerticalSpacing).isActive = true
messageLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: .cardPadding).isActive = true
messageLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: .cardPadding.negative).isActive = true
actionButton.heightAnchor.constraint(greaterThanOrEqualToConstant: 40.0).isActive = true
actionButton.topAnchor.constraint(equalTo: messageLabel.bottomAnchor, constant: .cardPadding).isActive = true
actionButton.leadingAnchor.constraint(equalTo: messageLabel.leadingAnchor).isActive = true
actionButton.trailingAnchor.constraint(equalTo: messageLabel.trailingAnchor).isActive = true
actionButton.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: .cardPadding.negative).isActive = true
}
@IBOutlet private weak var messageLabel: UILabel!
@IBOutlet private weak var actionButton: UIButton!
override func updateUI() {
super.updateUI()
actionButton.backgroundColor = AppTheme.Colors.lightGray
}
// MARK: - Private
// MARK: - Public
private func updateUI() {
var message = ""
switch status {
case .authorizedAlways:
message = ""
case .authorizedWhenInUse:
message = NSLocalizedString("permission_location_no_background", comment: "")
default:
message = NSLocalizedString("permission_location_no", comment: "")
@objc
func update(with status: CLAuthorizationStatus) {
messageLabel.text = switch status {
case .authorizedAlways: ""
case .authorizedWhenInUse: NSLocalizedString("permission_location_no_background", comment: "")
default: NSLocalizedString("permission_location_no", comment: "")
}
messageLabel.text = message
actionButton.setLocalizedTitle(key: "permission_location_no_background_solve")
}
// MARK: - Actions
@IBAction private func solveTapped(_ sender: UIButton) {
@objc private func solveTapped(_ sender: UIButton) {
guard let settingsUrl = URL(string: UIApplication.openSettingsURLString) else {
return
}
UIApplication.shared.open(settingsUrl, options: [:], completionHandler: nil)
}
}
@@ -8,50 +8,87 @@
import UIKit
class AlertsPastEartquakesTableViewCell: EQNBaseTableViewCell {
@objc var smartphoneNetwork: EQNReteSmartphone? {
didSet {
updateUI()
}
}
@objc
class AlertsPastEartquakesTableViewCell: EQNBaseContainerTableViewCell {
@objc var onTapMapButton: (() -> Void)?
@objc var onTapMap: (() -> Void)?
override var headerText: String { NSLocalizedString("main_past_quakes", comment: "") }
// MARK: - UI
private lazy var last24hLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.textColor = AppTheme.shared.cardTextColor
label.font = .preferredFont(forTextStyle: .title3)
label.textAlignment = .center
return label
}()
private lazy var from2013Label: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.textColor = AppTheme.shared.cardTextColor
label.font = .preferredFont(forTextStyle: .title3)
label.textAlignment = .center
return label
}()
private lazy var mapButton: UIButton = {
let button = EQNRoundedButton.make(target: self, action: #selector(mapTapped(_:)))
return button
}()
// MARK: - Internal
@IBOutlet private weak var headerLabel: UILabel!
@IBOutlet private weak var last24hLabel: UILabel!
@IBOutlet private weak var from2013Label: UILabel!
@IBOutlet private weak var mapButton: UIButton!
// MARK: - View Lifecycle
override func awakeFromNib() {
super.awakeFromNib()
override func setupUI() {
super.setupUI()
localizeUI()
containerView.addSubview(last24hLabel)
containerView.addSubview(from2013Label)
containerView.addSubview(mapButton)
last24hLabel.topAnchor.constraint(equalTo: topView.bottomAnchor, constant: .cardVerticalSpacing).isActive = true
last24hLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: .cardPadding).isActive = true
last24hLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: .cardPadding.negative).isActive = true
from2013Label.topAnchor.constraint(equalTo: last24hLabel.bottomAnchor, constant: .cardVerticalSpacing).isActive = true
from2013Label.leadingAnchor.constraint(equalTo: last24hLabel.leadingAnchor).isActive = true
from2013Label.trailingAnchor.constraint(equalTo: last24hLabel.trailingAnchor).isActive = true
mapButton.heightAnchor.constraint(greaterThanOrEqualToConstant: 40.0).isActive = true
mapButton.topAnchor.constraint(equalTo: from2013Label.bottomAnchor, constant: .cardVerticalSpacing).isActive = true
mapButton.leadingAnchor.constraint(equalTo: from2013Label.leadingAnchor).isActive = true
mapButton.trailingAnchor.constraint(equalTo: from2013Label.trailingAnchor).isActive = true
mapButton.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: .cardPadding.negative).isActive = true
}
// MARK: - Private
private func localizeUI() {
headerLabel.text = NSLocalizedString("main_past_quakes", comment: "")
override func updateUI() {
super.updateUI()
last24hLabel.text = NSLocalizedString("main_recent_quakes_initial", comment: "")
from2013Label.text = NSLocalizedString("main_total_quakes_initial", comment: "")
mapButton.setLocalizedTitle(key: "official_button_map", uppercased: true, emoji: "🗺")
}
private func updateUI() {
guard let smartphoneNetwork = smartphoneNetwork else { return }
// MARK: - Public
@objc
func update(with smartphoneNetwork: EQNReteSmartphone?) {
guard let smartphoneNetwork else { return }
last24hLabel.text = String(format: NSLocalizedString("main_recent_quakes", comment: ""), smartphoneNetwork.counterLastDayAlerts)
from2013Label.text = String(format: NSLocalizedString("main_total_quakes", comment: ""), smartphoneNetwork.counterTotalAlerts)
}
// MARK: - Actions
@IBAction func mapTapped(_ sender: UIButton) {
onTapMapButton?()
@objc private func mapTapped(_ sender: UIButton) {
onTapMap?()
}
}
@@ -9,20 +9,11 @@
import UIKit
import Solar
class AlertsPositionDataTableViewCell: EQNBaseTableViewCell {
@objc var position: CLLocation? {
didSet {
updateUI()
}
}
@objc
class AlertsPositionDataTableViewCell: EQNBaseContainerTableViewCell {
// MARK: - Internal
override var headerText: String { NSLocalizedString("weather_location", comment: "") }
@IBOutlet private weak var headerLabel: UILabel!
@IBOutlet private weak var positionLabel: UILabel!
@IBOutlet private weak var sunriseTimeLabel: UILabel!
@IBOutlet private weak var sunsetTimeLabel: UILabel!
private lazy var dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .none
@@ -30,26 +21,117 @@ class AlertsPositionDataTableViewCell: EQNBaseTableViewCell {
return formatter
}()
// MARK: - Private
// MARK: - UI
private func updateUI() {
headerLabel.text = NSLocalizedString("weather_location", comment: "")
private lazy var positionImage: UIImageView = {
let imageView = UIImageView(image: .init(named: "world_old"))
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFit
imageView.heightAnchor.constraint(equalToConstant: 30.0).isActive = true
imageView.widthAnchor.constraint(equalTo: imageView.heightAnchor).isActive = true
return imageView
}()
private lazy var positionLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.textColor = AppTheme.shared.cardTextColor
label.font = .preferredFont(forTextStyle: .body)
return label
}()
private lazy var sunriseImage: UIImageView = {
let imageView = UIImageView(image: .init(named: "sunrise"))
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFit
imageView.heightAnchor.constraint(equalToConstant: 30.0).isActive = true
imageView.widthAnchor.constraint(equalTo: imageView.heightAnchor).isActive = true
return imageView
}()
private lazy var sunriseTimeLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.textColor = AppTheme.shared.cardTextColor
label.font = .preferredFont(forTextStyle: .body)
return label
}()
private lazy var sunsetImage: UIImageView = {
let imageView = UIImageView(image: .init(named: "sunset"))
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFit
imageView.heightAnchor.constraint(equalToConstant: 30.0).isActive = true
imageView.widthAnchor.constraint(equalTo: imageView.heightAnchor).isActive = true
return imageView
}()
private lazy var sunsetTimeLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.textColor = AppTheme.shared.cardTextColor
label.font = .preferredFont(forTextStyle: .body)
return label
}()
// MARK: - Internal
override func setupUI() {
super.setupUI()
containerView.addSubview(positionImage)
containerView.addSubview(positionLabel)
containerView.addSubview(sunriseImage)
containerView.addSubview(sunriseTimeLabel)
containerView.addSubview(sunsetImage)
containerView.addSubview(sunsetTimeLabel)
positionImage.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: .cardPadding).isActive = true
positionImage.centerYAnchor.constraint(equalTo: positionLabel.centerYAnchor).isActive = true
positionImage.trailingAnchor.constraint(equalTo: positionLabel.leadingAnchor, constant: .cardPadding.negative).isActive = true
positionLabel.topAnchor.constraint(equalTo: topView.bottomAnchor, constant: .cardVerticalSpacing).isActive = true
positionLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: .cardPadding.negative).isActive = true
sunriseImage.leadingAnchor.constraint(equalTo: positionImage.leadingAnchor).isActive = true
sunriseImage.centerYAnchor.constraint(equalTo: sunriseTimeLabel.centerYAnchor).isActive = true
sunriseImage.trailingAnchor.constraint(equalTo: sunriseTimeLabel.leadingAnchor, constant: .cardPadding.negative).isActive = true
sunriseTimeLabel.topAnchor.constraint(equalTo: positionLabel.bottomAnchor, constant: .cardVerticalSpacing.x2).isActive = true
sunriseTimeLabel.trailingAnchor.constraint(equalTo: positionLabel.trailingAnchor).isActive = true
sunsetImage.leadingAnchor.constraint(equalTo: sunriseImage.leadingAnchor).isActive = true
sunsetImage.centerYAnchor.constraint(equalTo: sunsetTimeLabel.centerYAnchor).isActive = true
sunsetImage.trailingAnchor.constraint(equalTo: sunsetTimeLabel.leadingAnchor, constant: .cardPadding.negative).isActive = true
sunsetTimeLabel.topAnchor.constraint(equalTo: sunriseTimeLabel.bottomAnchor, constant: .cardVerticalSpacing.x2).isActive = true
sunsetTimeLabel.trailingAnchor.constraint(equalTo: sunriseTimeLabel.trailingAnchor).isActive = true
sunsetTimeLabel.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: .cardPadding.negative).isActive = true
}
override func updateUI() {
super.updateUI()
positionLabel.text = "n.d."
sunriseTimeLabel.text = "n.d."
sunsetTimeLabel.text = "n.d."
guard let position = position else { return }
}
// MARK: - Public
@objc
func update(with position: CLLocation?) {
guard let position else { return }
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)"
}
if let sunset = solar.sunset {
sunsetTimeLabel.text = dateFormatter.string(from: sunset) + " \(timeZone)"
}
guard let solar = Solar(coordinate: position.coordinate) else { return }
let timeZone = TimeZone.current.localizedName(for: .generic, locale: .current) ?? TimeZone.current.identifier
if let sunrise = solar.sunrise {
sunriseTimeLabel.text = dateFormatter.string(from: sunrise) + " \(timeZone)"
}
if let sunset = solar.sunset {
sunsetTimeLabel.text = dateFormatter.string(from: sunset) + " \(timeZone)"
}
}
}
@@ -8,37 +8,62 @@
import UIKit
class AlertsPriorityServiceTableViewCell: EQNBaseTableViewCell {
@objc var smartphoneNetwork: EQNReteSmartphone? {
didSet {
updateUI()
}
}
@objc
class AlertsPriorityServiceTableViewCell: EQNBaseContainerTableViewCell {
override var headerText: String { NSLocalizedString("inapp_list", comment: "") }
override var isRightArrowVisbile: Bool { true }
// MARK: - UI
private lazy var descriptionLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.textColor = AppTheme.Colors.darkGray
label.font = .preferredFont(forTextStyle: .body)
return label
}()
private lazy var lastSubscriptionLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.textColor = AppTheme.Colors.pureRed
label.font = .preferredFont(forTextStyle: .body)
return label
}()
// MARK: - Internal
@IBOutlet private weak var headerLabel: UILabel!
@IBOutlet private weak var descriptionLabel: UILabel!
@IBOutlet private weak var lastSubscriptionLabel: UILabel!
// MARK: - View Lifecycle
override func awakeFromNib() {
super.awakeFromNib()
override func setupUI() {
super.setupUI()
localizeUI()
containerView.addSubview(descriptionLabel)
containerView.addSubview(lastSubscriptionLabel)
descriptionLabel.topAnchor.constraint(equalTo: topView.bottomAnchor, constant: .cardVerticalSpacing).isActive = true
descriptionLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: .cardPadding).isActive = true
descriptionLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: .cardPadding.negative).isActive = true
lastSubscriptionLabel.topAnchor.constraint(equalTo: descriptionLabel.bottomAnchor, constant: .cardVerticalSpacing/2.0).isActive = true
lastSubscriptionLabel.leadingAnchor.constraint(equalTo: descriptionLabel.leadingAnchor).isActive = true
lastSubscriptionLabel.trailingAnchor.constraint(equalTo: descriptionLabel.trailingAnchor).isActive = true
lastSubscriptionLabel.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: .cardPadding.negative).isActive = true
}
// MARK: - Private
private func localizeUI() {
headerLabel.text = NSLocalizedString("inapp_list", comment: "")
override func updateUI() {
super.updateUI()
backgroundColor = AppTheme.Colors.cardBackgroundOrange
descriptionLabel.text = NSLocalizedString("inapp_adv", comment: "")
}
private func updateUI() {
guard let smartphoneNetwork = smartphoneNetwork else { return }
// MARK: - Public
@objc
func update(with smartphoneNetwork: EQNReteSmartphone?) {
guard let smartphoneNetwork else { return }
lastSubscriptionLabel.text = subscriptionText(for: smartphoneNetwork.lastSubscriptionDiff)
}
@@ -9,54 +9,112 @@
import UIKit
class AlertsSeismicNotificationCompactTableViewCell: EQNBaseTableViewCell {
@objc
class AlertsSeismicNotificationCompactTableViewCell: EQNBaseContainerTableViewCell {
typealias DefaultCompletion = () -> Void
@objc var onTapAlertTest: DefaultCompletion?
@objc var onTapSimulator: DefaultCompletion?
@objc var onTapHowItWorks: DefaultCompletion?
@objc var onTapShareApp: DefaultCompletion?
override var isHeaderVisible: Bool { false }
// MARK: - UI
private lazy var descriptionLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = AppTheme.Colors.green
label.font = .preferredFont(forTextStyle: .title3)
label.textAlignment = .center
label.numberOfLines = 0
return label
}()
@IBOutlet private weak var descriptionLabel: UILabel!
@IBOutlet private weak var testAlertButton: UIButton!
@IBOutlet private weak var simulatorAlertButton: UIButton!
@IBOutlet private weak var howItWorksAlertButton: UIButton!
@IBOutlet private weak var shareAppButton: UIButton!
private lazy var testAlertButton: UIButton = {
let button = EQNRoundedButton.make(target: self, action: #selector(testAlertTapped(_:)))
return button
}()
// MARK: - View Lifecycle
private lazy var simulatorAlertButton: UIButton = {
let button = EQNRoundedButton.make(target: self, action: #selector(simulatorTapped(_:)))
return button
}()
override func awakeFromNib() {
super.awakeFromNib()
private lazy var howItWorksAlertButton: UIButton = {
let button = EQNRoundedButton.make(target: self, action: #selector(howItWorksTapped(_:)))
return button
}()
private lazy var shareAppButton: UIButton = {
let button = EQNRoundedButton.make(target: self, action: #selector(shareAppTapped(_:)))
return button
}()
// MARK: - Internal
override func setupUI() {
super.setupUI()
localizeUI()
containerView.addSubview(descriptionLabel)
containerView.addSubview(testAlertButton)
containerView.addSubview(simulatorAlertButton)
containerView.addSubview(howItWorksAlertButton)
containerView.addSubview(shareAppButton)
descriptionLabel.topAnchor.constraint(equalTo: topView.bottomAnchor, constant: .cardPadding).isActive = true
descriptionLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: .cardPadding).isActive = true
descriptionLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: .cardPadding.negative).isActive = true
testAlertButton.heightAnchor.constraint(greaterThanOrEqualToConstant: 40.0).isActive = true
simulatorAlertButton.heightAnchor.constraint(equalTo: testAlertButton.heightAnchor).isActive = true
howItWorksAlertButton.heightAnchor.constraint(equalTo: testAlertButton.heightAnchor).isActive = true
shareAppButton.heightAnchor.constraint(equalTo: testAlertButton.heightAnchor).isActive = true
testAlertButton.topAnchor.constraint(equalTo: descriptionLabel.bottomAnchor, constant: .cardVerticalSpacing).isActive = true
testAlertButton.leadingAnchor.constraint(equalTo: descriptionLabel.leadingAnchor).isActive = true
testAlertButton.trailingAnchor.constraint(equalTo: simulatorAlertButton.leadingAnchor, constant: .cardPadding.negative).isActive = true
simulatorAlertButton.trailingAnchor.constraint(equalTo: descriptionLabel.trailingAnchor).isActive = true
simulatorAlertButton.centerYAnchor.constraint(equalTo: testAlertButton.centerYAnchor).isActive = true
simulatorAlertButton.widthAnchor.constraint(equalTo: testAlertButton.widthAnchor).isActive = true
howItWorksAlertButton.topAnchor.constraint(equalTo: testAlertButton.bottomAnchor, constant: .cardVerticalSpacing).isActive = true
howItWorksAlertButton.leadingAnchor.constraint(equalTo: testAlertButton.leadingAnchor).isActive = true
howItWorksAlertButton.trailingAnchor.constraint(equalTo: shareAppButton.leadingAnchor, constant: .cardPadding.negative).isActive = true
shareAppButton.trailingAnchor.constraint(equalTo: descriptionLabel.trailingAnchor).isActive = true
shareAppButton.centerYAnchor.constraint(equalTo: howItWorksAlertButton.centerYAnchor).isActive = true
shareAppButton.widthAnchor.constraint(equalTo: howItWorksAlertButton.widthAnchor).isActive = true
shareAppButton.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: .cardPadding.negative).isActive = true
}
// MARK: - Private
private func localizeUI() {
override func updateUI() {
super.updateUI()
backgroundColor = AppTheme.Colors.cardBackgroundGreen
descriptionLabel.text = NSLocalizedString("main_nodetection", comment: "")
testAlertButton.setLocalizedTitle(key: "main_alerttest", uppercased: true, emoji: "🚨")
simulatorAlertButton.setLocalizedTitle(key: "main_simulator", uppercased: true, emoji: "")
howItWorksAlertButton.setLocalizedTitle(key: "main_how_it_work", uppercased: true, emoji: "💡")
shareAppButton.setLocalizedTitle(key: "main_share_app", uppercased: true, emoji: "👥")
}
// MARK: - Actions
@IBAction private func testAlertTapped() {
@objc private func testAlertTapped(_ sender: UIButton) {
onTapAlertTest?()
}
@IBAction private func simulatorTapped() {
@objc private func simulatorTapped(_ sender: UIButton) {
onTapSimulator?()
}
@IBAction private func howItWorksTapped() {
@objc private func howItWorksTapped(_ sender: UIButton) {
onTapHowItWorks?()
}
@IBAction private func shareAppTapped() {
@objc private func shareAppTapped(_ sender: UIButton) {
onTapShareApp?()
}
}
@@ -10,78 +10,173 @@ import UIKit
import MapKit
import Shogun
class AlertsSeismicNotificationExpandedTableViewCell: EQNBaseTableViewCell, MKMapViewDelegate {
class AlertsSeismicNotificationExpandedTableViewCell: EQNBaseContainerTableViewCell, MKMapViewDelegate {
override var isHeaderVisible: Bool { false }
typealias DefaultCompletion = () -> Void
@objc var notification: EQNRealtimePushNotification? {
didSet {
updateUI()
}
}
@objc var onTapOpenTwitter: DefaultCompletion?
@objc var onTapRateApp: DefaultCompletion?
@objc var onTapClose: DefaultCompletion?
@objc var onTapShareApp: DefaultCompletion?
// MARK: - UI
private lazy var notificationTitleLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.textColor = AppTheme.shared.cardTextColor
label.font = .preferredFont(forTextStyle: .title1)
label.textAlignment = .center
return label
}()
private lazy var notificationIntensityLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.textColor = AppTheme.shared.cardTextColor
label.font = .preferredFont(forTextStyle: .title1)
label.textAlignment = .center
return label
}()
private lazy var notificationDescriptionLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.textColor = AppTheme.shared.cardTextColor
label.font = .preferredFont(forTextStyle: .body)
label.textAlignment = .center
return label
}()
private lazy var mapView: MKMapView = {
let mapView = MKMapView()
mapView.translatesAutoresizingMaskIntoConstraints = false
mapView.delegate = self
mapView.isScrollEnabled = false
mapView.isZoomEnabled = false
mapView.register(EQNCustomAnnotationView.self, forAnnotationViewWithReuseIdentifier: EQNCustomAnnotationView.SingleLineIdentifier)
return mapView
}()
private lazy var shareButton: UIButton = {
let button = EQNRoundedButton.make(target: self, action: #selector(shareAppTapped(_:)))
return button
}()
private lazy var rateAppButton: UIButton = {
let button = EQNRoundedButton.make(target: self, action: #selector(rateAppTapped(_:)))
return button
}()
private lazy var viewOnTwitterButton: UIButton = {
let button = EQNRoundedButton.make(target: self, action: #selector(viewInTwitterTapped(_:)))
return button
}()
private lazy var closeButton: UIButton = {
let button = EQNRoundedButton.make(target: self, action: #selector(closeTapped(_:)))
return button
}()
private lazy var descriptionLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.textColor = AppTheme.shared.cardTextColor
label.font = .preferredFont(forTextStyle: .body)
label.textAlignment = .center
return label
}()
// MARK: - Internal
@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 {
mapView.delegate = self
mapView.isScrollEnabled = false
mapView.isZoomEnabled = false
mapView.register(EQNCustomAnnotationView.self, forAnnotationViewWithReuseIdentifier: EQNCustomAnnotationView.SingleLineIdentifier)
}
}
@IBOutlet private weak var shareButton: UIButton!
@IBOutlet private weak var rateAppButton: UIButton!
@IBOutlet private weak var viewOnTwitterButton: UIButton!
@IBOutlet private weak var closeButton: UIButton!
@IBOutlet private weak var descriptionLabel: UILabel!
private var impactTimestamp: Date?
private var countdownTimer: Timer?
// MARK: - View Lifecycle
override func awakeFromNib() {
super.awakeFromNib()
override func setupUI() {
super.setupUI()
localizeUI()
setUI()
let stackView = UIStackView(arrangedSubviews: [shareButton, rateAppButton])
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .horizontal
stackView.distribution = .fillEqually
stackView.spacing = .cardVerticalSpacing
containerView.addSubview(notificationTitleLabel)
containerView.addSubview(notificationIntensityLabel)
containerView.addSubview(notificationDescriptionLabel)
containerView.addSubview(mapView)
containerView.addSubview(stackView)
containerView.addSubview(viewOnTwitterButton)
containerView.addSubview(descriptionLabel)
containerView.addSubview(closeButton)
notificationTitleLabel.topAnchor.constraint(equalTo: containerView.topAnchor, constant: .cardPadding).isActive = true
notificationTitleLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: .cardPadding).isActive = true
notificationTitleLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: .cardPadding.negative).isActive = true
notificationIntensityLabel.topAnchor.constraint(equalTo: notificationTitleLabel.bottomAnchor, constant: .cardVerticalSpacing).isActive = true
notificationIntensityLabel.leadingAnchor.constraint(equalTo: notificationTitleLabel.leadingAnchor).isActive = true
notificationIntensityLabel.trailingAnchor.constraint(equalTo: notificationTitleLabel.trailingAnchor).isActive = true
notificationDescriptionLabel.topAnchor.constraint(equalTo: notificationIntensityLabel.bottomAnchor, constant: .cardVerticalSpacing).isActive = true
notificationDescriptionLabel.leadingAnchor.constraint(equalTo: notificationTitleLabel.leadingAnchor).isActive = true
notificationDescriptionLabel.trailingAnchor.constraint(equalTo: notificationTitleLabel.trailingAnchor).isActive = true
mapView.topAnchor.constraint(equalTo: notificationDescriptionLabel.bottomAnchor, constant: .cardVerticalSpacing).isActive = true
mapView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor).isActive = true
mapView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true
mapView.heightAnchor.constraint(greaterThanOrEqualToConstant: 240.0).isActive = true
shareButton.heightAnchor.constraint(greaterThanOrEqualToConstant: 40.0).isActive = true
rateAppButton.heightAnchor.constraint(equalTo: shareButton.heightAnchor).isActive = true
viewOnTwitterButton.heightAnchor.constraint(equalTo: shareButton.heightAnchor).isActive = true
closeButton.heightAnchor.constraint(equalTo: shareButton.heightAnchor).isActive = true
stackView.topAnchor.constraint(equalTo: mapView.bottomAnchor, constant: .cardVerticalSpacing).isActive = true
stackView.leadingAnchor.constraint(equalTo: notificationDescriptionLabel.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: notificationDescriptionLabel.trailingAnchor).isActive = true
viewOnTwitterButton.topAnchor.constraint(equalTo: stackView.bottomAnchor, constant: .cardVerticalSpacing).isActive = true
viewOnTwitterButton.leadingAnchor.constraint(equalTo: stackView.leadingAnchor).isActive = true
viewOnTwitterButton.trailingAnchor.constraint(equalTo: stackView.trailingAnchor).isActive = true
descriptionLabel.topAnchor.constraint(equalTo: viewOnTwitterButton.bottomAnchor, constant: .cardPadding).isActive = true
descriptionLabel.leadingAnchor.constraint(equalTo: viewOnTwitterButton.leadingAnchor).isActive = true
descriptionLabel.trailingAnchor.constraint(equalTo: viewOnTwitterButton.trailingAnchor).isActive = true
closeButton.topAnchor.constraint(equalTo: descriptionLabel.bottomAnchor, constant: .cardVerticalSpacing).isActive = true
closeButton.leadingAnchor.constraint(equalTo: descriptionLabel.leadingAnchor).isActive = true
closeButton.trailingAnchor.constraint(equalTo: descriptionLabel.trailingAnchor).isActive = true
closeButton.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: .cardVerticalSpacing.negative).isActive = true
}
// MARK: - Private
private func localizeUI() {
override func updateUI() {
super.updateUI()
shareButton.setLocalizedTitle(key: "main_share_app")
rateAppButton.setLocalizedTitle(key: "main_vote")
viewOnTwitterButton.setLocalizedTitle(key: "main_twitter_see")
closeButton.setLocalizedTitle(key: "official_close")
descriptionLabel.text = NSLocalizedString("map_smartphone_magnitude", comment: "")
}
// MARK: - Public
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() {
@objc
func update(with notification: EQNRealtimePushNotification?) {
// clearn any other previous notifications
notificationTitleLabel.text = ""
notificationDescriptionLabel.text = ""
notificationTitleLabel.text = "Sisma rilevato a 150km (TEST)"
notificationIntensityLabel.text = "Previsto uno scuotimento forte"
notificationDescriptionLabel.text = "Distanza 150 km - 13 minuti fa"
mapView.removeAnnotations(mapView.annotations)
guard let notification = notification else { return }
containerView.backgroundColor = backgroundColor(for: notification.relativeIntensity())
backgroundColor = backgroundColor(for: notification.relativeIntensity())
notificationTitleLabel.text = notification.title
notificationIntensityLabel.text = notification.displayBody
notificationIntensityLabel.textColor = notification.relativeIntensityColor
@@ -95,7 +190,6 @@ class AlertsSeismicNotificationExpandedTableViewCell: EQNBaseTableViewCell, MKMa
notificationDescriptionLabel.text = ""
+ NSLocalizedString("official_card_distance", comment: "") + " \(distanceRound) km"
+ " - " + EQNUtility.formattedString(forTimeDifference: difference)
+ "\n" + String(format: NSLocalizedString("map_number", comment: ""), "\(notification.counter)")
}
let span = MKCoordinateSpan(latitudeDelta: 10.5, longitudeDelta: 10.5)
@@ -118,37 +212,37 @@ class AlertsSeismicNotificationExpandedTableViewCell: EQNBaseTableViewCell, MKMa
annotationView.title = annotation.title
return annotationView
}
// MARK: - Actions
@IBAction private func shareAppTapped(_ sender: UIButton) {
@objc private func shareAppTapped(_ sender: UIButton) {
onTapShareApp?()
}
@IBAction private func rateAppTapped(_ sender: UIButton) {
@objc private func rateAppTapped(_ sender: UIButton) {
onTapRateApp?()
}
@IBAction private func viewInTwitterTapped(_ sender: UIButton) {
@objc private func viewInTwitterTapped(_ sender: UIButton) {
onTapOpenTwitter?()
}
@IBAction private func closeTapped(_ sender: UIButton) {
@objc private func closeTapped(_ sender: UIButton) {
onTapClose?()
}
// MARK: - Helpers
// MARK: - Private
private func backgroundColor(for intensity: Double) -> UIColor {
switch intensity {
case _ where intensity < 0.004:
return UIColor(named: "Gray (card background)")!
return AppTheme.Colors.cardBackgroundGray
case _ where intensity < 0.30:
return UIColor(named: "Green (card background)")!
return AppTheme.Colors.cardBackgroundGreen
case _ where intensity < 0.70:
return UIColor(named: "Yellow (card background)")!
return AppTheme.Colors.cardBackgroundYellow
default:
return UIColor(named: "Red (card background)")!
return AppTheme.Colors.cardBackgroundRed
}
}
}
@@ -8,48 +8,82 @@
import UIKit
class AlertsSmartphoneNetworkTableViewCell: EQNBaseTableViewCell {
@objc var smartphoneNetwork: EQNReteSmartphone? {
didSet {
updateUI()
}
}
@objc
class AlertsSmartphoneNetworkTableViewCell: EQNBaseContainerTableViewCell {
@objc var onTapButton: (() -> Void)?
override var headerText: String { NSLocalizedString("main_network", comment: "") }
// MARK: - UI
private lazy var counterLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = AppTheme.Colors.green
label.font = .preferredFont(forTextStyle: .largeTitle)
label.textAlignment = .center
return label
}()
private lazy var descriptionLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.textColor = AppTheme.shared.cardTextColor
label.font = .preferredFont(forTextStyle: .body)
label.textAlignment = .center
return label
}()
private lazy var coverageButton: UIButton = {
let button = EQNRoundedButton.make(target: self, action: #selector(localCovergeTapped(_:)))
return button
}()
// MARK: - Internal
@IBOutlet private weak var headerLabel: UILabel!
@IBOutlet private weak var smartphoneCounterLabel: UILabel!
@IBOutlet private weak var coverageDescriptionLabel: UILabel!
@IBOutlet private weak var localCoverageButton: UIButton!
// MARK: - View Lifecycle
override func awakeFromNib() {
super.awakeFromNib()
override func setupUI() {
super.setupUI()
localizeUI()
containerView.addSubview(counterLabel)
containerView.addSubview(descriptionLabel)
containerView.addSubview(coverageButton)
counterLabel.topAnchor.constraint(equalTo: topView.bottomAnchor, constant: .cardVerticalSpacing).isActive = true
counterLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: .cardPadding).isActive = true
counterLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: .cardPadding.negative).isActive = true
descriptionLabel.topAnchor.constraint(equalTo: counterLabel.bottomAnchor, constant: .cardVerticalSpacing).isActive = true
descriptionLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: .cardPadding).isActive = true
descriptionLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: .cardPadding.negative).isActive = true
coverageButton.heightAnchor.constraint(greaterThanOrEqualToConstant: 40.0).isActive = true
coverageButton.topAnchor.constraint(equalTo: descriptionLabel.bottomAnchor, constant: .cardVerticalSpacing).isActive = true
coverageButton.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: .cardPadding).isActive = true
coverageButton.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: .cardPadding.negative).isActive = true
coverageButton.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: .cardPadding.negative).isActive = true
}
// MARK: - Private
private func localizeUI() {
headerLabel.text = NSLocalizedString("main_network", comment: "")
coverageDescriptionLabel.text = NSLocalizedString("main_monitoring_currently2", comment: "")
localCoverageButton.setLocalizedTitle(key: "main_coverage", uppercased: true, emoji: "🗺")
override func updateUI() {
super.updateUI()
coverageButton.setLocalizedTitle(key: "main_coverage", uppercased: true, emoji: "🗺")
descriptionLabel.text = NSLocalizedString("main_monitoring_currently2", comment: "")
}
private func updateUI() {
guard let smartphoneNetwork = smartphoneNetwork else { return }
smartphoneCounterLabel.text = "\(smartphoneNetwork.counterSmartphones)"
// MARK: - Public
@objc
func update(with smartphoneNetwork: EQNReteSmartphone?) {
guard let smartphoneNetwork else { return }
counterLabel.text = "\(smartphoneNetwork.counterSmartphones)"
}
// MARK: - Actions
@IBAction private func localCovergeTapped(_ sender: UIButton) {
@objc private func localCovergeTapped(_ sender: UIButton) {
onTapButton?()
}
}
@@ -98,8 +98,8 @@ class PasquakesMapViewController: EQNBaseMapViewController {
.first
// controlliamo che sia inferiore al raggio massimo impostato per le notifiche
if let radiusLow = Double(EQNAllertaSismica.shared().raggioSismiLievi),
let radiusStrong = Double(EQNAllertaSismica.shared().raggioSismiForti),
if let radiusLow = Double(EQNSettingRealTimeAlert.shared.raggioSismiLievi),
let radiusStrong = Double(EQNSettingRealTimeAlert.shared.raggioSismiForti),
let nearestPastquake = nearestPastquake {
let radius = max(radiusLow, radiusStrong)
if abs(nearestPastquake.coordinate.distance(from: userPosition)) < radius {
@@ -9,7 +9,6 @@
#import "EQNMainTabBarController.h"
#import "AppDelegate.h"
#import "EQNBaseViewController.h"
#import "SettingsBaseViewController.h"
#import "EQNManager.h"
#import "ServerRequest.h"
@@ -20,9 +19,6 @@
@implementation EQNMainTabBarController
static NSString * const SegueIdentifierSettings = @"ShowSettings";
static NSString * const SegueIdentifierLogs = @"ShowLogs";
#pragma mark - View Lifecycle
- (void)viewDidLoad
@@ -41,6 +37,7 @@ static NSString * const SegueIdentifierLogs = @"ShowLogs";
object:nil];
[self sincronizza];
[self migrationV5_8];
}
#pragma mark - Private
@@ -53,6 +50,23 @@ static NSString * const SegueIdentifierLogs = @"ShowLogs";
self.tabBar.items[EQNTabBarSectionImpostazioni].title = [NSLocalizedString(@"drawer_main_settings", comment: "") capitalizedString];
}
- (void)migrationV5_8
{
// forziamo il salvataggio delle impostazioni di notifica, perchè i vari valori devono essere migrati
BOOL alreadyMigrated = [NSUserDefaults.standardUserDefaults boolForKey:NSUserDefaults.SaveSettingsNotificationMigrationV5_8];
if (alreadyMigrated) {
return;
}
NSLog(@"[MIGRATION] perform notification settings save");
[SettingsBaseTableViewController saveSettingsWithCompletion:^(BOOL success) {
if (success) {
NSLog(@"[MIGRATION] settings saved");
[NSUserDefaults.standardUserDefaults setBool:true forKey:NSUserDefaults.SaveSettingsNotificationMigrationV5_8];
}
}];
}
#pragma mark - Notification
- (void)serverRegistrationFailedNotification:(NSNotification *)notification
@@ -119,7 +133,7 @@ static NSString * const SegueIdentifierLogs = @"ShowLogs";
// if user switch from settings page, we need to force a settings save
UIViewController *controller = [self getTopControllerFromController:tabBarController.selectedViewController];
if ([controller isKindOfClass:[SettingsViewController class]]) {
[SettingsBaseViewController saveSettings];
[SettingsBaseTableViewController saveSettings];
}
return YES;
@@ -1,151 +0,0 @@
//
// SubscriptionDetailViewController.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 SubscriptionDetailViewController: UIViewController {
/// Enable this allows shake to enable the current subscription
private static let ShakeToEnableSubscription = false
var product: SKProduct? {
didSet {
updateUI()
}
}
@IBOutlet private weak var containerView: UIView!
@IBOutlet private weak var productTitleLabel: UILabel!
@IBOutlet private weak var productImageView: UIImageView!
@IBOutlet private weak var productDescriptionLabel: UILabel!
@IBOutlet private weak var subscriptionDetailsLabel: UILabel!
@IBOutlet private weak var openPrivacyButton: UIButton!
@IBOutlet private weak var openTermsButton: UIButton!
@IBOutlet private weak var purchaseRecapLabel: UILabel!
@IBOutlet private weak var productPriceLabel: UILabel!
@IBOutlet private weak var purchaseButton: UIButton!
// MARK: - View Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(handlePurchaseNotification(_:)),
name: .EQNInAppPurchaseDidComplete,
object: nil)
updateUI()
setupUI()
}
// MARK: - Private
private func setupUI() {
containerView.eqn_applyShadowAndRoundedCorners()
}
private func updateUI() {
guard let product = product, isViewLoaded else { return }
productImageView.image = VersioneProProducts.image(for: product.productIdentifier)
productTitleLabel.text = product.localizedTitle
productDescriptionLabel.text = product.localizedDescription
var purchaseRecapString = ""
var subscriptionDetailsString = ""
switch product.productIdentifier {
case VersioneProProducts.Identifier.Subscription10kMonthly,
VersioneProProducts.Identifier.Subscription100kMonthly:
purchaseRecapString = "inapp_monthly_payment"
subscriptionDetailsString = "inapp_detail_description"
case VersioneProProducts.Identifier.Subscription100kYearly,
VersioneProProducts.Identifier.Subscription100kYearlyDiscounted,
VersioneProProducts.Identifier.Subscription10kYearly,
VersioneProProducts.Identifier.Subscription10kYearlyDiscounted:
purchaseRecapString = "inapp_yearly_payment"
subscriptionDetailsString = "inapp_detail_description"
case VersioneProProducts.Identifier.Subscription10kPerpetual,
VersioneProProducts.Identifier.Subscription100kPerpetual:
purchaseRecapString = "inapp_lifetime_payment"
subscriptionDetailsString = "inapp_lifetime_detail_description"
default:
break
}
subscriptionDetailsLabel.text = NSLocalizedString(subscriptionDetailsString, comment: "")
openPrivacyButton.setTitle(NSLocalizedString("network_pro_privacy_disclaimer", comment: ""), for: .normal)
openTermsButton.setTitle(NSLocalizedString("network_pro_terms_conditions", comment: ""), for: .normal)
purchaseRecapLabel.text = "\(product.localizedDescription), \(NSLocalizedString(purchaseRecapString, comment: ""))"
priceFormatter.locale = product.priceLocale
productPriceLabel.text = priceFormatter.string(from: product.price)
purchaseButton.setTitle(NSLocalizedString("inapp_purchase", comment: ""), for: .normal)
}
// MARK: - Notifications
@objc func handlePurchaseNotification(_ notification: Notification) {
navigationController?.popViewController(animated: true)
}
// MARK: - Actions
@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)
}
}
@IBAction func subscribeTapped(_ sender: UIButton) {
guard let product = product else { return }
VersioneProProducts.store.buyProduct(product)
}
// MARK: - Helper
private var priceFormatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.formatterBehavior = .behavior10_4
formatter.numberStyle = .currency
return formatter
}()
}
extension SubscriptionDetailViewController {
override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
guard let product = product, event?.subtype == .motionShake, Self.ShakeToEnableSubscription else {
return
}
let alert = UIAlertController(title: "🧑‍💻", message: "Please select an action", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Reset all purchases", style: .default) { action in
EQNPurchaseUtility.resetInAppPurchases()
})
alert.addAction(UIAlertAction(title: "Activate this subscription", style: .default) { action in
EQNPurchaseUtility.simulateProPurchase(identifier: product.productIdentifier)
})
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
present(alert, animated: true, completion: nil)
}
}
@@ -0,0 +1,204 @@
//
// SubscriptionDetailsTableViewCell.swift
// Earthquake Network
//
// Created by Andrea Busi on 18/06/24.
// Copyright © 2024 Earthquake Network. All rights reserved.
//
import UIKit
class SubscriptionDetailsTableViewCell: EQNBaseContainerTableViewCell {
var onTapPrivacy: () -> Void = { }
var onTapTerms: () -> Void = { }
var onTapPurchase: () -> Void = { }
var onChangePlan: (_ type: EQNInAppProducts.Plan) -> Void = { _ in }
override var isHeaderVisible: Bool { false }
// MARK: - UI
lazy var planSegmentedControl: UISegmentedControl = {
let control = UISegmentedControl(items: EQNInAppProducts.Plan.allCases.map(\.localizedTitle))
control.translatesAutoresizingMaskIntoConstraints = false
control.addTarget(self, action: #selector(onChangeSegmentedControl(_:)), for: .valueChanged)
return control
}()
lazy var productTitleLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = AppTheme.shared.cardTextColor
label.font = .preferredFont(forTextStyle: .title1)
label.textAlignment = .center
label.numberOfLines = 0
return label
}()
lazy var productImageView: UIImageView = {
let imageView = UIImageView(image: .init(named: "top_100k"))
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFit
imageView.heightAnchor.constraint(greaterThanOrEqualToConstant: 50.0).isActive = true
return imageView
}()
lazy var subscriptionDetailsLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = AppTheme.shared.cardTextColor
label.font = .preferredFont(forTextStyle: .body)
label.textAlignment = .justified
label.numberOfLines = 0
return label
}()
lazy var openPrivacyButton: UIButton = {
let button = UIButton(type: .system)
button.translatesAutoresizingMaskIntoConstraints = false
button.addTarget(self, action: #selector(onTapOpenPrivacyButton(_:)), for: .touchUpInside)
button.contentHorizontalAlignment = .leading
return button
}()
lazy var openTermsButton: UIButton = {
let button = UIButton(type: .system)
button.translatesAutoresizingMaskIntoConstraints = false
button.addTarget(self, action: #selector(onTapOpenTermsButton(_:)), for: .touchUpInside)
button.contentHorizontalAlignment = .leading
return button
}()
lazy var purchaseRecapLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = AppTheme.shared.cardTextColor
label.font = .preferredFont(forTextStyle: .headline)
label.numberOfLines = 0
label.textAlignment = .center
return label
}()
lazy var productPriceLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = AppTheme.shared.cardTextColor
label.font = .preferredFont(forTextStyle: .largeTitle)
label.numberOfLines = 0
label.textAlignment = .center
return label
}()
lazy var purchaseButton: UIButton = {
let button = UIButton(type: .system)
button.translatesAutoresizingMaskIntoConstraints = false
button.addTarget(self, action: #selector(onTapPurchaseButton(_:)), for: .touchUpInside)
button.titleLabel?.font = .preferredFont(forTextStyle: .headline)
button.heightAnchor.constraint(equalToConstant: 50.0).isActive = true
button.backgroundColor = .systemGroupedBackground
button.eqn_applyShadowAndRoundedCorners()
return button
}()
// MARK: - Internal
override func setupUI() {
super.setupUI()
containerView.addSubview(planSegmentedControl)
containerView.addSubview(productTitleLabel)
containerView.addSubview(productImageView)
containerView.addSubview(subscriptionDetailsLabel)
containerView.addSubview(openPrivacyButton)
containerView.addSubview(openTermsButton)
containerView.addSubview(purchaseRecapLabel)
containerView.addSubview(productPriceLabel)
containerView.addSubview(purchaseButton)
let leading: NSLayoutXAxisAnchor = planSegmentedControl.leadingAnchor
let trailing: NSLayoutXAxisAnchor = planSegmentedControl.trailingAnchor
planSegmentedControl.topAnchor.constraint(equalTo: topView.bottomAnchor, constant: .cardVerticalSpacing).isActive = true
planSegmentedControl.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: .cardPadding).isActive = true
planSegmentedControl.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: .cardPadding.negative).isActive = true
productTitleLabel.topAnchor.constraint(equalTo: planSegmentedControl.bottomAnchor, constant: .cardVerticalSpacing).isActive = true
productTitleLabel.leadingAnchor.constraint(equalTo: leading, constant: .cardPadding).isActive = true
productTitleLabel.trailingAnchor.constraint(equalTo: trailing, constant: .cardPadding.negative).isActive = true
productImageView.topAnchor.constraint(equalTo: productTitleLabel.bottomAnchor, constant: .cardVerticalSpacing.x2).isActive = true
productImageView.leadingAnchor.constraint(equalTo: leading).isActive = true
productImageView.trailingAnchor.constraint(equalTo: trailing).isActive = true
purchaseRecapLabel.topAnchor.constraint(equalTo: productImageView.bottomAnchor, constant: .cardVerticalSpacing.x2).isActive = true
purchaseRecapLabel.leadingAnchor.constraint(equalTo: leading).isActive = true
purchaseRecapLabel.trailingAnchor.constraint(equalTo: trailing).isActive = true
productPriceLabel.topAnchor.constraint(equalTo: purchaseRecapLabel.bottomAnchor, constant: .cardVerticalSpacing).isActive = true
productPriceLabel.leadingAnchor.constraint(equalTo: leading).isActive = true
productPriceLabel.trailingAnchor.constraint(equalTo: trailing).isActive = true
purchaseButton.topAnchor.constraint(equalTo: productPriceLabel.bottomAnchor, constant: .cardVerticalSpacing).isActive = true
purchaseButton.leadingAnchor.constraint(equalTo: leading).isActive = true
purchaseButton.trailingAnchor.constraint(equalTo: trailing).isActive = true
subscriptionDetailsLabel.topAnchor.constraint(equalTo: purchaseButton.bottomAnchor, constant: .cardVerticalSpacing.x2).isActive = true
subscriptionDetailsLabel.leadingAnchor.constraint(equalTo: leading).isActive = true
subscriptionDetailsLabel.trailingAnchor.constraint(equalTo: trailing).isActive = true
openPrivacyButton.topAnchor.constraint(equalTo: subscriptionDetailsLabel.bottomAnchor, constant: .cardVerticalSpacing.x2).isActive = true
openPrivacyButton.leadingAnchor.constraint(equalTo: leading).isActive = true
openPrivacyButton.trailingAnchor.constraint(equalTo: trailing).isActive = true
openTermsButton.topAnchor.constraint(equalTo: openPrivacyButton.bottomAnchor, constant: .cardPadding).isActive = true
openTermsButton.leadingAnchor.constraint(equalTo: leading).isActive = true
openTermsButton.trailingAnchor.constraint(equalTo: trailing).isActive = true
openTermsButton.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: .cardVerticalSpacing.x2.negative).isActive = true
}
override func updateUI() {
super.updateUI()
openPrivacyButton.setTitle(NSLocalizedString("network_pro_privacy_disclaimer", comment: ""), for: .normal)
openTermsButton.setTitle(NSLocalizedString("network_pro_terms_conditions", comment: ""), for: .normal)
purchaseButton.setTitle(NSLocalizedString("inapp_purchase", comment: ""), for: .normal)
}
// MARK: - Actions
@objc private func onTapOpenPrivacyButton(_ sender: UIButton) {
onTapPrivacy()
}
@objc private func onTapOpenTermsButton(_ sender: UIButton) {
onTapTerms()
}
@objc private func onTapPurchaseButton(_ sender: UIButton) {
onTapPurchase()
}
@objc private func onChangeSegmentedControl(_ sender: UISegmentedControl) {
let type: EQNInAppProducts.Plan = .from(index: sender.selectedSegmentIndex)
onChangePlan(type)
}
}
extension EQNInAppProducts.Plan {
var index: Int {
switch self {
case .monthly: 0
case .yearly: 1
case .perpetual: 2
}
}
static func from(index: Int) -> Self {
switch index {
case 0: .monthly
case 1: .yearly
default: .perpetual
}
}
}
@@ -0,0 +1,182 @@
//
// SubscriptionDetailsViewController.swift
// Earthquake Network
//
// Created by Andrea Busi on 18/06/24.
// Copyright © 2024 Earthquake Network. All rights reserved.
//
import UIKit
import StoreKit
import SafariServices
import Shogun
class SubscriptionDetailsViewController: UITableViewController {
/// Enable this allows shake to enable the current subscription
private static let ShakeToEnableSubscription = false
// MARK: - Internal
private let products: [EQNInAppProducts]
private var selectedProduct: EQNInAppProducts {
didSet {
onProductSelected()
}
}
private var priceFormatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.formatterBehavior = .behavior10_4
formatter.numberStyle = .currency
return formatter
}()
// MARK: - Init
init(
products: [EQNInAppProducts]
) {
self.products = products
self.selectedProduct = products.first(where: { $0.plan == .monthly }) ?? products.first!
super.init(style: .plain)
}
required init?(coder: NSCoder) {
fatalError("Please use init(products:) instead.")
}
// MARK: - View Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
configureUI()
addObservers()
}
private func addObservers() {
NotificationCenter.default.addObserver(self, selector: #selector(handlePurchaseNotification(_:)),
name: .EQNInAppPurchaseDidComplete,
object: nil)
}
private func configureUI() {
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 2000.0
tableView.separatorStyle = .none
tableView.backgroundColor = .systemGroupedBackground
tableView.registerCell(for: SubscriptionDetailsTableViewCell.self)
}
// MARK: - Notifications
@objc private func handlePurchaseNotification(_ notification: Notification) {
navigationController?.popViewController(animated: true)
}
// MARK: - Table view delegate & data source
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
1
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(cellIdentifiable: SubscriptionDetailsTableViewCell.self, for: indexPath)
cell.selectionStyle = .none
cell.productTitleLabel.text = selectedProduct.product.localizedTitle
cell.productImageView.image = selectedProduct.category.image
var purchaseRecapString = ""
var subscriptionDetailsString = ""
switch selectedProduct.productIdentifier {
case EQNInAppProducts.Identifier.Subscription10kMonthly,
EQNInAppProducts.Identifier.Subscription100kMonthly:
purchaseRecapString = "inapp_monthly_payment"
subscriptionDetailsString = "inapp_detail_description"
case EQNInAppProducts.Identifier.Subscription100kYearly,
EQNInAppProducts.Identifier.Subscription100kYearlyDiscounted,
EQNInAppProducts.Identifier.Subscription10kYearly,
EQNInAppProducts.Identifier.Subscription10kYearlyDiscounted:
purchaseRecapString = "inapp_yearly_payment"
subscriptionDetailsString = "inapp_detail_description"
case EQNInAppProducts.Identifier.Subscription10kPerpetual,
EQNInAppProducts.Identifier.Subscription100kPerpetual:
purchaseRecapString = "inapp_lifetime_payment"
subscriptionDetailsString = "inapp_lifetime_detail_description"
default:
break
}
cell.subscriptionDetailsLabel.text = NSLocalizedString(subscriptionDetailsString, comment: "")
cell.onTapPrivacy = { [weak self] in
self?.openExternalLink("\(EQNWebsiteAddress)/privacy/")
}
cell.onTapTerms = { [weak self] in
self?.openExternalLink("\(EQNWebsiteAddress)/terms-conditions/")
}
cell.onTapPurchase = { [weak self] in
self?.purchaseSelectedProduct()
}
cell.onChangePlan = { [weak self] type in
if let product = self?.productFromProductType(type) {
self?.selectedProduct = product
}
}
cell.planSegmentedControl.selectedSegmentIndex = selectedProduct.plan.index
cell.purchaseRecapLabel.text = "\(selectedProduct.product.localizedDescription), \(NSLocalizedString(purchaseRecapString, comment: ""))"
cell.productPriceLabel.text = priceFormatter.string(from: selectedProduct.product.price)
return cell
}
// MARK: - Private
private func onProductSelected() {
priceFormatter.locale = selectedProduct.product.priceLocale
tableView.reloadData()
}
private func openExternalLink(_ stringUrl: String) {
if let url = URL(string: stringUrl) {
let controller = SFSafariViewController(url: url)
present(controller, animated: true, completion: nil)
}
}
private func purchaseSelectedProduct() {
EQNInAppProducts.store.buyProduct(selectedProduct.product)
}
private func productFromProductType(_ type: EQNInAppProducts.Plan) -> EQNInAppProducts? {
let product: EQNInAppProducts?
switch type {
case .monthly:
product = products.first { $0.plan == .monthly }
case .yearly:
product = products.first { $0.plan == .yearly }
case .perpetual:
product = products.first { $0.plan == .perpetual }
}
return product
}
}
extension SubscriptionDetailsViewController {
override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
guard event?.subtype == .motionShake, Self.ShakeToEnableSubscription else {
return
}
let alert = UIAlertController(title: "🧑‍💻", message: "Please select an action", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Reset all purchases", style: .default) { action in
EQNPurchaseUtility.resetInAppPurchases()
})
alert.addAction(UIAlertAction(title: "Activate this subscription", style: .default) { action in
EQNPurchaseUtility.simulateProPurchase(identifier: self.selectedProduct.productIdentifier)
})
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
present(alert, animated: true, completion: nil)
}
}
@@ -9,57 +9,81 @@
import UIKit
import StoreKit
class SubscriptionProductTableViewCell: UITableViewCell {
class SubscriptionProductTableViewCell: EQNBaseContainerTableViewCell {
override var isHeaderVisible: Bool { false }
override var isRightArrowVisbile: Bool { true }
// MARK: - UI
private lazy var productImageView: UIImageView = {
let imageView = UIImageView(frame: .zero)
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFit
return imageView
}()
var product: SKProduct? {
didSet {
updateUI()
}
}
var availability: EQNPurchaseAvailability? {
didSet {
updateUI()
}
}
private lazy var productTitleLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = AppTheme.shared.cardTextColor
label.font = .preferredFont(forTextStyle: .headline)
label.numberOfLines = 0
return label
}()
@IBOutlet private weak var productImageView: UIImageView!
@IBOutlet private weak var productTitleLabel: UILabel!
@IBOutlet private weak var productDescriptionLabel: UILabel?
@IBOutlet private weak var productInfoLabel: UILabel!
// MARK: - View Lifecycle
// force an inset to have the same style of EQNBaseTableViewCell
override var frame: CGRect {
get {
return super.frame
}
set (newFrame) {
let inset: CGFloat = 8
var frame = newFrame
frame.origin.x += inset
frame.size.width -= 2 * inset
super.frame = frame
}
}
// MARK: - Private
private func updateUI() {
guard let product = product else { return }
private lazy var productInfoLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = AppTheme.Colors.red
label.font = .preferredFont(forTextStyle: .body)
label.numberOfLines = 0
return label
}()
productImageView.image = VersioneProProducts.image(for: product.productIdentifier)
productTitleLabel.text = product.localizedTitle
productDescriptionLabel?.text = product.localizedDescription
// MARK: - Internal
override func setupUI() {
super.setupUI()
let infoKey = VersioneProProducts.is100kSubscription(for: product.productIdentifier) ? "inapp_available_100k" : "inapp_available_10k"
let counter = availability(for: product.productIdentifier)
containerView.addSubview(productImageView)
containerView.addSubview(productTitleLabel)
containerView.addSubview(productInfoLabel)
productImageView.widthAnchor.constraint(equalToConstant: 80.0).isActive = true
productImageView.heightAnchor.constraint(equalToConstant: 40.0).isActive = true
productTitleLabel.topAnchor.constraint(equalTo: topView.bottomAnchor, constant: .cardVerticalSpacing).isActive = true
productTitleLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: .cardPadding.negative).isActive = true
productImageView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: .cardPadding).isActive = true
productImageView.trailingAnchor.constraint(equalTo: productTitleLabel.leadingAnchor, constant: .cardPadding.negative).isActive = true
productImageView.centerYAnchor.constraint(equalTo: productTitleLabel.centerYAnchor).isActive = true
productInfoLabel.topAnchor.constraint(equalTo: productTitleLabel.bottomAnchor, constant: .cardVerticalSpacing).isActive = true
productInfoLabel.leadingAnchor.constraint(equalTo: productImageView.leadingAnchor).isActive = true
productInfoLabel.trailingAnchor.constraint(equalTo: productTitleLabel.trailingAnchor).isActive = true
productInfoLabel.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: .cardVerticalSpacing.negative).isActive = true
}
// MARK: - Public
func update(
category: EQNInAppProducts.Category,
availability: EQNPurchaseAvailability?
) {
productImageView.image = category.image
productTitleLabel.text = category.localizedTitle
let infoKey = category == .top100k ? "inapp_available_100k" : "inapp_available_10k"
let counter = availabilityCounter(for: category, availability: availability)
productInfoLabel.text = String(format: NSLocalizedString(infoKey, comment: ""), counter)
}
private func availability(for productIdentifier: String) -> Int {
if VersioneProProducts.is100kSubscription(for: productIdentifier) {
private func availabilityCounter(
for category: EQNInAppProducts.Category,
availability: EQNPurchaseAvailability?
) -> Int {
if category == .top100k {
return availability?.top100kAvailable ?? 0
}
return availability?.top10kAvailable ?? 0
@@ -9,38 +9,87 @@
import UIKit
import StoreKit
class SubscriptionsActiveTableViewCell: EQNBaseTableViewCell {
var product: SKProduct? {
didSet {
updateUI()
}
}
class SubscriptionsActiveTableViewCell: EQNBaseContainerTableViewCell {
override var headerText: String { NSLocalizedString("inapp_active", comment: "") }
var onTapRestore: () -> Void = { }
// MARK: - UI
private lazy var noSubscriptionsLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = AppTheme.shared.cardTextColor
label.font = .preferredFont(forTextStyle: .body)
label.textAlignment = .center
label.numberOfLines = 0
return label
}()
@IBOutlet private weak var headerLabel: UILabel!
@IBOutlet private weak var noSubscriptionsLabel: UILabel!
@IBOutlet private weak var activeSubscriptionImageView: UIImageView!
private lazy var activeSubscriptionImageView: UIImageView = {
let imageView = UIImageView(frame: .zero)
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFit
return imageView
}()
// MARK: - View Lifecycle
private lazy var restoreButton: UIButton = {
let button = UIButton(type: .system)
button.translatesAutoresizingMaskIntoConstraints = false
button.addTarget(self, action: #selector(restoreSubscriptionsTapped(_:)), for: .touchUpInside)
button.titleLabel?.font = .preferredFont(forTextStyle: .headline)
button.backgroundColor = .systemGroupedBackground
button.eqn_applyShadowAndRoundedCorners()
return button
}()
// MARK: - Internal
override func awakeFromNib() {
super.awakeFromNib()
override func setupUI() {
super.setupUI()
localizeUI()
let stackView = UIStackView(arrangedSubviews: [ activeSubscriptionImageView, noSubscriptionsLabel, restoreButton ])
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.alignment = .center
stackView.distribution = .equalSpacing
stackView.axis = .vertical
stackView.spacing = 20.0
containerView.addSubview(stackView)
activeSubscriptionImageView.widthAnchor.constraint(equalToConstant: 150.0).isActive = true
activeSubscriptionImageView.heightAnchor.constraint(equalToConstant: 50.0).isActive = true
restoreButton.heightAnchor.constraint(equalToConstant: 40.0).isActive = true
restoreButton.leadingAnchor.constraint(equalTo: stackView.leadingAnchor).isActive = true
restoreButton.trailingAnchor.constraint(equalTo: stackView.trailingAnchor).isActive = true
stackView.topAnchor.constraint(equalTo: topView.bottomAnchor, constant: .cardVerticalSpacing).isActive = true
stackView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: .cardPadding).isActive = true
stackView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: .cardPadding.negative).isActive = true
stackView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: .cardVerticalSpacing.negative).isActive = true
}
// MARK: - Private
private func localizeUI() {
headerLabel.text = NSLocalizedString("inapp_active", comment: "")
override func updateUI() {
super.updateUI()
noSubscriptionsLabel.text = NSLocalizedString("inapp_nosub", comment: "")
restoreButton.setTitle(NSLocalizedString("purchase_pro_restore", comment: ""), for: .normal)
}
private func updateUI() {
if let productIdentifier = product?.productIdentifier {
// MARK: - Actions
@objc private func restoreSubscriptionsTapped(_ sender: UIButton) {
onTapRestore()
}
// MARK: - Public
func update(with product: EQNInAppProducts?) {
if let product {
noSubscriptionsLabel.isHidden = true
activeSubscriptionImageView.isHidden = false
activeSubscriptionImageView.image = VersioneProProducts.image(for: productIdentifier)
activeSubscriptionImageView.image = product.category.image
} else {
noSubscriptionsLabel.isHidden = false
activeSubscriptionImageView.isHidden = true
@@ -8,21 +8,40 @@
import UIKit
class SubscriptionsDescriptionTableViewCell: EQNBaseTableViewCell {
@IBOutlet private weak var headerLabel: UILabel!
@IBOutlet private weak var descriptionLabel: UILabel!
class SubscriptionsDescriptionTableViewCell: EQNBaseContainerTableViewCell {
override func awakeFromNib() {
super.awakeFromNib()
override var headerText: String { NSLocalizedString("inapp_list", comment: "") }
// MARK: - UI
private lazy var descriptionLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = AppTheme.shared.cardTextColor
label.font = .preferredFont(forTextStyle: .body)
label.textAlignment = .center
label.numberOfLines = 0
label.textAlignment = .justified
return label
}()
// MARK: - Internal
override func setupUI() {
super.setupUI()
localizeUI()
containerView.addSubview(descriptionLabel)
descriptionLabel.topAnchor.constraint(equalTo: topView.bottomAnchor, constant: .cardVerticalSpacing).isActive = true
descriptionLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: .cardPadding).isActive = true
descriptionLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: .cardPadding.negative).isActive = true
descriptionLabel.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: .cardVerticalSpacing.negative).isActive = true
}
// MARK: - Private
private func localizeUI() {
headerLabel.text = NSLocalizedString("inapp_list", comment: "")
override func updateUI() {
super.updateUI()
descriptionLabel.text = NSLocalizedString("inapp_description", comment: "")
}
}
@@ -8,26 +8,53 @@
import UIKit
class SubscriptionsHeaderTableViewCell: UITableViewCell {
var isLoading = false {
didSet {
updateUI()
}
class SubscriptionsHeaderTableViewCell: UITableViewHeaderFooterView {
// MARK: - UI
private lazy var headerTitleLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.preferredFont(forTextStyle: .title2)
label.textColor = AppTheme.Colors.darkGray
return label
}()
private lazy var loadingActivityIndicator: UIActivityIndicatorView = {
let spinner = UIActivityIndicatorView(style: .medium)
spinner.translatesAutoresizingMaskIntoConstraints = false
spinner.hidesWhenStopped = true
return spinner
}()
// MARK: - Init
override init(reuseIdentifier: String?) {
super.init(reuseIdentifier: reuseIdentifier)
setupUI()
}
var title: String? = nil {
didSet {
updateUI()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupUI()
}
@IBOutlet private weak var headerTitleLabel: UILabel!
@IBOutlet private weak var loadingActivityIndicator: UIActivityIndicatorView!
// MARK: - Private
private func updateUI() {
private func setupUI() {
contentView.addSubview(headerTitleLabel)
contentView.addSubview(loadingActivityIndicator)
headerTitleLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
headerTitleLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: .cardPadding).isActive = true
loadingActivityIndicator.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: .cardPadding.negative).isActive = true
loadingActivityIndicator.centerYAnchor.constraint(equalTo: headerTitleLabel.centerYAnchor).isActive = true
}
// MARK: - Public
func update(isLoading: Bool, title: String?) {
headerTitleLabel.text = title
if isLoading && title != nil {
@@ -10,38 +10,29 @@ import UIKit
import StoreKit
import Shogun
@objc
class SubscriptionsViewController: UITableViewController {
private static let SegueIdentifierSubscriptionDetail = "ShowSubscriptionDetail"
private static let CellHeightDescription: CGFloat = 320.0
// sezioni
private enum TableSection: CaseIterable {
case active
case description
case monthly
case yearly
case perpetual
case products
var sectionTitle: String? {
switch self {
case .monthly: return NSLocalizedString("inapp_monthly_subscriptions", comment: "")
case .yearly: return NSLocalizedString("inapp_yearly_subscriptions", comment: "")
case .perpetual: return NSLocalizedString("inapp_lifetime_subscriptions", comment: "")
default: return nil
case .products: NSLocalizedString("subscriptions_available", comment: "")
default: nil
}
}
}
private let sections = TableSection.allCases
private var allProducts = [SKProduct]()
private var monthlyProducts = [SKProduct]()
private var yearlyProducts = [SKProduct]()
private var perpetualProducts = [SKProduct]()
/// All products retrieved from AppStore
private var products = [EQNInAppProducts]()
/// Product already bought by the user
private var subscribedProduct: SKProduct?
private var productSubscribed: EQNInAppProducts?
/// Availability for subscriptions
private var availability: EQNPurchaseAvailability?
/// Tells if products are loading
@@ -49,6 +40,13 @@ class SubscriptionsViewController: UITableViewController {
/// Tells if a restore is in progress
private var isRestorePurchase = false
// MARK: - Init
@objc
convenience init() {
self.init(style: .plain)
}
// MARK: - View Lifecycle
override func viewDidLoad() {
@@ -64,15 +62,7 @@ class SubscriptionsViewController: UITableViewController {
loadData()
checkAvailabilities()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == Self.SegueIdentifierSubscriptionDetail,
let controller = segue.destination as? SubscriptionDetailViewController,
let product = sender as? SKProduct {
controller.product = product
}
}
// MARK: - Private
private func addObservers() {
@@ -90,75 +80,52 @@ class SubscriptionsViewController: UITableViewController {
}
private func configureUI() {
let restoreButton = UIBarButtonItem(title: NSLocalizedString("purchase_pro_restore", comment: ""),
style: .plain,
target: self,
action: #selector(restoreTapped(_:)))
navigationItem.rightBarButtonItem = restoreButton
// if is presented in Simulator, add done button
if navigationController?.viewControllers.first == self {
let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(closeTapped(_:)))
navigationItem.leftBarButtonItem = doneButton
}
navigationItem.largeTitleDisplayMode = .never
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = Self.CellHeightDescription;
}
private func updateUI() {
monthlyProducts.removeAll()
yearlyProducts.removeAll()
perpetualProducts.removeAll()
// creates list to show
let isDiscountAvailable = checkDiscountPrice()
allProducts.forEach { (product) in
if isDiscountAvailable {
if product.productIdentifier == VersioneProProducts.Identifier.Subscription10kMonthly ||
product.productIdentifier == VersioneProProducts.Identifier.Subscription100kMonthly {
monthlyProducts.append(product)
} else if product.productIdentifier == VersioneProProducts.Identifier.Subscription10kYearlyDiscounted ||
product.productIdentifier == VersioneProProducts.Identifier.Subscription100kYearlyDiscounted {
yearlyProducts.append(product)
}
} else {
if product.productIdentifier == VersioneProProducts.Identifier.Subscription10kMonthly ||
product.productIdentifier == VersioneProProducts.Identifier.Subscription100kMonthly {
monthlyProducts.append(product)
}
else if product.productIdentifier == VersioneProProducts.Identifier.Subscription10kYearly ||
product.productIdentifier == VersioneProProducts.Identifier.Subscription100kYearly {
yearlyProducts.append(product)
}
}
// perpetual scribuscriptions doesn't have discounted version
if product.productIdentifier == VersioneProProducts.Identifier.Subscription10kPerpetual ||
product.productIdentifier == VersioneProProducts.Identifier.Subscription100kPerpetual {
perpetualProducts.append(product)
}
}
tableView.reloadData()
tableView.estimatedRowHeight = 600.0
tableView.separatorStyle = .none
tableView.backgroundColor = .systemGroupedBackground
tableView.registerCell(for: SubscriptionsActiveTableViewCell.self)
tableView.registerCell(for: SubscriptionsDescriptionTableViewCell.self)
tableView.registerCell(for: SubscriptionProductTableViewCell.self)
tableView.registerHeaderFooterView(for: SubscriptionsHeaderTableViewCell.self)
}
private func loadData() {
isLoading = true
VersioneProProducts.store.requestProducts{ [weak self] success, products in
EQNInAppProducts.store.requestProducts { [weak self] success, storeProducts in
self?.isLoading = false
guard let self = self, let products = products, success == true else { return }
guard let self = self, let storeProducts, success == true else { return }
let purchased = products.filter { (product) -> Bool in
let isPurchased = VersioneProProducts.store.isProductPurchased(product.productIdentifier)
let isSubscription = VersioneProProducts.isSubscription(for: product.productIdentifier)
return isPurchased && isSubscription
}
self.subscribedProduct = purchased.first
self.allProducts = products.sorted(by: { $0.productIdentifier > $1.productIdentifier })
let products = storeProducts.compactMap { EQNInAppProducts.from(product: $0) }
self.updateUI()
let purchased = products
.filter { (product) -> Bool in
// filter for subscriptions
let isPurchased = EQNInAppProducts.store.isProductPurchased(product.productIdentifier)
let isSubscription = product.isSubscription
return isPurchased && isSubscription
}.sorted { lProduct, rProduct in
// if user has more than one subscriptions,
// show first the Top10k
let lIs10k = lProduct.isTop100k
let rIs10k = rProduct.isTop100k
if lIs10k {
return lIs10k
}
return rIs10k
}
self.productSubscribed = purchased.first
self.products = products.sorted(by: { $0.productIdentifier > $1.productIdentifier })
self.tableView.reloadData()
}
}
@@ -171,18 +138,13 @@ class SubscriptionsViewController: UITableViewController {
EQNPurchaseUtility.availableSubscriptions { (availability) in
DispatchQueue.main.async {
self.availability = availability
self.updateUI()
self.tableView.reloadData()
}
}
}
// MARK: - Actions
@objc func restoreTapped(_ sender: AnyObject) {
isRestorePurchase = true
VersioneProProducts.store.restorePurchases()
}
@objc func closeTapped(_ sender: AnyObject) {
dismiss(animated: true, completion: nil)
}
@@ -190,7 +152,7 @@ class SubscriptionsViewController: UITableViewController {
// MARK: - Notifications
@objc func fail(_ notification: Notification){
VersioneProProducts.store.loadPurchase()
EQNInAppProducts.store.loadPurchase()
}
@objc func handlePurchaseNotification(_ notification: Notification) {
@@ -209,7 +171,7 @@ class SubscriptionsViewController: UITableViewController {
present(alert, animated: true, completion: nil)
}
VersioneProProducts.store.loadPurchase()
EQNInAppProducts.store.loadPurchase()
loadData()
}
@@ -228,12 +190,9 @@ class SubscriptionsViewController: UITableViewController {
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let tableSection = sections[section]
if let cell = tableView.dequeueReusableCell(withIdentifier: "SectionHeaderCell") as? SubscriptionsHeaderTableViewCell {
cell.title = tableSection.sectionTitle
cell.isLoading = isLoading
return cell
}
return nil
let view = tableView.dequeueHeaderFooterView(cellIdentifiable: SubscriptionsHeaderTableViewCell.self)
view.update(isLoading: isLoading, title: tableSection.sectionTitle)
return view
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
@@ -249,96 +208,59 @@ class SubscriptionsViewController: UITableViewController {
switch tableSection {
case .active: return 1
case .description: return 1
case .monthly,
.yearly,
.perpetual:
return availableProducts(for: tableSection).count
case .products: return products.isEmpty ? 0 : 2
}
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let tableSection = sections[indexPath.section]
if tableSection == .description {
// autolayout in description doesn't work 🤷
return Self.CellHeightDescription
}
return UITableView.automaticDimension
}
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
let tableSection = sections[indexPath.section]
if tableSection == .active || tableSection == .description {
return
}
// add round borders to first and last row in products cells
let cornerRadius = AppTheme.shared.cardCornerRadius
var corners: UIRectCorner = []
if indexPath.row == 0 {
corners.update(with: .topLeft)
corners.update(with: .topRight)
}
if indexPath.row == tableView.numberOfRows(inSection: indexPath.section) - 1 {
corners.update(with: .bottomLeft)
corners.update(with: .bottomRight)
}
let maskLayer = CAShapeLayer()
maskLayer.path = UIBezierPath(roundedRect: cell.bounds,
byRoundingCorners: corners,
cornerRadii: CGSize(width: cornerRadius, height: cornerRadius)).cgPath
cell.layer.mask = maskLayer
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let tableSection = sections[indexPath.section]
if tableSection == .active {
let cell = tableView.dequeueReusableCell(withIdentifier: "ActiveSubscriptionsCell", for: indexPath) as! SubscriptionsActiveTableViewCell
cell.product = subscribedProduct
return cell
}
if tableSection == .description {
let cell = tableView.dequeueReusableCell(withIdentifier: "DescriptionCell", for: indexPath) as! SubscriptionsDescriptionTableViewCell
return cell
}
let products = availableProducts(for: tableSection)
let cell = tableView.dequeueReusableCell(withIdentifier: "SubscriptionCell", for: indexPath) as! SubscriptionProductTableViewCell
cell.product = products[indexPath.row]
cell.availability = availability
return cell
switch tableSection {
case .active:
let cell = tableView.dequeueReusableCell(cellIdentifiable: SubscriptionsActiveTableViewCell.self, for: indexPath)
cell.selectionStyle = .none
cell.update(with: productSubscribed)
cell.onTapRestore = { [weak self] in
guard let self else { return }
self.isRestorePurchase = true
EQNInAppProducts.store.restorePurchases()
}
return cell
case .description:
let cell = tableView.dequeueReusableCell(cellIdentifiable: SubscriptionsDescriptionTableViewCell.self, for: indexPath)
cell.selectionStyle = .none
return cell
case .products:
let cell = tableView.dequeueReusableCell(cellIdentifiable: SubscriptionProductTableViewCell.self, for: indexPath)
let category: EQNInAppProducts.Category = switch indexPath.row {
case 0: .top10k
case 1: .top100k
default: .top100k
}
cell.update(category: category, availability: availability)
return cell
}
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let tableSection = sections[indexPath.section]
let products = availableProducts(for: tableSection)
let products = availableProducts(for: indexPath)
if !products.isEmpty {
performSegue(withIdentifier: Self.SegueIdentifierSubscriptionDetail, sender: products[indexPath.row])
let controller = SubscriptionDetailsViewController(products: products)
navigationController?.pushViewController(controller, animated: true)
}
}
// MARK: - Helpers
private func availableProducts(for section: TableSection) -> [SKProduct] {
switch section {
case .monthly: return monthlyProducts
case .yearly: return yearlyProducts
case .perpetual: return perpetualProducts
private func availableProducts(for indexPath: IndexPath) -> [EQNInAppProducts] {
let section = sections[indexPath.section]
switch (section, indexPath.row) {
case (.products, 0): return products.filter { $0.isTop10k }
case (.products, 1): return products.filter { $0.isTop100k }
default: return []
}
}
}
extension SubscriptionsViewController: StoryboardInitializable {
static var storyboardName: String {
"Main"
}
static var storyboardControllerId: String {
"subscriptionsController"
}
}
@@ -15,6 +15,8 @@ class RealtimeAlertContainerView: UIView {
lazy var alertView: RealtimeAlertView = {
let view = RealtimeAlertView()
view.translatesAutoresizingMaskIntoConstraints = false
view.eqn_applyRoundedCorners()
view.clipsToBounds = true
return view
}()
@@ -78,10 +80,11 @@ class RealtimeAlertView: UIView {
let waveTimeLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = .preferredFont(forTextStyle: .title2)
label.font = .preferredFont(forTextStyle: .largeTitle)
label.textColor = AppTheme.Colors.red
label.text = String.localizedStringWithFormat(NSLocalizedString("alert_wave", comment: ""), 0)
label.textAlignment = .center
label.numberOfLines = 2
label.isHidden = true
return label
}()
@@ -89,7 +92,7 @@ class RealtimeAlertView: UIView {
let intensityLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = .preferredFont(forTextStyle: .title3)
label.font = .preferredFont(forTextStyle: .largeTitle)
label.textColor = AppTheme.Colors.red
label.textAlignment = .center
label.numberOfLines = 2
@@ -9,46 +9,97 @@
import UIKit
import Shogun
class SegnalazioniLast24HoursCell: EQNBaseTableViewCell {
@objc
class SegnalazioniLast24HoursCell: EQNBaseContainerTableViewCell {
@IBOutlet private weak var headerLabel: UILabel!
@IBOutlet private weak var reportsLabel: UILabel!
@IBOutlet private weak var reportsDescriptionLabel: UILabel!
@objc var onTapTwitter: (() -> Void)?
@objc var onTapMap: (() -> Void)?
@objc var onTapTelegram: (() -> Void)?
override var headerText: String { NSLocalizedString("tab_manual", comment: "") }
// MARK: - UI
private lazy var reportsLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = AppTheme.Colors.red
label.font = .preferredFont(forTextStyle: .largeTitle)
label.textAlignment = .center
label.numberOfLines = 0
return label
}()
private lazy var reportsDescriptionLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = AppTheme.shared.cardTextColor
label.font = .preferredFont(forTextStyle: .body)
label.numberOfLines = 0
label.textAlignment = .center
return label
}()
@IBOutlet private weak var twitterButton: UIButton! {
didSet {
twitterButton.imageView?.contentMode = .scaleAspectFit
}
}
@IBOutlet private weak var telegramButton: UIButton! {
didSet {
telegramButton.imageView?.contentMode = .scaleAspectFit
}
}
@IBOutlet private weak var mapButton: UIButton!
private lazy var twitterButton: UIButton = {
let button = EQNRoundedButton.make(target: self, action: #selector(twitterButtonTapped(_:)))
button.imageView?.contentMode = .scaleAspectFit
button.setImage(.init(named: "twitter_icon"), for: .normal)
return button
}()
private lazy var mapButton: UIButton = {
let button = EQNRoundedButton.make(title: NSLocalizedString("official_button_map", comment: ""), target: self, action: #selector(mapButtonTapped(_:)))
return button
}()
private lazy var telegramButton: UIButton = {
let button = EQNRoundedButton.make(target: self, action: #selector(telegramButtonTapped(_:)))
button.imageView?.contentMode = .scaleAspectFit
button.setImage(.init(named: "telegram_icon"), for: .normal)
return button
}()
// MARK: - View Lifecycle
// MARK: - Internal
override func awakeFromNib() {
super.awakeFromNib()
override func setupUI() {
super.setupUI()
localizeUI()
containerView.addSubview(reportsLabel)
containerView.addSubview(reportsDescriptionLabel)
let stackView = UIStackView(arrangedSubviews: [twitterButton, mapButton, telegramButton])
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .horizontal
stackView.spacing = .cardVerticalSpacing
stackView.distribution = .fillEqually
containerView.addSubview(stackView)
reportsLabel.topAnchor.constraint(equalTo: topView.bottomAnchor, constant: .cardPadding).isActive = true
reportsLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: .cardPadding).isActive = true
reportsLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: .cardPadding.negative).isActive = true
reportsDescriptionLabel.topAnchor.constraint(equalTo: reportsLabel.bottomAnchor, constant: .cardVerticalSpacing).isActive = true
reportsDescriptionLabel.leadingAnchor.constraint(equalTo: reportsLabel.leadingAnchor).isActive = true
reportsDescriptionLabel.trailingAnchor.constraint(equalTo: reportsLabel.trailingAnchor).isActive = true
stackView.heightAnchor.constraint(greaterThanOrEqualToConstant: 40.0).isActive = true
stackView.topAnchor.constraint(equalTo: reportsDescriptionLabel.bottomAnchor, constant: .cardVerticalSpacing).isActive = true
stackView.leadingAnchor.constraint(equalTo: reportsDescriptionLabel.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: reportsDescriptionLabel.trailingAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: .cardPadding.negative).isActive = true
}
// MARK: - Private
private func localizeUI() {
headerLabel.text = NSLocalizedString("tab_manual", comment: "").capitalized
override func updateUI() {
super.updateUI()
reportsDescriptionLabel.text = NSLocalizedString("main_map", comment: "")
mapButton.setTitle(NSLocalizedString("official_button_map", comment: ""), for: .normal)
}
// MARK: - Public
@objc func updateUI(for smartphoneNetwork: EQNReteSmartphone?) {
guard let smartphoneNetwork = smartphoneNetwork else {
return
}
@objc
func update(with smartphoneNetwork: EQNReteSmartphone?) {
guard let smartphoneNetwork = smartphoneNetwork else { return }
let reports = smartphoneNetwork.manual
self.reportsLabel.text = "\(reports)"
@@ -61,4 +112,18 @@ class SegnalazioniLast24HoursCell: EQNBaseTableViewCell {
self.reportsLabel.textColor = UIColor(hex6: 0xff0000)
}
}
// MARK: - Actions
@objc private func twitterButtonTapped(_ sender: UIButton) {
onTapTwitter?()
}
@objc private func mapButtonTapped(_ sender: UIButton) {
onTapMap?()
}
@objc private func telegramButtonTapped(_ sender: UIButton) {
onTapTelegram?()
}
}
@@ -28,35 +28,7 @@ class SegnalazioniMapViewController: EQNBaseMapViewController {
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
@@ -95,16 +67,11 @@ class SegnalazioniMapViewController: EQNBaseMapViewController {
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() {
@@ -153,7 +120,7 @@ class SegnalazioniMapViewController: EQNBaseMapViewController {
.first
// controlliamo che sia inferiore al raggio impostato per le notifiche
if let radius = Double(EQNNotificheSegnalazioniUtente.shared().distanzaPosizione),
if let radius = Double(EQNSettingUserReportNotification.shared.distanzaMassima),
let nearestCluser = nearestCluser,
abs(nearestCluser.distance(from: userPosition)) < radius {
centerLocation = nearestCluser
@@ -191,33 +158,20 @@ class SegnalazioniMapViewController: EQNBaseMapViewController {
@objc private func onTapMapDetailStyleButton(_ sender: Any) {
appPreferences.userReportExpandedView.toggle()
loadDataSource()
reloadMap()
}
@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 screenshot = createSnapshot {
// nascondiamo la legenda
magnitudeLegendView.isHidden = true
} restore: {
// ri-visualizziamo la legenda
magnitudeLegendView.isHidden = false
}
// torniamo allo stato originale
watermarkView.isHidden = true
magnitudeLegendView.isHidden = false
return image
let controller = UIActivityViewController(activityItems: [screenshot], applicationActivities: [])
present(controller, animated: true)
}
// MARK: - Private
@@ -233,10 +187,10 @@ class SegnalazioniMapViewController: EQNBaseMapViewController {
var cluster_code = 0
var vector_cluster = [Int](repeating: 0, count: vector_latitude.count)
for i in 0..<vector_latitude.count {
let deltaMinute_i = getDeltaMinute(vector_date[i])
let deltaMinute_i = EQNUtility.getDeltaMinute(vector_date[i])
if vector_cluster[i] == 0 && deltaMinute_i <= minutes {
for j in 0..<vector_latitude.count {
let deltaMinute_j = getDeltaMinute(vector_date[j])
let deltaMinute_j = EQNUtility.getDeltaMinute(vector_date[j])
if i != j && deltaMinute_j <= minutes {
if abs(vector_latitude[i] - vector_latitude[j]) < 4 && abs(vector_longitude[i] - vector_longitude[j]) < 4 && abs(deltaMinute_i - deltaMinute_j) <= 20 {
if vector_cluster[j] > 0 {
@@ -327,10 +281,6 @@ class SegnalazioniMapViewController: EQNBaseMapViewController {
mapView.addOverlays(overlays)
}
private func getDeltaMinute(_ date: Date) -> TimeInterval {
Date().timeIntervalSince(date) / 60.0
}
// MARK: - Map
override func setupAnnotationView(for annotation: MKAnnotation, on mapView: MKMapView) -> MKAnnotationView? {
@@ -7,49 +7,100 @@
//
import UIKit
import Shogun
class SegnalazioniSendReportCell: EQNBaseTableViewCell {
class SegnalazioniSendReportCell: EQNBaseContainerTableViewCell {
private struct Report: Equatable {
let magnitude: Int
let text: String
let color: UIColor
static func == (lhs: Self, rhs: Self) -> Bool {
lhs.magnitude == rhs.magnitude
}
}
@objc var onTapReport: (_ magnitude: Int) -> Void = { _ in }
override var headerText: String { NSLocalizedString("main_feel", comment: "") }
// 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
private let reports: [Report] = [
.init(magnitude: 20, text: NSLocalizedString("mercalli_II", comment: ""), color: .init(named: "Mercalli 20")!),
.init(magnitude: 30, text: NSLocalizedString("mercalli_III", comment: ""), color: .init(named: "Mercalli 30")!),
.init(magnitude: 40, text: NSLocalizedString("mercalli_IV", comment: ""), color: .init(named: "Mercalli 40")!),
.init(magnitude: 50, text: NSLocalizedString("mercalli_V", comment: ""), color: .init(named: "Mercalli 50")!),
.init(magnitude: 60, text: NSLocalizedString("mercalli_VI", comment: ""), color: .init(named: "Mercalli 60")!),
.init(magnitude: 70, text: NSLocalizedString("mercalli_VII", comment: ""), color: .init(named: "Mercalli 70")!),
.init(magnitude: 80, text: NSLocalizedString("mercalli_VIII", comment: ""), color: .init(named: "Mercalli 80")!),
.init(magnitude: 90, text: NSLocalizedString("mercalli_IX", comment: ""), color: .init(named: "Mercalli 90")!),
.init(magnitude: 100, text: NSLocalizedString("mercalli_X", comment: ""), color: .init(named: "Mercalli 100")!),
.init(magnitude: 110, text: NSLocalizedString("mercalli_XI", comment: ""), color: .init(named: "Mercalli 110")!),
.init(magnitude: 120, text: NSLocalizedString("mercalli_XII", comment: ""), color: .init(named: "Mercalli 120")!)
]
override func awakeFromNib() {
super.awakeFromNib()
// MARK: - Internal
override func setupUI() {
super.setupUI()
localizeUI()
var previousView = topView
reports.enumerated().forEach { index, report in
let view = createContentView(magnitude: report.magnitude, text: report.text, color: report.color)
containerView.addSubview(view)
view.leadingAnchor.constraint(equalTo: containerView.leadingAnchor).isActive = true
view.trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true
let padding: CGFloat = report == reports.first ? .cardPadding : 0
view.topAnchor.constraint(equalTo: previousView.bottomAnchor, constant: padding).isActive = true
if report == reports.last {
view.bottomAnchor.constraint(equalTo: containerView.bottomAnchor).isActive = true
}
previousView = view
}
}
// MARK: - Private
private func localizeUI() {
headerLabel.text = NSLocalizedString("main_feel", 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: "")
private func createContentView(
magnitude: Int,
text: String,
color: UIColor
) -> UIView {
let view = UIView(frame: .zero)
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = color
let label = UILabel(frame: .zero)
label.translatesAutoresizingMaskIntoConstraints = false
label.font = .preferredFont(forTextStyle: .body)
label.textColor = AppTheme.shared.cardTextColor
label.numberOfLines = 0
label.text = text
let button = UIButton(type: .system)
button.translatesAutoresizingMaskIntoConstraints = false
button.backgroundColor = .clear
button.tag = magnitude
button.addTarget(self, action: #selector(onTapMagnitudeButton(_:)), for: .touchUpInside)
view.addSubview(label)
view.addSubview(button)
// use a custom vertical spacing to make single lines bigger
let verticalSpacing: CGFloat = 15.0
label.topAnchor.constraint(equalTo: view.topAnchor, constant: verticalSpacing).isActive = true
label.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: .cardPadding).isActive = true
label.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: .cardPadding.negative).isActive = true
label.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -verticalSpacing).isActive = true
button.constraint(to: view)
return view
}
// MARK: - Actions
@@ -58,4 +109,9 @@ class SegnalazioniSendReportCell: EQNBaseTableViewCell {
let magnitude = sender.tag
onTapReport(magnitude)
}
@objc private func onTapMagnitudeButton(_ sender: UIButton) {
let magnitude = sender.tag
onTapReport(magnitude)
}
}
@@ -40,6 +40,8 @@
self.title = [NSLocalizedString(@"tab_manual", nil) capitalizedString];
self.tableView.estimatedRowHeight = 500.0;
self.tableView.rowHeight = UITableViewAutomaticDimension;
[self.tableView registerClass:[SegnalazioniLast24HoursCell class] forCellReuseIdentifier:@"Last24HCell"];
[self.tableView registerClass:[SegnalazioniSendReportCell class] forCellReuseIdentifier:@"ReportEarthquakeCell"];
}
- (void)refreshUI
@@ -74,8 +76,17 @@
{
if (indexPath.row == 0) {
SegnalazioniLast24HoursCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Last24HCell" forIndexPath:indexPath];
EQNReteSmartphone *reteSmartPhone = [EQNManager defaultManager].rete_smartphone;
[cell updateUIFor:reteSmartPhone];
[cell updateWith:[EQNManager defaultManager].rete_smartphone];
__weak SegnalazioniViewController *weakSelf = self;
cell.onTapMap = ^{
[weakSelf openMap];
};
cell.onTapTwitter = ^{
[weakSelf openTwitter];
};
cell.onTapTelegram = ^{
[weakSelf openTelegram];
};
return cell;
}
@@ -88,21 +99,21 @@
#pragma mark - Actions
- (IBAction)openMapTapped:(id)sender
- (void)openMap
{
SegnalazioniMapViewController *controller = [[SegnalazioniMapViewController alloc] init];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:controller];
[self presentViewController:navController animated:YES completion:nil];
}
- (IBAction)openTwitterTapped:(id)sender
- (void)openTwitter
{
NSURL *twitterUrl = [NSURL URLWithString:EQNTwitterProfileUrl];
SFSafariViewController *controller = [[SFSafariViewController alloc] initWithURL:twitterUrl];
[self presentViewController:controller animated:YES completion:nil];
}
- (IBAction)openTelegramTapped:(id)sender
- (void)openTelegram
{
NSURL *telegramUrl = [NSURL URLWithString:EQNTelegramUrl];
[[UIApplication sharedApplication] openURL:telegramUrl options:@{} completionHandler:nil];
@@ -52,13 +52,12 @@ class SeismicNetworkTableViewCell: UITableViewCell {
// MARK: - Internal
private static let DefaultVerticalSpacing: CGFloat = 6.0
private static let DefaultBodyFont = UIFont.preferredFont(forTextStyle: .body)
private static let DefaultBodyFontLight = UIFont.preferredFont(forTextStyle: .body, weight: .light)
/// Seismic to show
private var seismic: EQNSisma?
private(set) var displayType = DisplayType.normal
private var informationTypes = [InformationType]()
private var isPushSelected = false
private var colors: MagnitudeColors?
@@ -67,23 +66,22 @@ class SeismicNetworkTableViewCell: UITableViewCell {
private lazy var containerView: UIView = {
let view = UIView(frame: .zero)
view.translatesAutoresizingMaskIntoConstraints = false
view.layer.cornerRadius = AppTheme.shared.cardCornerRadius
view.layer.masksToBounds = false
// add shadow
view.layer.shadowColor = UIColor.black.cgColor
view.layer.shadowOpacity = 0.5
view.layer.shadowOffset = CGSize(width: 0, height: 2)
view.layer.shadowRadius = 2
view.backgroundColor = .white
view.clipsToBounds = true
return view
}()
private lazy var titleImageView: UIImageView = {
let imageView = UIImageView(frame: .zero)
imageView.translatesAutoresizingMaskIntoConstraints = false
return imageView
private lazy var gradientView: UIImageView = {
// Per gestire il gradiente, utilizziamo una image view in cui inseriamo un'immagine
// creata ad-hoc con il gradiente desiderato.
// Le prove fatte utilizzando una view normale sono fallite perchè al momento di
// disegnare la view non abbiamo le misure corrette.
let view = UIImageView(frame: .zero)
view.translatesAutoresizingMaskIntoConstraints = false
view.contentMode = .scaleToFill
return view
}()
private lazy var placeLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
@@ -95,8 +93,9 @@ class SeismicNetworkTableViewCell: UITableViewCell {
private lazy var networkLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.backgroundColor = UIColor.white.withAlphaComponent(0.5)
label.textAlignment = .center
label.textAlignment = .right
label.font = .preferredFont(forTextStyle: .subheadline)
label.numberOfLines = 2
return label
}()
@@ -111,35 +110,40 @@ class SeismicNetworkTableViewCell: UITableViewCell {
private lazy var depthLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = Self.DefaultBodyFontLight
label.font = .preferredFont(forTextStyle: .body)
label.numberOfLines = 2
return label
}()
private lazy var timeLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = Self.DefaultBodyFontLight
label.font = .preferredFont(forTextStyle: .body)
label.numberOfLines = 2
return label
}()
private lazy var distanceLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = Self.DefaultBodyFontLight
label.font = .preferredFont(forTextStyle: .body)
label.numberOfLines = 2
return label
}()
private lazy var coordinateLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = Self.DefaultBodyFontLight
label.font = .preferredFont(forTextStyle: .body)
label.numberOfLines = 2
return label
}()
private lazy var populationLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = Self.DefaultBodyFontLight
label.font = .preferredFont(forTextStyle: .body)
label.numberOfLines = 2
return label
}()
@@ -147,8 +151,9 @@ class SeismicNetworkTableViewCell: UITableViewCell {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.font = Self.DefaultBodyFont
label.font = .preferredFont(forTextStyle: .body)
label.textAlignment = .center
label.numberOfLines = 2
return label
}()
@@ -156,8 +161,9 @@ class SeismicNetworkTableViewCell: UITableViewCell {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.font = Self.DefaultBodyFont
label.font = .preferredFont(forTextStyle: .body)
label.textAlignment = .center
label.numberOfLines = 2
return label
}()
@@ -183,6 +189,15 @@ class SeismicNetworkTableViewCell: UITableViewCell {
setupUI()
}
// MARK: - View Lifecycle
override func layoutSubviews() {
super.layoutSubviews()
containerView.eqn_applyShadowAndRoundedCorners()
gradientView.eqn_applyRoundedCorners()
}
// MARK: - Setup
private func setupUI() {
@@ -196,6 +211,9 @@ class SeismicNetworkTableViewCell: UITableViewCell {
containerView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 8.0).isActive = true
containerView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -4.0).isActive = true
containerView.addSubview(gradientView)
gradientView.constraint(to: containerView)
// this variable is used to keep track of the previous view, in order to attach proper constraints
var previousView: UIView = containerView
@@ -230,16 +248,9 @@ class SeismicNetworkTableViewCell: UITableViewCell {
shareButton.setImage(UIImage(named: "share_icon"), for: .normal)
shareButton.addTarget(self, action: #selector(shareTapped(_:)), for: .touchUpInside)
stackViewTitle.addArrangedSubview(titleImageView)
stackViewTitle.addArrangedSubview(placeLabel)
stackViewTitle.addArrangedSubview(networkLabel)
stackViewTitle.addArrangedSubview(shareButton)
titleImageView.heightAnchor.constraint(equalToConstant: titleComponentsHeight).isActive = true
titleImageView.widthAnchor.constraint(equalTo: titleImageView.heightAnchor).isActive = true
networkLabel.heightAnchor.constraint(equalToConstant: 34.0).isActive = true
networkLabel.setContentHuggingPriority(.init(800), for: .horizontal)
networkLabel.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
placeLabel.setContentHuggingPriority(.init(200), for: .horizontal)
placeLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
shareButton.widthAnchor.constraint(equalToConstant: titleComponentsHeight).isActive = true
@@ -318,6 +329,13 @@ class SeismicNetworkTableViewCell: UITableViewCell {
previousView = separator3
}
// network
containerView.addSubview(networkLabel)
networkLabel.topAnchor.constraint(equalTo: previousView.bottomAnchor, constant: Self.DefaultVerticalSpacing).isActive = true
networkLabel.leadingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.leadingAnchor).isActive = true
networkLabel.trailingAnchor.constraint(equalTo: containerView.layoutMarginsGuide.trailingAnchor).isActive = true
previousView = networkLabel
if informationTypes.contains(.buttons) {
// buttons
let stackViewButtons = UIStackView()
@@ -326,11 +344,11 @@ class SeismicNetworkTableViewCell: UITableViewCell {
stackViewButtons.distribution = .fillEqually
stackViewButtons.spacing = 4
let buttonMap = createRoundedButton(title: "🗺", action: #selector(mapTapped(_:)))
let buttonMap = EQNRoundedButton.make(title: "🗺", target: self, action: #selector(mapTapped(_:)))
stackViewButtons.addArrangedSubview(buttonMap)
let buttonCalendar = createRoundedButton(title: "📆", action: #selector(calendarTapped(_:)))
let buttonCalendar = EQNRoundedButton.make(title: "📆", target: self, action: #selector(calendarTapped(_:)))
stackViewButtons.addArrangedSubview(buttonCalendar)
let buttonSettings = createRoundedButton(title: "🔧", action: #selector(settingsTapped(_:)))
let buttonSettings = EQNRoundedButton.make(title: "🔧", target: self, action: #selector(settingsTapped(_:)))
stackViewButtons.addArrangedSubview(buttonSettings)
containerView.addSubview(stackViewButtons)
@@ -353,7 +371,7 @@ class SeismicNetworkTableViewCell: UITableViewCell {
}
if (displayType == .mapExpanded) {
let buttonClose = createRoundedButton(title: NSLocalizedString("official_close", comment: "").uppercased(), action: #selector(closeTapped(_:)))
let buttonClose = EQNRoundedButton.make(title: NSLocalizedString("official_close", comment: "").uppercased(), target: self, action: #selector(closeTapped(_:)))
containerView.addSubview(buttonClose)
buttonClose.topAnchor.constraint(equalTo: previousView.bottomAnchor, constant: Self.DefaultVerticalSpacing).isActive = true
@@ -364,6 +382,9 @@ class SeismicNetworkTableViewCell: UITableViewCell {
else {
previousView.bottomAnchor.constraint(equalTo: containerView.layoutMarginsGuide.bottomAnchor).isActive = true
}
containerView.eqn_applyShadowAndRoundedCorners()
gradientView.eqn_applyRoundedCorners()
}
private func recreateUI() {
@@ -377,14 +398,16 @@ class SeismicNetworkTableViewCell: UITableViewCell {
let viewModel = SeismicNetworkViewModel(seismic: seismic)
containerView.backgroundColor = colors?.startColor
let notified = couldBeNotified(for: seismic)
titleImageView.image = notified ? UIImage(named: "bell") : UIImage(named: "bell_disabled")
if let colors = colors {
gradientView.image = .gradient(from: colors.startColor, to: colors.endColor, with: .init(origin: .zero, size: .init(width: 500, height: 1)))
} else {
gradientView.image = nil
}
// update seismic data
placeLabel.text = viewModel.place
networkLabel.text = viewModel.network + " " // add some padding
placeLabel.textColor = isPushSelected ? AppTheme.Colors.pureBlue : AppTheme.shared.cardTextColor
networkLabel.text = String(format: NSLocalizedString("official_provider", comment: ""), viewModel.network)
magnitudeLabel.textColor = colors?.textColor
magnitudeLabel.text = viewModel.magnitude
depthLabel.text = viewModel.depth
@@ -431,11 +454,17 @@ class SeismicNetworkTableViewCell: UITableViewCell {
/// - seismic: Seismic to display
/// - type: Type of cell
/// - informations: Informations to show
public func configure(with seismic: EQNSisma, type: DisplayType, informations: [InformationType]) {
public func configure(
with seismic: EQNSisma,
type: DisplayType,
informations: [InformationType],
isPushSelected: Bool
) {
self.seismic = seismic
self.colors = calculateColors(for: seismic.magnitude.doubleValue)
self.displayType = type
self.informationTypes = informations
self.isPushSelected = isPushSelected
if !informations.contains(.time) {
self.informationTypes += [.time]
@@ -499,48 +528,7 @@ class SeismicNetworkTableViewCell: UITableViewCell {
return separator
}
private func createRoundedButton(title: String, action: Selector) -> EQNRoundedButton {
let button = EQNRoundedButton(frame: .zero)
button.translatesAutoresizingMaskIntoConstraints = false
button.addTarget(self, action: action, for: .touchUpInside)
button.setTitle(title, for: .normal)
button.setTitleColor(AppTheme.Colors.darkGray, for: .normal)
button.backgroundColor = UIColor.white.withAlphaComponent(0.5)
return button
}
/// Check if the user could be received a notification for this seismic
private func couldBeNotified(for seismic: EQNSisma) -> Bool {
let settings = EQNNotificheReteSismiche.shared()
if !settings.isAbilitato {
return false
}
if !settings.listaEnti.contains(seismic.provider) {
return false
}
var notified = true
if let radius = Double(settings.distanzaPosizione), seismic.userDistance > radius {
notified = false
}
if let magnitude = Double(settings.energiaSisma), seismic.magnitude.doubleValue < magnitude {
notified = false
}
if settings.isAbilitaVicini, seismic.userDistance < 50 {
notified = true
}
if settings.isTerremortiForti, let strongMagnitude = Double(settings.energiaTerremotiForti), seismic.magnitude.doubleValue >= strongMagnitude {
notified = true
}
return notified
}
/// Determines the zoom for the map, based on the involved population
private func mapSpanLongitude(population: Double) -> CLLocationDegrees {
var zoom: CLLocationDegrees = 1
@@ -1,48 +0,0 @@
//
// FiltersViewModel.swift
// Earthquake Network
//
// Created by Andrea Busi on 22/03/21.
// Copyright © 2021 Earthquake Network. All rights reserved.
//
import Foundation
struct FiltersViewModel {
let magnitude: String
let distance: String
let timeframe: String
init() {
let magnitudoMinima = EQNData.magitudoDebole(for: EQNSeismic.shared.magnitudoMinima)
self.magnitude = Self.formattedMagnitude(magnitudoMinima.value)
let distanzaMassima = EQNData.raggioSisma(for: EQNSeismic.shared.distanzaMassima)
self.distance = Self.formattedDistance(distanzaMassima.value)
let periodoTemporale = EQNData.periodoTemporale(for: EQNSeismic.shared.periodoTemporale)
self.timeframe = Self.formattedTimeframe(periodoTemporale.value)
}
// MARK: - Private
private static func formattedMagnitude(_ magnitude: String) -> String {
return magnitude
}
private static func formattedDistance(_ distance: String) -> String {
if distance == EQNData.MaxRaggioSisma {
return ""
}
return "\(distance)km"
}
private static func formattedTimeframe(_ timeframe: String) -> String {
let time = Int(timeframe) ?? 0
if time < 60 {
return "\(time)m"
}
return "\(time/60)h"
}
}
@@ -16,13 +16,11 @@ protocol SeismicFiltersViewControllerDelegate: AnyObject {
class SeismicFiltersViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
private enum RowIdentifier: Int {
case magnitudoMinima
case sismiNelRaggio
case distanzaMassima
case periodoTemporale
case sismiFortiAbilita
case sismiFortiDistanza
case sismiQualsiasiMagnitudo
case modificaImpostazioni
case magnitudoMinima
case sismiRilevanti
case sismiTutti
}
weak var delegate: SeismicFiltersViewControllerDelegate?
@@ -37,29 +35,20 @@ class SeismicFiltersViewController: UIViewController, UITableViewDelegate, UITab
@IBOutlet private weak var closeButton: UIButton!
private var settings = [
SettingItem(type: .slider, title: NSLocalizedString("filter_magnitude", 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: "")),
SettingItem(type: .enable, title: NSLocalizedString("filter_near", comment: "")),
SettingItem(type: .enable, title: NSLocalizedString("filter_reflect", comment: ""))
SettingItem(type: .enable, title: NSLocalizedString("filter_show_area", comment: "")),
SettingItem(type: .slider, title: ""),
SettingItem(type: .slider, title: NSLocalizedString("filter_minimum_magnitude", comment: "")),
SettingItem(type: .enable, title: NSLocalizedString("filter_show_relevant", comment: "")),
SettingItem(type: .enable, title: NSLocalizedString("filter_show_all", comment: "")),
]
private let dataSourceMagnitudoMinima = EQNData.magitudoDeboli()
private let dataSourceDistanzaMassima = EQNData.raggioSismi()
private let dataSourcePeriodoTemporale = EQNData.periodiTemporali()
private let dataSourceSismiForti = EQNData.magitudoForti()
private var initialMagnitudoMinima: EQNGenericValue?
private var initialQualsiasiMagnitudo: Bool?
private let initialFilterType = EQNSeismic.shared.filterOption
private var currentFilterType = EQNSeismic.FilterType.inRadius
private var currentMaximumDistance = EQNData.DefaultFilterRadius
private var currentMinimumMagnitude = EQNData.DefaultFilterMagnitude
private var currentMagnitudoMinima = EQNData.DefaultMagitudoDebole
private var currentDistanzaMassima = EQNData.DefaultRaggioSisma
private var currentPeriodoTemporale = EQNData.DefaultPeriodoTemporale
private var currentSismiFortiAbilitati = false
private var currentSismiFortiDistanza = EQNData.DefaultMagitudoForte
private var currentSismiQualsiasiMagnitudo = false
private var currentModificaImpostazioni = false
private let dataSourceMaximumDistance = EQNData.filterRadius
private let dataSourceMinimumMagnitude = EQNData.filterMagnitude
// MARK: - View Lifecycle
@@ -86,19 +75,9 @@ class SeismicFiltersViewController: UIViewController, UITableViewDelegate, UITab
}
private func loadDataSource() {
currentMagnitudoMinima = EQNData.magitudoDebole(for: EQNSeismic.shared.magnitudoMinima)
if initialMagnitudoMinima == nil {
initialMagnitudoMinima = currentMagnitudoMinima
}
currentDistanzaMassima = EQNData.raggioSisma(for: EQNSeismic.shared.distanzaMassima)
currentPeriodoTemporale = EQNData.periodoTemporale(for: EQNSeismic.shared.periodoTemporale)
currentSismiFortiAbilitati = EQNSeismic.shared.sismiFortiAbilitati
currentSismiFortiDistanza = EQNData.magitudoForte(for: EQNSeismic.shared.sismiFortiMagnitudo)
currentSismiQualsiasiMagnitudo = EQNSeismic.shared.sismiQualsiasiAbilitati
if initialQualsiasiMagnitudo == nil {
initialQualsiasiMagnitudo = currentSismiQualsiasiMagnitudo
}
currentModificaImpostazioni = EQNSeismic.shared.modificaImpostazioniAbilitato
currentFilterType = EQNSeismic.shared.filterOption
currentMaximumDistance = EQNData.filterRadius(for: EQNSeismic.shared.maximumDistance)
currentMinimumMagnitude = EQNData.filterMagnitude(for: EQNSeismic.shared.minimumMagnitude)
}
// MARK: - Table view delegate and data source
@@ -108,45 +87,36 @@ class SeismicFiltersViewController: UIViewController, UITableViewDelegate, UITab
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let identifier = RowIdentifier(rawValue: indexPath.row) else {
return UITableViewCell()
}
let setting = settings[indexPath.row]
let isLocationAvailable = EQNUser.default().lastPosition != nil
switch setting.type {
case .slider:
let cell = SettingSliderTableViewCell(style: .default, reuseIdentifier: nil)
cell.titleLabel.text = setting.displayTitle
if indexPath.row == RowIdentifier.magnitudoMinima.rawValue {
cell.configureSlider(with: dataSourceMagnitudoMinima, current: currentMagnitudoMinima)
cell.valueChanged = { [unowned self] value in
currentMagnitudoMinima = value
EQNSeismic.shared.magnitudoMinima = value.value
EQNSeismic.shared.saveFilters()
let isFilterInRadiusEnabled = currentFilterType == .inRadius && isLocationAvailable
switch identifier {
case .distanzaMassima:
cell.isDisabled = !isFilterInRadiusEnabled
cell.isUserInteractionEnabled = isFilterInRadiusEnabled
cell.configureSlider(with: dataSourceMaximumDistance, current: currentMaximumDistance)
cell.valueChanged = { [weak self] value in
self?.onChangeMaximumDistance(value)
}
cell.dragEnded = { [unowned self] in
showWarningAlertIfNeeded(for: currentMagnitudoMinima)
}
} else if indexPath.row == RowIdentifier.distanzaMassima.rawValue {
cell.configureSlider(with: dataSourceDistanzaMassima, current: currentDistanzaMassima)
cell.valueChanged = { [unowned self] value in
currentDistanzaMassima = value
EQNSeismic.shared.distanzaMassima = value.value
EQNSeismic.shared.saveFilters()
}
} else if indexPath.row == RowIdentifier.periodoTemporale.rawValue {
cell.configureSlider(with: dataSourcePeriodoTemporale, current: currentPeriodoTemporale)
cell.valueChanged = { [unowned self] value in
currentPeriodoTemporale = value
EQNSeismic.shared.periodoTemporale = value.value
EQNSeismic.shared.saveFilters()
}
} else if indexPath.row == RowIdentifier.sismiFortiDistanza.rawValue {
cell.isDisabled = !currentSismiFortiAbilitati
cell.configureSlider(with: dataSourceSismiForti, current: currentSismiFortiDistanza)
cell.valueChanged = { [unowned self] value in
currentSismiFortiDistanza = value
EQNSeismic.shared.sismiFortiMagnitudo = value.value
EQNSeismic.shared.saveFilters()
case .magnitudoMinima:
cell.isDisabled = !isFilterInRadiusEnabled
cell.isUserInteractionEnabled = isFilterInRadiusEnabled
cell.configureSlider(with: dataSourceMinimumMagnitude, current: currentMinimumMagnitude)
cell.valueChanged = { [weak self] value in
self?.onChangeMinimumMagnitude(value)
}
default:
break
}
return cell
@@ -155,30 +125,31 @@ class SeismicFiltersViewController: UIViewController, UITableViewDelegate, UITab
cell.titleLabel.text = setting.displayTitle
cell.detailTextLabel?.text = setting.subtitle
if indexPath.row == RowIdentifier.sismiFortiAbilita.rawValue {
cell.toggleSwitch.isOn = currentSismiFortiAbilitati
cell.valueChanged = { [unowned self] value in
currentSismiFortiAbilitati = value
EQNSeismic.shared.sismiFortiAbilitati = value
EQNSeismic.shared.saveFilters()
loadDataSource()
tableView.reloadData()
switch identifier {
case .sismiNelRaggio:
let isCurrentFilter = currentFilterType == .inRadius
cell.isDisabled = !isLocationAvailable
cell.toggleSwitch.isOn = isCurrentFilter
cell.valueChanged = { [weak self] enabled in
self?.onChangeFilterOption(enabled, filter: .inRadius)
}
} else if indexPath.row == RowIdentifier.sismiQualsiasiMagnitudo.rawValue {
cell.toggleSwitch.isOn = currentSismiQualsiasiMagnitudo
cell.valueChanged = { [unowned self] value in
currentSismiQualsiasiMagnitudo = value
EQNSeismic.shared.sismiQualsiasiAbilitati = value
EQNSeismic.shared.saveFilters()
cell.errorLabel.text = !isLocationAvailable ? NSLocalizedString("filter_nolocation", comment: "") : nil
case .sismiRilevanti:
let isCurrentFilter = currentFilterType == .positionRelevant
cell.isDisabled = !isLocationAvailable
cell.toggleSwitch.isOn = isCurrentFilter
cell.valueChanged = { [weak self] enabled in
self?.onChangeFilterOption(enabled, filter: .positionRelevant)
}
} else if indexPath.row == RowIdentifier.modificaImpostazioni.rawValue {
cell.toggleSwitch.isOn = currentModificaImpostazioni
cell.valueChanged = { [unowned self] value in
currentModificaImpostazioni = value
EQNSeismic.shared.modificaImpostazioniAbilitato = value
EQNSeismic.shared.saveFilters()
cell.errorLabel.text = !isLocationAvailable ? NSLocalizedString("filter_nolocation", comment: "") : nil
case .sismiTutti:
let isCurrentFilter = currentFilterType == .worldWide
cell.toggleSwitch.isOn = isCurrentFilter
cell.valueChanged = { [weak self] enabled in
self?.onChangeFilterOption(enabled, filter: .worldWide)
}
default:
break
}
return cell
@@ -191,41 +162,34 @@ class SeismicFiltersViewController: UIViewController, UITableViewDelegate, UITab
@IBAction func exitTapped(_ sender: UIButton) {
// data needs to be re-downloaded if (or conditions):
// a) new magnitude is lower than the previous one and new value is less than 2.0
// b) show any near earthquake is active and value is changed
if let initialMagnitude = Float(initialMagnitudoMinima?.value ?? "10.0"), let currentMagnitude = Float(currentMagnitudoMinima.value) {
needsDataUpdate = currentMagnitude < 2.0 && initialMagnitude > currentMagnitude
}
if let initialQualsiasiMagnitudo = initialQualsiasiMagnitudo, currentSismiQualsiasiMagnitudo == true, initialQualsiasiMagnitudo != currentSismiQualsiasiMagnitudo {
needsDataUpdate = true
}
// a) filter type is changed
needsDataUpdate = initialFilterType != currentFilterType
delegate?.seismicFiltersControllerDidUpdateFilters(self)
updateNotificationSettingsIfNeeded()
dismiss(animated: true, completion: nil)
}
// MARK: - Private
private func showWarningAlertIfNeeded(for value: EQNGenericValue) {
guard let magnitude = Double(value.value), magnitude < 2.0 else { return }
private func onChangeFilterOption(_ enabled: Bool, filter: EQNSeismic.FilterType) {
currentFilterType = filter
EQNSeismic.shared.filterOption = filter
EQNSeismic.shared.saveFilters()
let alert = UIAlertController(title: NSLocalizedString("attention", comment: ""), message: NSLocalizedString("options_low_magnitude", comment: ""), preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("main_understood", comment: ""), style: .default, handler: nil))
present(alert, animated: true, completion: nil)
loadDataSource()
tableView.reloadData()
}
private func onChangeMaximumDistance(_ item: EQNGenericValue) {
currentMaximumDistance = item
EQNSeismic.shared.maximumDistance = item.value
EQNSeismic.shared.saveFilters()
}
private func updateNotificationSettingsIfNeeded() {
// if the switch is enabled, update also the settings notification
guard currentModificaImpostazioni == true else { return }
// update notification settings with current filters
EQNNotificheReteSismiche.shared().energiaSisma = EQNSeismic.shared.magnitudoMinima;
EQNNotificheReteSismiche.shared().distanzaPosizione = EQNSeismic.shared.distanzaMassima
EQNNotificheReteSismiche.shared().isAbilitaVicini = EQNSeismic.shared.sismiQualsiasiAbilitati
EQNNotificheReteSismiche.shared().isTerremortiForti = EQNSeismic.shared.sismiFortiAbilitati
EQNNotificheReteSismiche.shared().energiaTerremotiForti = EQNSeismic.shared.sismiFortiMagnitudo
private func onChangeMinimumMagnitude(_ item: EQNGenericValue) {
currentMinimumMagnitude = item
EQNSeismic.shared.minimumMagnitude = item.value
EQNSeismic.shared.saveFilters()
}
}
@@ -11,23 +11,25 @@ import Foundation
struct SeismicNetworkViewModel {
var place: String
var network: String
var isPreliminary: Bool
var magnitude: String
var depth: String
var time: String
var distance: String
var coordinate: String
var population: String
var smartphones: String
var users: String
let place: String
let network: String
let isPreliminary: Bool
let magnitude: String
let rawMagnitude: Double
let depth: String
let time: String
let distance: String
let coordinate: String
let population: String
let smartphones: String
let users: String
// MARK: - Init
init(seismic: EQNSisma) {
self.place = seismic.place
self.network = seismic.provider
self.rawMagnitude = seismic.magnitude.doubleValue
let isPreliminary = seismic.preliminary.intValue > 0
self.isPreliminary = isPreliminary
@@ -38,7 +40,7 @@ struct SeismicNetworkViewModel {
self.depth = ""
} else {
self.magnitude = String(format: "%.1f%@", seismic.magnitude.doubleValue, seismic.magnitudeType)
self.depth = String(format: "%@ %.1f km", NSLocalizedString("official_depth", comment: ""), seismic.depth.doubleValue)
self.depth = String(format: "%.1f km", seismic.depth.doubleValue)
}
// we need to check agains null values, because sometimes WS returns invalid dates
@@ -16,10 +16,27 @@ protocol SeismicNetworksMapDetailViewControllerDelegate: AnyObject {
class SeismicNetworksMapDetailViewController: EQNBaseMapViewController {
private enum PinStyle: CaseIterable {
case full
case light
case circle
func next() -> Self {
let all = Self.allCases
let idx = all.firstIndex(of: self)!
let next = all.index(after: idx)
return all[next == all.endIndex ? all.startIndex : next]
}
}
private var pinStyle: PinStyle = .full
private let eqnSeismic = EQNSeismic.shared
// MARK: - State
override var isCloseButtonVisible: Bool { false }
override var isFilterViewVisible: Bool {
// a custom filter view id displayed
// a custom filter view is displayed
true
}
weak var delegate: SeismicNetworksMapDetailViewControllerDelegate?
@@ -42,10 +59,7 @@ class SeismicNetworksMapDetailViewController: EQNBaseMapViewController {
seismicsFilterLabel.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
seismicsFilterLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 8.0).isActive = true
seismicsFilterLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -8.0).isActive = true
// tap recognizer
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(filtersTapped(_:)))
view.addGestureRecognizer(tapRecognizer)
return view
}()
@@ -76,6 +90,24 @@ class SeismicNetworksMapDetailViewController: EQNBaseMapViewController {
fatalError("init(coder:) is not available, please use init(seismic:allSeismics:)")
}
// MARK: - View Lifecycle
override func configureUI() {
super.configureUI()
navigationItem.leftBarButtonItem = UIBarButtonItem(systemItem: .done, primaryAction: .init(handler: { [weak self] _ in
self?.dismiss(animated: true)
}))
navigationItem.rightBarButtonItems = [
UIBarButtonItem(image: UIImage(named: "navbar-icon-screenshot"), primaryAction: .init(handler: { [weak self] _ in
self?.shareScreenshot()
})),
UIBarButtonItem(image: UIImage(named: "navbar-icon-pin-arrow"), primaryAction: .init(handler: { [weak self] _ in
self?.nextPinStyle()
})),
]
}
// MARK: - Public
func updateSeismics(_ seismics: [EQNSisma]) {
@@ -84,7 +116,9 @@ class SeismicNetworksMapDetailViewController: EQNBaseMapViewController {
}
override func registerMapAnnotationViews() {
mapView.register(EQNCustomAnnotationView.self, forAnnotationViewWithReuseIdentifier: EQNCustomAnnotationView.DoubleLineIdentifier)
mapView.register(EQNSeismicAnnotationView.self, forAnnotationViewWithReuseIdentifier: EQNSeismicAnnotationView.IdentifierFull)
mapView.register(EQNSeismicAnnotationView.self, forAnnotationViewWithReuseIdentifier: EQNSeismicAnnotationView.IdentifierLight)
mapView.register(EQNSeismicAnnotationView.self, forAnnotationViewWithReuseIdentifier: EQNSeismicAnnotationView.IdentifierCircle)
}
override func loadDataSource() {
@@ -92,14 +126,14 @@ class SeismicNetworksMapDetailViewController: EQNBaseMapViewController {
updateMap(with: annotations)
// if the given seismic is still in the data source, show circles
// otherwise just remove any other circles already on the map
if allSeismics.contains(seismic) {
addCircles(for: seismic.coordinate)
// if the filter is "in radius",
// show a circle with selected radius
if eqnSeismic.filterOption == .inRadius, let distance = Double(eqnSeismic.maximumDistance) {
addCircle(center: EQNUser.default().lastPosition, radius: distance * 1_000)
} else {
addCircles(for: nil)
addCircle(center: nil, radius: 0)
}
loadFiltersRecap()
}
@@ -127,31 +161,60 @@ class SeismicNetworksMapDetailViewController: EQNBaseMapViewController {
present(alert, animated: true)
}
// MARK: - Private
private func loadFiltersRecap() {
let filters = FiltersViewModel()
let recap = "\(NSLocalizedString("filter_filter", comment: "")): "
+ "M≥\(filters.magnitude) "
+ "D≤\(filters.distance) "
+ "T≤\(filters.timeframe)"
seismicsFilterLabel.text = recap
override func zPriority(for annotation: MKAnnotation) -> MKAnnotationViewZPriority {
guard let annotation = annotation as? EQNMapAnnotationSeismic else {
return .min
}
// il sisma cliccato dall'utente sta sopra a tutti
if annotation.seismic == seismic {
return .max
}
// Ordiniamo le annotazioni in base all amagnitudo, quelle con valore maggiore devono stare sopra.
// La `zPriority` viene calcolata utilizzando la posizione nella lista
let index = mapAnnotations
.compactMap { $0 as? EQNMapAnnotationSeismic }
.sorted(by: { $0.seismic.magnitude.doubleValue < $1.seismic.magnitude.doubleValue })
.firstIndex(where: { $0 == annotation })
guard let index else {
return .min
}
let priority = Float(index) / Float(mapAnnotations.count)
return .init(priority)
}
private func addCircles(for location: CLLocation?) {
// MARK: - Private
private func nextPinStyle() {
pinStyle = pinStyle.next()
reloadMap()
}
private func loadFiltersRecap() {
let filter = EQNSeismic.shared.filterOption
let text = switch filter {
case .inRadius: NSLocalizedString("filter_area", comment: "")
case .positionRelevant: NSLocalizedString("filter_relevant", comment: "")
case .worldWide: NSLocalizedString("filter_all", comment: "")
}
seismicsFilterLabel.text = text
}
private func addCircle(
center location: CLLocation?,
radius: Double
) {
// remove any previous circles
mapView.removeOverlays(mapCircles)
mapCircles.removeAll()
// aggiungiamo 3 cerchi concentrici per il punto specificato (se disponibile)
// li inseriamo a a 50, 100 e 200 km.
guard let location = location else { return }
let circles = [
MKCircle(center: location.coordinate, radius: 25_000),
MKCircle(center: location.coordinate, radius: 100_000),
MKCircle(center: location.coordinate, radius: 200_000)
MKCircle(center: location.coordinate, radius: radius),
]
// !!note: is important to assign here the circles
@@ -162,16 +225,13 @@ class SeismicNetworksMapDetailViewController: EQNBaseMapViewController {
mapView.addOverlays(circles)
}
// MARK: - Actions
@objc override func filtersTapped(_ sender: UIGestureRecognizer) {
let controller = SeismicFiltersViewController.makeViewController()
controller.delegate = self
controller.modalPresentationStyle = .overCurrentContext
controller.modalTransitionStyle = .crossDissolve
present(controller, animated: true, completion: nil)
private func shareScreenshot() {
let screenshot = createSnapshot()
let controller = UIActivityViewController(activityItems: [screenshot], applicationActivities: [])
present(controller, animated: true)
}
// MARK: - Map
override func setupAnnotationView(for annotation: MKAnnotation, on mapView: MKMapView) -> MKAnnotationView? {
@@ -179,15 +239,23 @@ class SeismicNetworksMapDetailViewController: EQNBaseMapViewController {
return nil
}
let viewModel = SeismicNetworkViewModel(seismic: annotation.seismic)
let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: EQNCustomAnnotationView.DoubleLineIdentifier, for: annotation) as! EQNCustomAnnotationView
annotationView.image = annotation.image
annotationView.title = annotation.title
annotationView.subtitle = viewModel.magnitude
return annotationView
let isUserSelection = annotation.seismic == seismic
switch pinStyle {
case .full, .light:
let identifier = pinStyle == .full ? EQNSeismicAnnotationView.IdentifierFull : EQNSeismicAnnotationView.IdentifierLight
let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier, for: annotation) as! EQNSeismicAnnotationView
annotationView.title = annotation.title
annotationView.subtitle = annotation.subtitle
annotationView.magnitude = String(format: "M%.1f", annotation.seismic.magnitude.doubleValue)
annotationView.magnitudeColor = annotation.textColor
annotationView.isUserSelection = isUserSelection
return annotationView
case .circle:
let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: EQNSeismicAnnotationView.IdentifierCircle, for: annotation) as! EQNSeismicAnnotationView
annotationView.image = annotation.image(height: EQNSeismicAnnotationView.CircleViewHeight,
isUserSelection: isUserSelection)
return annotationView
}
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
@@ -196,8 +264,8 @@ class SeismicNetworksMapDetailViewController: EQNBaseMapViewController {
}
let circle = MKCircleRenderer(overlay: circleOverlay)
circle.strokeColor = AppTheme.Colors.darkGray
circle.lineWidth = 1.0
circle.strokeColor = AppTheme.Colors.red
circle.lineWidth = 2.0
return circle
}
}
@@ -19,16 +19,10 @@ class SeismicNetworksViewController: UIViewController, UITableViewDelegate, UITa
}
private static let SegueIdentifierFilters = "ShowFilters"
private static let SegueIdentifierSettings = "ShowSettings"
private static let SegueIdentifierSeismicNetworks = "ShowSeismicNetworks"
private static let SegueIdentifierCardSettings = "ShowCardSettings"
// MARK: - Internal
@IBOutlet private weak var tableView: UITableView?
@IBOutlet private weak var expandeCollapseButton: UIBarButtonItem!
weak var currentMapController: SeismicNetworksMapDetailViewController?
/// The ad loader
private lazy var adLoader: GADAdLoader = {
let adLoader = GADAdLoader(
@@ -44,21 +38,64 @@ class SeismicNetworksViewController: UIViewController, UITableViewDelegate, UITa
private var informations = [SeismicNetworkTableViewCell.InformationType]()
/// Index path of row with map expanded
private var openMapIndexPath: IndexPath?
/// Push notification opened by the user
private var openedPushNotification: EQNOfficialPushNotification? {
didSet {
scrollToOpenedSeismic = true
}
}
private var scrollToOpenedSeismic = false
/// Current displayed controller with map
private weak var currentMapController: SeismicNetworksMapDetailViewController?
// MARK: - UI
@IBOutlet private weak var expandeCollapseButton: UIBarButtonItem!
@IBOutlet private weak var sortButton: UIBarButtonItem!
private var tableViewTopConstraint: NSLayoutConstraint?
private lazy var tableView: UITableView = {
let tableView = UITableView(frame: .zero, style: .plain)
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.delegate = self
tableView.dataSource = self
return tableView
}()
private lazy var filterChangedView: UIView = {
let view = UIView(frame: .zero)
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = AppTheme.Colors.pureBlue
view.addSubview(filterChangedLabel)
filterChangedLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 8.0).isActive = true
filterChangedLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -8.0).isActive = true
filterChangedLabel.topAnchor.constraint(equalTo: view.topAnchor, constant: 8.0).isActive = true
filterChangedLabel.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -8.0).isActive = true
return view
}()
private lazy var filterChangedLabel: UILabel = {
let label = UILabel(frame: .zero)
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = .white
label.font = .preferredFont(forTextStyle: .subheadline)
label.numberOfLines = 0
label.textAlignment = .center
return label
}()
// MARK: - View Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
configureUI()
checkForLocation()
refreshUI()
// only the first time, show the popup for country selection
let alreadyPresented = UserDefaults.standard.bool(forKey: EQNUserDefaultKeyOneShotShowCountry)
if !alreadyPresented {
performSegue(withIdentifier: Self.SegueIdentifierSettings, sender: nil)
UserDefaults.standard.setValue(true, forKey: EQNUserDefaultKeyOneShotShowCountry)
}
configureFilterView(isVisible: false)
NotificationCenter.default.addObserver(self, selector: #selector(didReceiveDownloadCompleteNotification(_:)), name: .EQNDownloadDataDidComplete, object: nil)
}
@@ -67,16 +104,95 @@ class SeismicNetworksViewController: UIViewController, UITableViewDelegate, UITa
super.viewWillAppear(animated)
loadData(forced: false)
// check for a push to manage
if let notification = EQNOfficialPushNotification.stored() {
manageFilter(for: notification)
self.openedPushNotification = notification
EQNOfficialPushNotification.removeStored()
} else {
configureFilterView(isVisible: false)
self.openedPushNotification = nil
}
tableView.reloadData()
}
// MARK: - Private
private func setupUI() {
view.addSubview(tableView)
tableViewTopConstraint = tableView.topAnchor.constraint(equalTo: view.topAnchor)
tableViewTopConstraint?.isActive = true
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
}
private func setupFilterView(isVisible: Bool) {
if isVisible && filterChangedView.superview == nil {
view.addSubview(filterChangedView)
tableViewTopConstraint?.isActive = false
filterChangedView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
filterChangedView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
filterChangedView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
tableViewTopConstraint = filterChangedView.bottomAnchor.constraint(equalTo: tableView.topAnchor)
tableViewTopConstraint?.isActive = true
} else {
filterChangedView.removeFromSuperview()
tableViewTopConstraint?.isActive = false
tableViewTopConstraint = tableView.topAnchor.constraint(equalTo: view.topAnchor)
tableViewTopConstraint?.isActive = true
}
}
private func configureFilterView(isVisible: Bool) {
setupFilterView(isVisible: isVisible)
if isVisible {
filterChangedLabel.text = NSLocalizedString("official_filter_changed", comment: "")
filterChangedView.backgroundColor = AppTheme.Colors.pureBlue
} else {
filterChangedLabel.text = nil
filterChangedView.backgroundColor = .white
}
}
private func configureUI() {
title = NSLocalizedString("tab_official", comment: "").capitalized
tableView?.estimatedRowHeight = 300.0
tableView?.rowHeight = UITableView.automaticDimension
tableView?.register(SeismicNetworkTableViewCell.self, forCellReuseIdentifier: SeismicNetworkTableViewCell.Identifier)
tableView?.register(SeismicNetworkAdvertiseTableViewCell.self, forCellReuseIdentifier: SeismicNetworkAdvertiseTableViewCell.Identifier)
tableView?.emptyDataSetSource = self
tableView.estimatedRowHeight = 300.0
tableView.rowHeight = UITableView.automaticDimension
tableView.registerCell(for: SeismicNetworkTableViewCell.self)
tableView.registerCell(for: SeismicNetworkAdvertiseTableViewCell.self)
tableView.emptyDataSetSource = self
tableView.separatorStyle = .none
setupSortMenu()
}
private func setupSortMenu() {
let currentSort = EQNSeismic.shared.sort
sortButton.menu = UIMenu(title: "", image: nil, identifier: nil, options: [], children: [
UIAction(title: NSLocalizedString("sort_date", comment: ""), image: UIImage(systemName: "calendar"), state: currentSort == .time ? .on : .off) { [weak self ] _ in
self?.changeSort(to: .time)
},
UIAction(title: NSLocalizedString("sort_position", comment: ""), image: UIImage(systemName: "ruler"), state: currentSort == .position ? .on : .off) { [weak self] _ in
self?.changeSort(to: .position)
},
UIAction(title: NSLocalizedString("sort_magnitude", comment: ""), image: UIImage(systemName: "thermometer"), state: currentSort == .magnitude ? .on : .off) { [weak self] _ in
self?.changeSort(to: .magnitude)
}
])
}
private func checkForLocation() {
// check if a valid location is available,
// otherwise change the filter settings
if !isLocationAvailable() {
EQNSeismic.shared.filterOption = .worldWide
EQNSeismic.shared.saveFilters()
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
@@ -85,14 +201,6 @@ class SeismicNetworksViewController: UIViewController, UITableViewDelegate, UITa
if let controller = segue.destination as? SeismicFiltersViewController {
controller.delegate = self
}
case Self.SegueIdentifierSettings:
if let controller = segue.destination as? SeismicSettingsViewController {
controller.delegate = self
}
case Self.SegueIdentifierSeismicNetworks:
if let navController = segue.destination as? UINavigationController, let controller = navController.viewControllers.first as? SeismicSettingsNetworksViewController {
controller.delegate = self
}
case Self.SegueIdentifierCardSettings:
if let controller = segue.destination as? SeismicCardSettingsViewController {
controller.delegate = self
@@ -106,7 +214,8 @@ class SeismicNetworksViewController: UIViewController, UITableViewDelegate, UITa
let seismics = getSeismics()
let controller = SeismicNetworksMapDetailViewController(seismic: seismic, allSeismics: seismics)
controller.delegate = self
present(controller, animated: true, completion: nil)
let navController = UINavigationController(rootViewController: controller)
present(navController, animated: true, completion: nil)
self.currentMapController = controller
}
@@ -136,7 +245,12 @@ class SeismicNetworksViewController: UIViewController, UITableViewDelegate, UITa
expandeCollapseButton.image = UIImage(named: "navbar-icon-arrow-expand")
}
tableView?.reloadData()
tableView.reloadData()
if scrollToOpenedSeismic, let index = getSeismics().firstIndex(where: { isSeismicToHighlight(seismic: $0) }) {
tableView.scrollToRow(at: IndexPath(row: index, section: 0), at: .middle, animated: true)
scrollToOpenedSeismic = false
}
}
private func loadAd() {
@@ -176,6 +290,276 @@ class SeismicNetworksViewController: UIViewController, UITableViewDelegate, UITa
return seismics
}
private func changeSort(to sort: EQNSeismic.Sort) {
EQNSeismic.shared.sort = sort
EQNSeismic.shared.saveFilters()
setupSortMenu()
refreshUI()
}
private func manageFilter(
for notification: EQNOfficialPushNotification
) {
//gestisco i filtri solo se la posizione dell'utente è nota
guard let userPosition = EQNUser.default().lastPosition else {
return
}
var filter_type = EQNSeismic.shared.filterOption
var filter_radius = Double(EQNSeismic.shared.maximumDistance) ?? 0
var filter_min_magnitude = Double(EQNSeismic.shared.minimumMagnitude) ?? 0
var filter_changed = false
//recupero i dati del sisma notificato
let notification_magnitude = notification.magnitude
let notification_latitude = notification.coordinate.coordinate.latitude
let notification_longitude = notification.coordinate.coordinate.longitude
//distanza tra smartphone utente e sisma notificato
let locationNotification = CLLocation(latitude: notification_latitude, longitude: notification_longitude)
let distance = userPosition.distance(from: locationNotification) / 1_000
//verifico se il sisma è significativo in base alla definizione di significativo
var is_significant = true
if notification_magnitude < 7.0 && distance > 2000 {
is_significant = false
} else if notification_magnitude < 6.5 && distance > 1600 {
is_significant = false
} else if notification_magnitude < 6.0 && distance > 1300 {
is_significant = false
} else if notification_magnitude < 5.5 && distance > 1000 {
is_significant = false
} else if notification_magnitude < 5.0 && distance > 700 {
is_significant = false
} else if notification_magnitude < 4.5 && distance > 500 {
is_significant = false
} else if notification_magnitude < 4.0 && distance > 350 {
is_significant = false
} else if notification_magnitude < 3.5 && distance > 200 {
is_significant = false
} else if notification_magnitude < 3.0 && distance > 125 {
is_significant = false
} else if notification_magnitude < 2.5 && distance > 70 {
is_significant = false
} else if notification_magnitude < 2.0 && distance > 35 {
is_significant = false
} else if notification_magnitude < 1.5 && distance > 20 {
is_significant = false
}
//verifico se devo modificare il filtro scelto dall'utente
if filter_type == .inRadius { //filter_type=0 è il filtro basato su raggio e magnitudo
if distance > 2000 && is_significant {
filter_type = .positionRelevant //passo al filtro che mostra i sismi significativi (perché il raggio massimo del filtro basato sul raggio è 2000)
updateFilter(type: filter_type)
filter_changed = true
}
else if distance > 2000 && notification_magnitude >= 2.0 {
filter_type = .worldWide //passo al filtro che mostra tutti i sismi nel mondo
updateFilter(type: filter_type)
filter_changed = true
}
else {
//verifico se devo cambiare il raggio del filtro
if distance > filter_radius {
if distance > 1500 {
filter_radius = 2000
} else if distance > 1000 {
filter_radius = 1500
} else if distance > 750 {
filter_radius = 1000
} else if distance > 500 {
filter_radius = 750
} else if distance > 250 {
filter_radius = 500
} else if distance > 100 {
filter_radius = 250
}
updateFilter(radius: filter_radius)
filter_changed = true
}
//verifico se devo cambiare la mgnitudo del filtro
if notification_magnitude < filter_min_magnitude {
if notification_magnitude < 1.0 {
filter_min_magnitude = 0.0
} else if notification_magnitude < 2.0 {
filter_min_magnitude = 1.0
} else if notification_magnitude < 3.0 {
filter_min_magnitude = 2.0
} else if notification_magnitude < 4.0 {
filter_min_magnitude = 3.0
} else if notification_magnitude < 5.0 {
filter_min_magnitude = 4.0
} else if notification_magnitude < 6.0 {
filter_min_magnitude = 5.0
}
filter_changed = true
updateFilter(magnitude: filter_min_magnitude)
}
}
}
if filter_type == .positionRelevant && !is_significant && distance <= 2000 {
filter_type = .inRadius //passo a filtro basato su raggio e magnitudo
updateFilter(type: filter_type)
if distance > filter_radius {
if distance>1500 {
filter_radius = 2000
}
else if distance > 1000 {
filter_radius = 1500
}
else if distance > 750 {
filter_radius = 1000
}
else if distance > 500 {
filter_radius = 750
}
else if distance > 250 {
filter_radius = 500
}
else if distance > 100 {
filter_radius = 250
}
updateFilter(radius: filter_radius)
}
if notification_magnitude < filter_min_magnitude {
if notification_magnitude < 1.0 {
filter_min_magnitude = 0.0
}
else if notification_magnitude < 2.0 {
filter_min_magnitude = 1.0
}
else if notification_magnitude < 3.0 {
filter_min_magnitude = 2.0
}
else if notification_magnitude < 4.0 {
filter_min_magnitude = 3.0
}
else if notification_magnitude < 5.0 {
filter_min_magnitude = 4.0
}
else if notification_magnitude < 6.0 {
filter_min_magnitude = 5.0
}
updateFilter(magnitude: filter_min_magnitude)
}
filter_changed = true
}
if filter_type == .positionRelevant && !is_significant && distance > 2000 && notification_magnitude >= 2.0 {
filter_type = .worldWide //passo a filtro che mostra tutti i sismi nel mondo
updateFilter(type: filter_type)
filter_changed = true
}
if filter_type == .worldWide && notification_magnitude < 2.0 && is_significant {
filter_type = .positionRelevant //passo a filtro sismi significativi
updateFilter(type: filter_type)
filter_changed = true
}
if filter_type == .worldWide && notification_magnitude < 2.0 && distance <= 2000 && !is_significant {
filter_type = .inRadius
updateFilter(type: filter_type)
if distance > filter_radius {
if distance > 1500 {
filter_radius = 2000
}
else if distance > 1000 {
filter_radius = 1500
}
else if distance > 750 {
filter_radius = 1000
}
else if distance > 500 {
filter_radius = 750
}
else if distance > 250 {
filter_radius = 500
}
else if distance > 100 {
filter_radius = 250
}
updateFilter(radius: filter_radius)
}
if notification_magnitude < filter_min_magnitude {
if notification_magnitude < 1.0 {
filter_min_magnitude = 0.0
}
else if notification_magnitude < 2.0 {
filter_min_magnitude = 1.0
}
else if notification_magnitude < 3.0 {
filter_min_magnitude = 2.0
}
else if notification_magnitude < 4.0 {
filter_min_magnitude = 3.0
}
else if notification_magnitude < 5.0 {
filter_min_magnitude = 4.0
}
else if notification_magnitude < 6.0 {
filter_min_magnitude = 5.0
}
updateFilter(magnitude: filter_min_magnitude)
}
filter_changed = true
}
//mostro all'utente un messaggio per avvisarlo che i filtri sono stati modificati
configureFilterView(isVisible: filter_changed)
if filter_changed {
loadData(forced: true)
}
}
private func updateFilter(
type: EQNSeismic.FilterType? = nil,
radius: Double? = nil,
magnitude: Double? = nil
) {
if let type {
EQNSeismic.shared.filterOption = type
}
if let radius {
EQNSeismic.shared.maximumDistance = String(format: "%.0f", radius)
}
if let magnitude {
EQNSeismic.shared.minimumMagnitude = String(format: "%.1f", magnitude)
}
EQNSeismic.shared.saveFilters()
}
private func isLocationAvailable() -> Bool {
EQNUser.default().lastPosition != nil
}
private func isSeismicToHighlight(seismic: EQNSisma) -> Bool {
guard let notification = openedPushNotification else {
return false
}
guard let seismicDate = seismic.date, let notificationDate = notification.date else { return false }
let deltaTime = abs(seismicDate.timeIntervalSince(notificationDate))
let magnitudeRatio = seismic.magnitude.doubleValue / notification.magnitude
let latitudeDiff = abs(seismic.coordinate.coordinate.latitude - notification.coordinate.coordinate.latitude)
let longitudeDiff = abs(seismic.coordinate.coordinate.longitude - notification.coordinate.coordinate.longitude)
if deltaTime <= 120 && magnitudeRatio > 0.8 && magnitudeRatio < 1.2 && latitudeDiff < 1 && longitudeDiff < 1 { // secondi?
return true
}
return false
}
// MARK: - Actions
@IBAction func refreshDataTapped(_ sender: Any) {
@@ -185,11 +569,7 @@ class SeismicNetworksViewController: UIViewController, UITableViewDelegate, UITa
@IBAction func openFilterTapped(_ sender: Any) {
performSegue(withIdentifier: Self.SegueIdentifierFilters, sender: nil)
}
@IBAction func openSettingsTapped(_ sender: Any) {
performSegue(withIdentifier: Self.SegueIdentifierSettings, sender: nil)
}
@IBAction func collapseExpandTapped(_ sender: Any) {
if informations.contains(.buttons) {
informations.removeAll(where: { $0 == .buttons })
@@ -211,18 +591,19 @@ class SeismicNetworksViewController: UIViewController, UITableViewDelegate, UITa
let row = rows[indexPath.row]
switch row {
case .seismic(let seismic):
let cell = tableView.dequeueReusableCell(withIdentifier: SeismicNetworkTableViewCell.Identifier, for: indexPath) as! SeismicNetworkTableViewCell
let cell = tableView.dequeueReusableCell(cellIdentifiable: SeismicNetworkTableViewCell.self, for: indexPath)
var type = SeismicNetworkTableViewCell.DisplayType.normal
if openMapIndexPath == indexPath {
type = .mapExpanded
}
cell.configure(with: seismic, type: type, informations: informations)
let isPushSelected = isSeismicToHighlight(seismic: seismic)
cell.configure(with: seismic, type: type, informations: informations, isPushSelected: isPushSelected)
cell.delegate = self
return cell
case .advertise(let nativeAd):
let cell = tableView.dequeueReusableCell(withIdentifier: SeismicNetworkAdvertiseTableViewCell.Identifier, for: indexPath) as! SeismicNetworkAdvertiseTableViewCell
let cell = tableView.dequeueReusableCell(cellIdentifiable: SeismicNetworkAdvertiseTableViewCell.self, for: indexPath)
cell.loadNativeAd(nativeAd)
return cell
}
@@ -298,7 +679,7 @@ extension SeismicNetworksViewController: GADNativeAdLoaderDelegate {
let adPosition = min(3, rows.count)
rows.insert(.advertise(nativeAd), at: adPosition)
tableView?.reloadData()
tableView.reloadData()
}
func adLoader(_ adLoader: GADAdLoader, didFailToReceiveAdWithError error: Error) {
@@ -310,7 +691,7 @@ extension SeismicNetworksViewController: GADNativeAdLoaderDelegate {
extension SeismicNetworksViewController: SeismicNetworkTableViewCellDelegate {
func seismicNetworkCellDidTapShare(_ cell: SeismicNetworkTableViewCell) {
guard let index = tableView?.indexPath(for: cell), case let .seismic(seismic) = rows[index.row] else { return }
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.contentView.createSnapshot()
@@ -327,22 +708,22 @@ extension SeismicNetworksViewController: SeismicNetworkTableViewCellDelegate {
}
func seismicNetworkCellDidTapMap(_ cell: SeismicNetworkTableViewCell) {
guard let index = tableView?.indexPath(for: cell) else { return }
guard let index = tableView.indexPath(for: cell) else { return }
let indexToReloads = [openMapIndexPath, index].compactMap { $0 }
openMapIndexPath = index
tableView?.reloadRows(at: indexToReloads, with: .automatic)
tableView.reloadRows(at: indexToReloads, with: .automatic)
}
func seismicNetworkCellDidTapMapDetail(_ cell: SeismicNetworkTableViewCell) {
guard let index = tableView?.indexPath(for: cell), case let .seismic(seismic) = rows[index.row] else { return }
guard let index = tableView.indexPath(for: cell), case let .seismic(seismic) = rows[index.row] else { return }
showMapDetail(for: seismic)
}
func seismicNetworkCellDidTapCalendar(_ cell: SeismicNetworkTableViewCell) {
guard let index = tableView?.indexPath(for: cell), case let .seismic(seismic) = rows[index.row] else { return }
guard let index = tableView.indexPath(for: cell), case let .seismic(seismic) = rows[index.row] else { return }
openCalendar(for: seismic)
}
@@ -352,12 +733,12 @@ extension SeismicNetworksViewController: SeismicNetworkTableViewCellDelegate {
}
func seismicNetworkCellDidTapClose(_ cell: SeismicNetworkTableViewCell) {
guard let index = tableView?.indexPath(for: cell) else { return }
guard let index = tableView.indexPath(for: cell) else { return }
let indexToReloads = [openMapIndexPath, index].compactMap { $0 }
openMapIndexPath = nil
tableView?.reloadRows(at: indexToReloads, with: .automatic)
tableView.reloadRows(at: indexToReloads, with: .automatic)
}
}
@@ -375,24 +756,6 @@ extension SeismicNetworksViewController: SeismicFiltersViewControllerDelegate {
}
}
extension SeismicNetworksViewController: SeismicSettingsViewControllerDelegate {
func seismicSettingsControllerDidComplete(_ controller: SeismicSettingsViewController) {
// riscarichiamo i dati, le reti selezionate potrebbero essere cambiate
loadData(forced: true)
}
func seismicSettingsControllerWillOpenProviders(_ controller: SeismicSettingsViewController) {
performSegue(withIdentifier: Self.SegueIdentifierSeismicNetworks, sender: nil)
}
}
extension SeismicNetworksViewController: SeismicSettingsNetworksViewControllerDelegate {
func seismicSettingsNetworksControllerDidComplete(_ controller: SeismicSettingsNetworksViewController) {
// riscarichiamo i dati, le reti selezionate potrebbero essere cambiate
loadData(forced: true)
}
}
extension SeismicNetworksViewController: SeismicCardSettingsViewControllerDelegate {
func seismicCardSettingsDidComplete(_ controller: SeismicCardSettingsViewController) {
refreshUI()
@@ -400,9 +763,12 @@ extension SeismicNetworksViewController: SeismicCardSettingsViewControllerDelega
}
extension SeismicNetworksViewController: DZNEmptyDataSetSource {
func title(forEmptyDataSet scrollView: UIScrollView!) -> NSAttributedString! {
func title(forEmptyDataSet scrollView: UIScrollView) -> NSAttributedString? {
let text = EQNSeismic.shared.filterOption == .positionRelevant
? NSLocalizedString("filter_empty_relevant", comment: "")
: NSLocalizedString("filter_empty_area", comment: "")
let attributes = [ NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .body) ]
let string = NSAttributedString(string: NSLocalizedString("filter_empty", comment: ""), attributes: attributes)
let string = NSAttributedString(string: text, attributes: attributes)
return string
}
}
@@ -1,118 +0,0 @@
//
// SeismicSettingsNetworksViewController.swift
// Earthquake Network
//
// Created by Busi Andrea on 14/09/2020.
// Copyright © 2020 Earthquake Network. All rights reserved.
//
import UIKit
protocol SeismicSettingsNetworksViewControllerDelegate: AnyObject {
func seismicSettingsNetworksControllerDidComplete(_ controller: SeismicSettingsNetworksViewController)
}
class SeismicSettingsNetworksViewController: UITableViewController {
weak var delegate: SeismicSettingsNetworksViewControllerDelegate?
// MARK: - Private
private var networks = [EQNSeismicNetwork]()
private var savedNetworks = [String]()
// MARK: - View Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(SettingDetailTableViewCell.self, forCellReuseIdentifier: SettingDetailTableViewCell.Identifier)
tableView.register(SettingSectionHeaderView.self, forHeaderFooterViewReuseIdentifier: SettingSectionHeaderView.Identifier)
loadData()
}
// MARK: - Private
private func loadData() {
networks = EQNData.seismicNetworks().sorted(by: { $0.acronym < $1.acronym })
// load saved selected networks or fill with all available networks
let savedNetworks = EQNUserData.shared.seismicNetworksSelected()
if !savedNetworks.isEmpty {
self.savedNetworks = savedNetworks
} else {
self.savedNetworks = EQNData.seismicNetworkAcronyms()
}
}
// MARK: - Table view data source
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: SettingSectionHeaderView.Identifier) as! SettingSectionHeaderView
headerView.titleLabel.text = NSLocalizedString("options_agencies", comment: "");
return headerView
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
CGFloat(SettingSectionHeaderView.Height)
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
networks.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let network = networks[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: SettingDetailTableViewCell.Identifier, for: indexPath) as! SettingDetailTableViewCell
cell.textLabel?.text = "\(network.acronym) (\(network.country))"
if savedNetworks.contains(network.acronym) {
cell.accessoryType = .checkmark
} else {
cell.accessoryType = .none
}
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let network = networks[indexPath.row]
if let index = savedNetworks.firstIndex(of: network.acronym) {
savedNetworks.remove(at: index)
} else {
savedNetworks.append(network.acronym)
}
// 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
@IBAction func cancelTapped(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
@IBAction func saveTapped(_ sender: Any) {
// save selected networks
EQNUserData.shared.saveSelectedSeismicNetworks(savedNetworks)
// se solo un'ente è selezionato, salviamolo anche come nazione
if savedNetworks.count == 1 {
UserDefaults.standard.set(savedNetworks.first!, forKey: IMPOSTAZIONE_NAZIONE_RETI_SISMICHE)
} else {
UserDefaults.standard.removeObject(forKey: IMPOSTAZIONE_NAZIONE_RETI_SISMICHE)
}
delegate?.seismicSettingsNetworksControllerDidComplete(self)
dismiss(animated: true, completion: nil)
}
}
@@ -1,130 +0,0 @@
//
// SeismicSettingsViewController.swift
// Earthquake Network
//
// Created by Busi Andrea on 13/09/2020.
// Copyright © 2020 Earthquake Network. All rights reserved.
//
import Foundation
protocol SeismicSettingsViewControllerDelegate: AnyObject {
func seismicSettingsControllerDidComplete(_ controller: SeismicSettingsViewController)
func seismicSettingsControllerWillOpenProviders(_ controller: SeismicSettingsViewController)
}
class SeismicSettingsViewController: UIViewController {
weak var delegate: SeismicSettingsViewControllerDelegate?
// MARK: - Private
@IBOutlet private weak var containerView: UIView!
@IBOutlet private weak var titleLabel: UILabel!
@IBOutlet private weak var countryTextField: UITextField!
@IBOutlet private weak var confirmButton: UIButton!
@IBOutlet private weak var otherwiseLabel: UILabel!
@IBOutlet private weak var manageNetworksButton: UIButton!
@IBOutlet private weak var cancelButton: UIButton!
private let networks = EQNData.seismicNetworks().sorted(by: { $0.country < $1.country })
private let picker = EQNGenericPickerViewController()
private var selectedNetwork: EQNSeismicNetwork?
// MARK: - View Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
}
// MARK: - Private
private func setupUI() {
containerView.layer.cornerRadius = AppTheme.shared.cardCornerRadius
containerView.layer.masksToBounds = true
// localize
titleLabel.text = NSLocalizedString("official_select_country", comment: "")
countryTextField.placeholder = NSLocalizedString("official_select_country_placeholder", comment: "")
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: "status_cancel", uppercased: false)
// load saved country (if exists)
let savedCountry = UserDefaults.standard.object(forKey: IMPOSTAZIONE_NAZIONE_RETI_SISMICHE) as? String
selectedNetwork = EQNData.seismic(for: savedCountry)
countryTextField.text = selectedNetwork?.country
countryTextField.inputView = picker.view
let selectedIndex: Int? = selectedNetwork != nil ? networks.firstIndex(of: selectedNetwork!) : nil
picker.configure(with: networks, selectedIndex: selectedIndex) { [unowned self] (network) in
guard let network = network as? EQNSeismicNetwork else { return }
self.view.endEditing(true)
self.selectedNetwork = network
self.countryTextField.text = self.selectedNetwork?.country
}
picker.onCancel = { [unowned self] in
self.view.endEditing(true)
}
}
private func performSave(for network: EQNSeismicNetwork) {
// salviamo la sigla dell'ente selezionato
UserDefaults.standard.set(network.acronym, forKey: IMPOSTAZIONE_NAZIONE_RETI_SISMICHE)
// gli enti selezionati conterranno solo l'ente della nazione selezionata
let selectedNetworks = [network.acronym]
EQNUserData.shared.saveSelectedSeismicNetworks(selectedNetworks)
// aggiorniamo le impostazioni di notifica
EQNNotificheReteSismiche.shared().listaEnti = selectedNetworks
EQNNotificheReteSismiche.shared().saveUserInfo()
SettingsBaseViewController.saveSettings()
// informiamo il delegato
delegate?.seismicSettingsControllerDidComplete(self)
dismiss(animated: true, completion: nil)
}
// MARK: - Actions
@IBAction func confirmCountryTapped(_ sender: UIButton) {
guard let network = selectedNetwork else {
let alert = UIAlertController(title: NSLocalizedString("attention", comment: ""),
message: NSLocalizedString("official_no_country_selected", comment: ""),
preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("ok", comment: ""), style: .cancel, handler: { [unowned self] (action) in
self.countryTextField.becomeFirstResponder()
}))
present(alert, animated: true, completion: nil)
return
}
// ask confirm to change settings for notifications
let alert = UIAlertController(title: NSLocalizedString("attention", comment: ""),
message: NSLocalizedString("official_select_message", comment: ""),
preferredStyle: .alert)
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)
}))
present(alert, animated: true, completion: nil)
}
@IBAction func selectNetworksTapped(_ sender: UIButton) {
delegate?.seismicSettingsControllerWillOpenProviders(self)
dismiss(animated: true, completion: nil)
}
@IBAction func cancelTapped(_ sender: UIButton) {
dismiss(animated: true, completion: nil)
}
}
@@ -11,27 +11,27 @@ import Foundation
class SettingDateTableViewCell: UITableViewCell {
@objc static let Identifier = "DateCell"
static let Identifier = "DateCell"
@objc var isDisabled: Bool = false {
var isDisabled: Bool = false {
didSet {
updateUI()
}
}
@objc var isPickerVisible: Bool = false {
var isPickerVisible: Bool = false {
didSet {
if oldValue != isPickerVisible {
updateUI()
}
}
}
@objc private(set) var date = Date()
@objc var valueChanged: ((Date) -> Void)?
private(set) var date = Date()
var valueChanged: ((Date) -> Void)?
// MARK: - Properties
@objc lazy var titleLabel: UILabel = {
lazy var titleLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
@@ -40,7 +40,7 @@ class SettingDateTableViewCell: UITableViewCell {
return label
}()
@objc lazy var valuesLabel: UILabel = {
lazy var valuesLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
@@ -58,7 +58,7 @@ class SettingDateTableViewCell: UITableViewCell {
return picker
}()
@objc lazy var stackView: UIStackView = {
lazy var stackView: UIStackView = {
let stackView = UIStackView()
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .vertical
@@ -81,7 +81,7 @@ class SettingDateTableViewCell: UITableViewCell {
// MARK: - Public
@objc public func updateDate(_ date: Date) {
public func updateDate(_ date: Date) {
self.date = date
datePicker.setDate(date, animated: true)
}
@@ -10,17 +10,16 @@ import UIKit
class SettingDetailTableViewCell: UITableViewCell {
@objc static let Identifier = "DetailCell"
static let Identifier = "DetailCell"
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
@@ -10,10 +10,10 @@ import UIKit
class SettingEnableTableViewCell: UITableViewCell {
@objc static let Identifier = "EnableCell"
static let Identifier = "EnableCell"
@objc var valueChanged: ((Bool) -> Void)?
@objc var isDisabled: Bool = false {
var valueChanged: ((Bool) -> Void)?
var isDisabled: Bool = false {
didSet {
updateUI()
}
@@ -21,7 +21,7 @@ class SettingEnableTableViewCell: UITableViewCell {
// MARK: - Properties
@objc lazy var titleLabel: UILabel = {
lazy var titleLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
@@ -29,7 +29,7 @@ class SettingEnableTableViewCell: UITableViewCell {
return label
}()
@objc lazy var descriptionLabel: UILabel = {
lazy var descriptionLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
@@ -37,7 +37,7 @@ class SettingEnableTableViewCell: UITableViewCell {
return label
}()
@objc lazy var toggleSwitch: UISwitch = {
lazy var toggleSwitch: UISwitch = {
let toggle = UISwitch()
toggle.setContentHuggingPriority(.required, for: .horizontal)
toggle.setContentCompressionResistancePriority(.required, for: .horizontal)
@@ -45,6 +45,15 @@ class SettingEnableTableViewCell: UITableViewCell {
return toggle
}()
lazy var errorLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.font = UIFont.preferredFont(forTextStyle: .subheadline)
label.textColor = AppTheme.Colors.red
label.text = nil
return label
}()
// MARK: - Init
@@ -75,6 +84,7 @@ class SettingEnableTableViewCell: UITableViewCell {
contentView.addSubview(stackView)
contentView.addSubview(descriptionLabel)
contentView.addSubview(errorLabel)
stackView.topAnchor.constraint(equalTo: contentView.layoutMarginsGuide.topAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.trailingAnchor).isActive = true
@@ -83,7 +93,12 @@ class SettingEnableTableViewCell: UITableViewCell {
descriptionLabel.topAnchor.constraint(equalTo: stackView.bottomAnchor, constant: 8).isActive = true
descriptionLabel.trailingAnchor.constraint(equalTo: stackView.trailingAnchor).isActive = true
descriptionLabel.leadingAnchor.constraint(equalTo: stackView.leadingAnchor).isActive = true
descriptionLabel.bottomAnchor.constraint(equalTo: contentView.layoutMarginsGuide.bottomAnchor).isActive = true
//descriptionLabel.bottomAnchor.constraint(equalTo: contentView.layoutMarginsGuide.bottomAnchor).isActive = true
errorLabel.topAnchor.constraint(equalTo: descriptionLabel.bottomAnchor, constant: 8.0).isActive = true
errorLabel.trailingAnchor.constraint(equalTo: descriptionLabel.trailingAnchor).isActive = true
errorLabel.leadingAnchor.constraint(equalTo: descriptionLabel.leadingAnchor).isActive = true
errorLabel.bottomAnchor.constraint(equalTo: contentView.layoutMarginsGuide.bottomAnchor).isActive = true
}
private func updateUI() {
@@ -11,9 +11,9 @@ import Foundation
class SettingMultivaluesTableViewCell: UITableViewCell {
@objc static let Identifier = "MultivaluesCell"
static let Identifier = "MultivaluesCell"
@objc var isDisabled: Bool = false {
var isDisabled: Bool = false {
didSet {
updateUI()
}
@@ -21,7 +21,7 @@ class SettingMultivaluesTableViewCell: UITableViewCell {
// MARK: - Properties
@objc lazy var titleLabel: UILabel = {
lazy var titleLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
@@ -30,7 +30,7 @@ class SettingMultivaluesTableViewCell: UITableViewCell {
return label
}()
@objc lazy var valuesLabel: UILabel = {
lazy var valuesLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
@@ -10,18 +10,17 @@ import UIKit
class SettingSectionHeaderView: UITableViewHeaderFooterView {
@objc static let Identifier = "SectionHeaderView"
@objc static let Height = 50.0
static let Identifier = "SectionHeaderView"
static let Height = 50.0
// MARK: - Properties
@objc lazy var titleLabel: UILabel = {
lazy var titleLabel: UILabel = {
let titleLabel = UILabel()
titleLabel.font = UIFont.preferredFont(forTextStyle: .headline)
titleLabel.textColor = AppTheme.Colors.lightBlue
return titleLabel
}()
// MARK: - Init
@@ -34,7 +33,6 @@ class SettingSectionHeaderView: UITableViewHeaderFooterView {
super.init(coder: coder)
setupUI()
}
// MARK: - Private
@@ -45,6 +43,5 @@ class SettingSectionHeaderView: UITableViewHeaderFooterView {
titleLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
titleLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
titleLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
}
}
@@ -11,19 +11,19 @@ import Foundation
class SettingSegmentedTableViewCell: UITableViewCell {
@objc static let Identifier = "SegmentedCell"
static let Identifier = "SegmentedCell"
@objc var isDisabled: Bool = false {
var isDisabled: Bool = false {
didSet {
updateUI()
}
}
@objc var valueChanged: ((EQNGenericValue) -> Void)?
var valueChanged: ((EQNGenericValue) -> Void)?
private var items = [EQNGenericValue]()
// MARK: - Properties
@objc lazy var titleLabel: UILabel = {
lazy var titleLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
@@ -32,7 +32,7 @@ class SettingSegmentedTableViewCell: UITableViewCell {
return label
}()
@objc lazy var segmentedControl: UISegmentedControl = {
lazy var segmentedControl: UISegmentedControl = {
let segmented = UISegmentedControl()
segmented.translatesAutoresizingMaskIntoConstraints = false
segmented.addTarget(self, action: #selector(segmentedControlChanged(_:)), for: .valueChanged)
@@ -10,21 +10,21 @@ import UIKit
class SettingSliderTableViewCell: UITableViewCell {
@objc static let Identifier = "SliderCell"
static let Identifier = "SliderCell"
@objc var isDisabled: Bool = false {
var isDisabled: Bool = false {
didSet {
updateUI()
}
}
@objc var valueChanged: ((EQNGenericValue) -> Void)?
@objc var dragEnded: (() -> Void)?
var valueChanged: ((EQNGenericValue) -> Void)?
var dragEnded: (() -> Void)?
private var items = [EQNGenericValue]()
// MARK: - Properties
@objc lazy var titleLabel: UILabel = {
lazy var titleLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
@@ -33,7 +33,7 @@ class SettingSliderTableViewCell: UITableViewCell {
return label
}()
@objc lazy var valueLabel: UILabel = {
lazy var valueLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
@@ -42,7 +42,7 @@ class SettingSliderTableViewCell: UITableViewCell {
return label
}()
@objc lazy var slider: UISlider = {
lazy var slider: UISlider = {
let slider = UISlider()
slider.isContinuous = true
slider.addTarget(self, action: #selector(sliderChanged(_:)), for: .valueChanged)
@@ -0,0 +1,43 @@
//
// SettingsBaseTableViewController.swift
// Earthquake Network
//
// Created by Andrea Busi on 10/06/24.
// Copyright © 2024 Earthquake Network. All rights reserved.
//
import UIKit
@objc
class SettingsBaseTableViewController: UITableViewController {
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if isMovingFromParent {
Self.saveSettings()
}
}
// MARK: - Class
@objc class func saveSettings() {
saveSettings { _ in
// nope
}
}
@objc class func saveSettings(
completion: @escaping (_ success: Bool) -> Void
) {
let url = EQNGeneratoreURLServer.urlInvioImpostazioniNotifiche()
ServerRequest.default().inviaInformazioniAlServer(with: url, richiesta: .impostazioniNotifiche) { _ in
print("[SETTINGS] Settings saved successfully")
completion(true)
} failure: { error in
print("[SETTINGS] Settings saved failed. Error: \(error?.localizedDescription ?? "n.d.")")
completion(false)
}
}
}
@@ -1,19 +0,0 @@
//
// SettingsBaseViewController.h
// Earthquake Network
//
// Created by Busi Andrea on 30/08/2020.
// Copyright © 2020 Earthquake Network. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface SettingsBaseViewController : UITableViewController
+ (void)saveSettings;
@end
NS_ASSUME_NONNULL_END
@@ -1,42 +0,0 @@
//
// SettingsBaseViewController.m
// Earthquake Network
//
// Created by Busi Andrea on 30/08/2020.
// Copyright © 2020 Earthquake Network. All rights reserved.
//
#import "SettingsBaseViewController.h"
#import "ServerRequest.h"
#import "EQNGeneratoreURLServer.h"
@interface SettingsBaseViewController ()
@end
@implementation SettingsBaseViewController
#pragma mark - View Lifecycle
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
// when controller is dismissed, save settings
if (self.isMovingFromParentViewController) {
[SettingsBaseViewController saveSettings];
}
}
#pragma mark - Private
+ (void)saveSettings
{
[[ServerRequest defaultServerConnectionSingleton] inviaInformazioniAlServerWithURL:[EQNGeneratoreURLServer urlInvioImpostazioniNotifiche] richiesta:EQNTipoChiamataImpostazioniNotifiche success:^(id result){
NSLog(@"Settings saved successfully");
} failure:^(NSError *error){
NSLog(@"Settings saved failed. Error: %@", error.localizedDescription);
}];
}
@end
@@ -1,18 +0,0 @@
//
// AletaSismiTableViewController.h
// Earthquake Network
//
// Refactored by Andrea Busi 25/08/2020.
// Copyright © 2020 Earthquake Network. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "SettingsBaseViewController.h"
NS_ASSUME_NONNULL_BEGIN
@interface SettingsRealTimeAlertsViewController : SettingsBaseViewController
@end
NS_ASSUME_NONNULL_END
@@ -1,165 +0,0 @@
//
// AletaSismiTableViewController.m
// Earthquake Network
//
// Refactored by Andrea Busi 25/08/2020.
// Copyright © 2020 Earthquake Network. All rights reserved.
//
#import "SettingsRealTimeAlertsViewController.h"
#import "EQNAllertaSismica.h"
@import UserNotifications;
@interface SettingsRealTimeAlertsViewController () <UITextFieldDelegate>
@property (nonatomic, strong) NSArray<SettingItem *> *settings;
@property (nonatomic, strong) NSDateFormatter *dateFormatter;
@property (nonatomic, assign) BOOL notificationEnabled;
@property (nonatomic, assign) BOOL criticalAlertsEnabled;
@end
@implementation SettingsRealTimeAlertsViewController
typedef NS_ENUM(NSInteger, RowIdentifier) {
RowIdentifierAbilitaNotifiche = 0,
RowIdentifierAbilitaCriticalAlerts
};
#pragma mark - Accessories
- (NSDateFormatter *)dateFormatter
{
if (!_dateFormatter) {
_dateFormatter = [[NSDateFormatter alloc] init];
[_dateFormatter setDateFormat:@"HH:mm"];
}
return _dateFormatter;
}
#pragma mark - View Lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
[self setupUI];
self.settings = @[
[[SettingItem alloc] initWithType:SettingTypeEnable title:NSLocalizedString(@"options_notification_enable_alarm", @"")],
[[SettingItem alloc] initWithType:SettingTypeEnable title:NSLocalizedString(@"critical_alerts_setting", @"")]
];
[self loadDataSource];
[self.tableView reloadData];
}
#pragma mark - Private
- (void)setupUI
{
self.navigationItem.largeTitleDisplayMode = UINavigationItemLargeTitleDisplayModeNever;
self.tableView.estimatedRowHeight = 200.0;
self.tableView.rowHeight = UITableViewAutomaticDimension;
[self.tableView registerClass:[SettingSectionHeaderView class] forHeaderFooterViewReuseIdentifier:SettingSectionHeaderView.Identifier];
[self.tableView registerClass:[SettingEnableTableViewCell class] forCellReuseIdentifier:SettingEnableTableViewCell.Identifier];
[self.tableView registerClass:[SettingSliderTableViewCell class] forCellReuseIdentifier:SettingSliderTableViewCell.Identifier];
[self.tableView registerClass:[SettingMultivaluesTableViewCell class] forCellReuseIdentifier:SettingMultivaluesTableViewCell.Identifier];
[self.tableView registerClass:[SettingSegmentedTableViewCell class] forCellReuseIdentifier:SettingSegmentedTableViewCell.Identifier];
[self.tableView registerClass:[SettingDateTableViewCell class] forCellReuseIdentifier:SettingDateTableViewCell.Identifier];
}
- (void)loadDataSource
{
self.notificationEnabled = [EQNAllertaSismica sharedInstance].isAbilitato;
self.criticalAlertsEnabled = [EQNAllertaSismica sharedInstance].isCriticalAlertsEnabled;
[[EQNAllertaSismica sharedInstance] saveUserInfo];
}
#pragma mark - Table view data source
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.settings.count;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
SettingSectionHeaderView *headerView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:SettingSectionHeaderView.Identifier];
headerView.titleLabel.text = NSLocalizedString(@"options_alarms", @"");
return headerView;
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return SettingSectionHeaderView.Height;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
SettingItem *setting = self.settings[indexPath.row];
if (setting.type == SettingTypeEnable) {
SettingEnableTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SettingEnableTableViewCell.Identifier forIndexPath:indexPath];
cell.titleLabel.text = setting.displayTitle;
cell.descriptionLabel.text = setting.subtitle;
if (indexPath.row == RowIdentifierAbilitaNotifiche) {
cell.toggleSwitch.on = self.notificationEnabled;
cell.valueChanged = ^(BOOL enabled) {
self.notificationEnabled = enabled;
[EQNAllertaSismica sharedInstance].isAbilitato = self.notificationEnabled;
[[EQNAllertaSismica sharedInstance] saveUserInfo];
[self.tableView reloadData];
};
} else if (indexPath.row == RowIdentifierAbilitaCriticalAlerts) {
cell.toggleSwitch.on = self.criticalAlertsEnabled;
cell.valueChanged = ^(BOOL enabled) {
if (enabled) {
[self askForCriticalAlertsPermission];
}
self.criticalAlertsEnabled = enabled;
[EQNAllertaSismica sharedInstance].isCriticalAlertsEnabled = self.criticalAlertsEnabled;
[[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;
return cell;
} else if (setting.type == SettingTypeSlider) {
SettingSliderTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SettingSliderTableViewCell.Identifier forIndexPath:indexPath];
cell.titleLabel.text = setting.displayTitle;
return cell;
}
return nil;
}
#pragma mark - Private
- (void)updateSismicToNotify:(EQNGenericValue *)seismic
{
[EQNAllertaSismica sharedInstance].sismiDaNotificare = seismic.value;
[[EQNAllertaSismica sharedInstance] saveUserInfo];
[self loadDataSource];
}
- (void)askForCriticalAlertsPermission
{
UNAuthorizationOptions authOptions = UNAuthorizationOptionCriticalAlert;
[[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions:authOptions completionHandler:^(BOOL granted, NSError *error) {
// nope
}];
}
@end
@@ -0,0 +1,136 @@
//
// SettingsRealTimeAlertsViewController.swift
// Earthquake Network
//
// Created by Andrea Busi on 10/06/24.
// Copyright © 2024 Earthquake Network. All rights reserved.
//
import UIKit
import Shogun
class SettingsRealTimeAlertsViewController: SettingsBaseTableViewController {
private enum RowIdentifier: Int {
case abilitaNotifiche
case abilitaCriticalAlerts
}
private var isNotificationEnabled = false
private var isCriticalAlertsEnabled = false
private let settings: [SettingItem] = [
.init(type: .enable, title: NSLocalizedString("options_notification_enable_alarm", comment: "")),
.init(type: .enable, title: NSLocalizedString("critical_alerts_setting", comment: ""))
]
// MARK: - View Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
tableView.reloadData()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
loadDataSource()
tableView.reloadData()
}
// MARK: - Private
private func setupUI() {
navigationItem.largeTitleDisplayMode = .never
tableView.estimatedRowHeight = 200.0
tableView.rowHeight = UITableView.automaticDimension
tableView.registerHeaderFooterView(for: SettingSectionHeaderView.self)
tableView.registerCell(for: SettingEnableTableViewCell.self)
}
private func loadDataSource() {
let saved = EQNSettingRealTimeAlert.shared
isNotificationEnabled = saved.isAbilitato
isCriticalAlertsEnabled = saved.isCriticalAlertsEnabled
}
// MARK: - Table view delegate and data source
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let view = tableView.dequeueHeaderFooterView(cellIdentifiable: SettingSectionHeaderView.self)
view.titleLabel.text = NSLocalizedString("options_alarms", comment: "")
return view
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
SettingSectionHeaderView.Height
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return settings.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let identifier = RowIdentifier(rawValue: indexPath.row) else {
return UITableViewCell()
}
let setting = settings[indexPath.row]
switch setting.type {
case .enable:
let cell = tableView.dequeueReusableCell(cellIdentifiable: SettingEnableTableViewCell.self, for: indexPath)
cell.titleLabel.text = setting.displayTitle
cell.descriptionLabel.text = setting.subtitle
switch identifier {
case .abilitaNotifiche:
cell.toggleSwitch.isOn = isNotificationEnabled
cell.valueChanged = { [weak self] enabled in
self?.onChangeNotificationEnabled(enabled)
}
case .abilitaCriticalAlerts:
cell.toggleSwitch.isOn = isCriticalAlertsEnabled
cell.valueChanged = { [weak self] enabled in
self?.onChangeCriticalAlertsEnabled(enabled)
}
}
return cell
default:
fatalError()
}
}
// MARK: - Private
private func onChangeNotificationEnabled(_ enabled: Bool) {
isNotificationEnabled = enabled
EQNSettingRealTimeAlert.shared.isAbilitato = isNotificationEnabled
EQNSettingRealTimeAlert.shared.saveUserInfo()
tableView.reloadData()
}
private func onChangeCriticalAlertsEnabled(_ enabled: Bool) {
if enabled {
askForCriticalAlertsPermission()
}
isCriticalAlertsEnabled = enabled
EQNSettingRealTimeAlert.shared.isCriticalAlertsEnabled = isCriticalAlertsEnabled
EQNSettingRealTimeAlert.shared.saveUserInfo()
tableView.reloadData()
}
private func askForCriticalAlertsPermission() {
UNUserNotificationCenter.current().requestAuthorization(options: [ .criticalAlert ]) { granted, error in
// nope
}
}
}
@@ -1,18 +0,0 @@
//
// SettingsSeismicNetworkAlertsViewController.h
// Earthquake Network
//
// Refactored by Andrea Busi 25/08/2020.
// Copyright © 2020 Earthquake Network. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "SettingsBaseViewController.h"
NS_ASSUME_NONNULL_BEGIN
@interface SettingsSeismicNetworkAlertsViewController : SettingsBaseViewController
@end
NS_ASSUME_NONNULL_END
@@ -1,258 +0,0 @@
//
// SettingsSeismicNetworkAlertsViewController.m
// Earthquake Network
//
// Refactored by Andrea Busi 25/08/2020.
// Copyright © 2020 Earthquake Network. All rights reserved.
//
#import "SettingsSeismicNetworkAlertsViewController.h"
#import "EQNNotificheReteSismiche.h"
@interface SettingsSeismicNetworkAlertsViewController ()
@property (nonatomic, strong) NSArray<SettingItem *> *settings;
@property (nonatomic, strong) NSArray<EQNGenericValue *> *dataSourceRaggioSisma;
@property (nonatomic, strong) NSArray<EQNGenericValue *> *dataSourceMagnitudoDeboli;
@property (nonatomic, strong) NSArray<EQNGenericValue *> *dataSourceMagnitudoForti;
@property (nonatomic, assign) BOOL notificationEnabled;
@property (nonatomic, assign) BOOL notificationNearEarthquakeEnabled;
@property (nonatomic, assign) BOOL notificationStrongEarthquakeEnabled;
@property (nonatomic, strong) EQNGenericValue *currentUserPositionRadius;
@property (nonatomic, strong) EQNGenericValue *currentSeismicEnergy;
@property (nonatomic, strong) EQNGenericValue *currentStrongEarthquakeDistance;
@end
@implementation SettingsSeismicNetworkAlertsViewController
static NSString * const SegueIdentifierListaEnti = @"ShowListaEnti";
typedef NS_ENUM(NSInteger, RowIdentifier) {
RowIdentifierAbilitaNotifiche = 0,
RowIdentifierRetiSismiche,
RowIdentifierRaggioPosizione,
RowIdentifierEnergiaSisma,
RowIdentifierTerremotiVicini,
RowIdentifierTerremotiForti,
RowIdentifierTerremotiFortiDistanza
};
#pragma mark - View Lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
[self setupUI];
self.settings = @[
[[SettingItem alloc] initWithType:SettingTypeEnable title:NSLocalizedString(@"options_notification_enable_official", @"")],
[[SettingItem alloc] initWithType:SettingTypeMultiValues title:NSLocalizedString(@"options_agencies", @"") segue:SegueIdentifierListaEnti],
[[SettingItem alloc] initWithType:SettingTypeSlider title:NSLocalizedString(@"options_radius", @"")],
[[SettingItem alloc] initWithType:SettingTypeSlider title:NSLocalizedString(@"options_energy", @"")],
[[SettingItem alloc] initWithType:SettingTypeEnable title:NSLocalizedString(@"options_near", @"") subtitle:NSLocalizedString(@"options_near_alert", @"")],
[[SettingItem alloc] initWithType:SettingTypeEnable title:NSLocalizedString(@"options_strong", @"") subtitle:NSLocalizedString(@"options_strong_alert", @"")],
[[SettingItem alloc] initWithType:SettingTypeSlider title:NSLocalizedString(@"options_strong_magnitude", @"")]
];
self.dataSourceMagnitudoDeboli = [EQNData magitudoDeboli];
self.dataSourceRaggioSisma = [EQNData raggioSismi];
self.dataSourceMagnitudoForti = [EQNData magitudoForti];
[self loadDataSource];
[self.tableView reloadData];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self loadDataSource];
[self.tableView reloadData];
}
#pragma mark - Private
- (void)setupUI
{
self.navigationItem.largeTitleDisplayMode = UINavigationItemLargeTitleDisplayModeNever;
self.tableView.estimatedRowHeight = 200.0;
self.tableView.rowHeight = UITableViewAutomaticDimension;
[self.tableView registerClass:[SettingSectionHeaderView class] forHeaderFooterViewReuseIdentifier:SettingSectionHeaderView.Identifier];
[self.tableView registerClass:[SettingEnableTableViewCell class] forCellReuseIdentifier:SettingEnableTableViewCell.Identifier];
[self.tableView registerClass:[SettingSliderTableViewCell class] forCellReuseIdentifier:SettingSliderTableViewCell.Identifier];
[self.tableView registerClass:[SettingMultivaluesTableViewCell class] forCellReuseIdentifier:SettingMultivaluesTableViewCell.Identifier];
}
- (void)loadDataSource
{
self.notificationEnabled = [EQNNotificheReteSismiche sharedInstance].isAbilitato;
self.notificationNearEarthquakeEnabled = [EQNNotificheReteSismiche sharedInstance].isAbilitaVicini;
self.notificationStrongEarthquakeEnabled = [EQNNotificheReteSismiche sharedInstance].isTerremortiForti;
// raggio dalla tua posizione
EQNGenericValue *raggioSisma = [EQNData raggioSismaFor:[EQNNotificheReteSismiche sharedInstance].distanzaPosizione];
self.currentUserPositionRadius = raggioSisma;
// energia sisma
EQNGenericValue *energiaSisma = [EQNData magitudoDeboleFor:[EQNNotificheReteSismiche sharedInstance].energiaSisma];
self.currentSeismicEnergy = energiaSisma;
// terremoti forti
EQNGenericValue *terremotiForti = [EQNData magitudoForteFor:[EQNNotificheReteSismiche sharedInstance].energiaTerremotiForti];
self.currentStrongEarthquakeDistance = terremotiForti;
// enti
if (![EQNNotificheReteSismiche sharedInstance].listaEnti) {
[EQNNotificheReteSismiche sharedInstance].listaEnti = [EQNData.seismicNetworkAcronyms copy];
}
[[EQNNotificheReteSismiche sharedInstance] saveUserInfo];
}
#pragma mark - Table view data source
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
SettingSectionHeaderView *headerView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:SettingSectionHeaderView.Identifier];
headerView.titleLabel.text = NSLocalizedString(@"options_notification_official", @"titolo impostazioni notifiche");
return headerView;
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return SettingSectionHeaderView.Height;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.settings.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
SettingItem *setting = self.settings[indexPath.row];
if (setting.type == SettingTypeEnable) {
SettingEnableTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SettingEnableTableViewCell.Identifier forIndexPath:indexPath];
cell.titleLabel.text = setting.displayTitle;
cell.descriptionLabel.text = setting.subtitle;
if (indexPath.row == RowIdentifierAbilitaNotifiche) {
cell.toggleSwitch.on = self.notificationEnabled;
cell.valueChanged = ^(BOOL enabled) {
self.notificationEnabled = enabled;
[EQNNotificheReteSismiche sharedInstance].isAbilitato = self.notificationEnabled;
[[EQNNotificheReteSismiche sharedInstance] saveUserInfo];
[self.tableView reloadData];
};
} else if (indexPath.row == RowIdentifierTerremotiVicini) {
cell.toggleSwitch.on = self.notificationNearEarthquakeEnabled;
cell.isDisabled = !self.notificationEnabled;
cell.valueChanged = ^(BOOL enabled) {
self.notificationNearEarthquakeEnabled = enabled;
[EQNNotificheReteSismiche sharedInstance].isAbilitaVicini = self.notificationNearEarthquakeEnabled;
[[EQNNotificheReteSismiche sharedInstance] saveUserInfo];
[self.tableView reloadData];
};
} else if (indexPath.row == RowIdentifierTerremotiForti) {
cell.toggleSwitch.on = self.notificationStrongEarthquakeEnabled;
cell.isDisabled = !self.notificationEnabled;
cell.valueChanged = ^(BOOL enabled) {
self.notificationStrongEarthquakeEnabled = enabled;
[EQNNotificheReteSismiche sharedInstance].isTerremortiForti = self.notificationStrongEarthquakeEnabled;
[[EQNNotificheReteSismiche sharedInstance] saveUserInfo];
[self.tableView reloadData];
};
}
return cell;
} else if (setting.type == SettingTypeMultiValues) {
SettingMultivaluesTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SettingMultivaluesTableViewCell.Identifier forIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.isDisabled = !self.notificationEnabled;
cell.userInteractionEnabled = self.notificationEnabled;
cell.titleLabel.text = setting.title;
if (indexPath.row == RowIdentifierRetiSismiche) {
cell.valuesLabel.text = [self stringOfSelectedNetworks];
}
return cell;
} else if (setting.type == SettingTypeSlider) {
SettingSliderTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SettingSliderTableViewCell.Identifier forIndexPath:indexPath];
cell.titleLabel.text = setting.displayTitle;
if (indexPath.row == RowIdentifierRaggioPosizione) {
cell.isDisabled = !self.notificationEnabled;
[cell configureSliderWith:self.dataSourceRaggioSisma current:self.currentUserPositionRadius];
cell.valueChanged = ^(EQNGenericValue *item) {
[self updateUserPositionRadius:item];
};
} else if (indexPath.row == RowIdentifierEnergiaSisma) {
cell.isDisabled = !self.notificationEnabled;
[cell configureSliderWith:self.dataSourceMagnitudoDeboli current:self.currentSeismicEnergy];
cell.valueChanged = ^(EQNGenericValue *item) {
[self updateSeismicEnergy:item];
};
} else if (indexPath.row == RowIdentifierTerremotiFortiDistanza) {
cell.isDisabled = !self.notificationEnabled || !self.notificationStrongEarthquakeEnabled;
[cell configureSliderWith:self.dataSourceMagnitudoForti current:self.currentStrongEarthquakeDistance];
cell.valueChanged = ^(EQNGenericValue *item) {
[self updateStrongEarthquakeEnergy:item];
};
}
return cell;
}
return nil;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
SettingItem *setting = self.settings[indexPath.row];
if (setting.segue != nil) {
[self performSegueWithIdentifier:setting.segue sender:nil];
}
}
#pragma mark - Private
- (void)updateUserPositionRadius:(EQNGenericValue *)radius
{
[EQNNotificheReteSismiche sharedInstance].distanzaPosizione = radius.value;
[[EQNNotificheReteSismiche sharedInstance] saveUserInfo];
[self loadDataSource];
}
- (void)updateSeismicEnergy:(EQNGenericValue *)energy
{
[EQNNotificheReteSismiche sharedInstance].energiaSisma = energy.value;
[[EQNNotificheReteSismiche sharedInstance] saveUserInfo];
[self loadDataSource];
}
- (void)updateStrongEarthquakeEnergy:(EQNGenericValue *)energy
{
[EQNNotificheReteSismiche sharedInstance].energiaTerremotiForti = energy.value;
[[EQNNotificheReteSismiche sharedInstance] saveUserInfo];
[self loadDataSource];
}
- (NSString *)stringOfSelectedNetworks
{
NSArray *networks = [EQNNotificheReteSismiche sharedInstance].listaEnti;
networks = [networks sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
return [networks componentsJoinedByString:@", "];
}
@end
@@ -0,0 +1,166 @@
//
// SettingsSeismicNetworkNotificationsViewController.swift
// Earthquake Network
//
// Created by Andrea Busi on 06/06/24.
// Copyright © 2024 Earthquake Network. All rights reserved.
//
import UIKit
import Shogun
class SettingsSeismicNetworkNotificationsViewController: SettingsBaseTableViewController {
private enum RowIdentifier: Int {
case abilitaNotifiche
case magnitudoMinima
case distanzaMassima
}
private var isNotificationEnabled = false
private var currentMinimumMagnitude = EQNData.DefaultSettingSeismicNetworkNotificationMagitude
private var currentMaximumDistance = EQNData.DefaultSettingSeismicNetworkNotificationRadius
private let dataSourceMinimumMagnitude = EQNData.settingSeismicNetworkNotificationMagnitudes
private let dataSourceMaximumDistance = EQNData.settingSeismicNetworkNotificationRadius
private let settings: [SettingItem] = [
.init(type: .enable, title: NSLocalizedString("options_notification_enable_official", comment: "")),
.init(type: .slider, title: NSLocalizedString("options_official_minmag", comment: "")),
.init(type: .slider, title: NSLocalizedString("options_official_maxdist", comment: ""))
]
// MARK: - View Liefcycle
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
tableView.reloadData()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
loadDataSource()
tableView.reloadData()
}
// MARK: - Private
private func setupUI() {
navigationItem.largeTitleDisplayMode = .never
tableView.estimatedRowHeight = 200.0
tableView.rowHeight = UITableView.automaticDimension
tableView.registerHeaderFooterView(for: SettingSectionHeaderView.self)
tableView.registerCell(for: SettingEnableTableViewCell.self)
tableView.registerCell(for: SettingSliderTableViewCell.self)
tableView.registerCell(for: SettingMultivaluesTableViewCell.self)
}
private func loadDataSource() {
let saved = EQNSettingSeismicNetworkNotification.shared
isNotificationEnabled = saved.isAbilitato
// magnitudo minima
let magnitudoMinima = EQNData.getSettingSeismicNetworkNotificationMagnitudes(for: saved.magnitudoMinima)
currentMinimumMagnitude = magnitudoMinima
// raggio dalla tua posizione
let distanzaMassima = EQNData.getSettingSeismicNetworkNotificationRadius(for: saved.distanzaMassima)
currentMaximumDistance = distanzaMassima
}
// MARK: - Table view data source
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let view = tableView.dequeueHeaderFooterView(cellIdentifiable: SettingSectionHeaderView.self)
view.titleLabel.text = NSLocalizedString("options_notification_official", comment: "")
return view
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
SettingSectionHeaderView.Height
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return settings.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let identifier = RowIdentifier(rawValue: indexPath.row) else {
return UITableViewCell()
}
let setting = settings[indexPath.row]
switch setting.type {
case .enable:
let cell = tableView.dequeueReusableCell(cellIdentifiable: SettingEnableTableViewCell.self, for: indexPath)
cell.titleLabel.text = setting.displayTitle
cell.descriptionLabel.text = setting.subtitle
switch identifier {
case .abilitaNotifiche:
cell.toggleSwitch.isOn = isNotificationEnabled
cell.valueChanged = { [weak self] enabled in
self?.onChangeNotificationEnabled(enabled)
}
default:
break
}
return cell
case .slider:
let cell = tableView.dequeueReusableCell(cellIdentifiable: SettingSliderTableViewCell.self, for: indexPath)
cell.titleLabel.text = setting.displayTitle
let filtersEnabled = isNotificationEnabled
switch identifier {
case .magnitudoMinima:
cell.isDisabled = !filtersEnabled
cell.configureSlider(with: dataSourceMinimumMagnitude, current: currentMinimumMagnitude)
cell.valueChanged = { [weak self] item in
self?.onChangeMinimumMagnitude(item)
}
case .distanzaMassima:
cell.isDisabled = !filtersEnabled
cell.configureSlider(with: dataSourceMaximumDistance, current: currentMaximumDistance)
cell.valueChanged = { [weak self] item in
self?.onChangeMaximumDistance(item)
}
default:
break
}
return cell
default:
fatalError()
}
}
private func onChangeNotificationEnabled(_ enabled: Bool) {
isNotificationEnabled = enabled
EQNSettingSeismicNetworkNotification.shared.isAbilitato = isNotificationEnabled
EQNSettingSeismicNetworkNotification.shared.saveUserInfo()
tableView.reloadData()
}
private func onChangeMinimumMagnitude(_ item: EQNGenericValue) {
EQNSettingSeismicNetworkNotification.shared.magnitudoMinima = item.value
EQNSettingSeismicNetworkNotification.shared.saveUserInfo()
loadDataSource()
}
private func onChangeMaximumDistance(_ item: EQNGenericValue) {
EQNSettingSeismicNetworkNotification.shared.distanzaMassima = item.value
EQNSettingSeismicNetworkNotification.shared.saveUserInfo()
loadDataSource()
}
}
@@ -1,71 +0,0 @@
//
// SettingsSeismicNetworksViewController.swift
// Earthquake Network
//
// Created by Busi Andrea on 26/08/2020.
// Copyright © 2020 Earthquake Network. All rights reserved.
//
import UIKit
class SettingsSeismicNetworksViewController: UITableViewController {
var networks = [EQNSeismicNetwork]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(SettingDetailTableViewCell.self, forCellReuseIdentifier: SettingDetailTableViewCell.Identifier)
tableView.register(SettingSectionHeaderView.self, forHeaderFooterViewReuseIdentifier: SettingSectionHeaderView.Identifier)
networks = EQNData.seismicNetworks().sorted(by: { $0.acronym < $1.acronym })
}
// MARK: - Table view data source
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: SettingSectionHeaderView.Identifier) as! SettingSectionHeaderView
headerView.titleLabel.text = NSLocalizedString("options_agencies", comment: "");
return headerView
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
CGFloat(SettingSectionHeaderView.Height)
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
networks.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let network = networks[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: SettingDetailTableViewCell.Identifier, for: indexPath) as! SettingDetailTableViewCell
cell.textLabel?.text = "\(network.acronym) (\(network.country))"
if EQNNotificheReteSismiche.shared().listaEnti.contains(network.acronym) {
cell.accessoryType = .checkmark
} else {
cell.accessoryType = .none
}
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let network = networks[indexPath.row]
var savedNetworks = EQNNotificheReteSismiche.shared().listaEnti
if let index = savedNetworks.firstIndex(where: { $0 == network.acronym }) {
savedNetworks.remove(at: index)
} else {
savedNetworks.append(network.acronym)
}
EQNNotificheReteSismiche.shared().listaEnti = savedNetworks
EQNNotificheReteSismiche.shared().saveUserInfo()
tableView.reloadData()
}
}
@@ -1,18 +0,0 @@
//
// SettingsUserReportAlertsViewController.h
// Earthquake Network
//
// Refactored by Andrea Busi 25/08/2020.
// Copyright © 2020 Earthquake Network. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "SettingsBaseViewController.h"
NS_ASSUME_NONNULL_BEGIN
@interface SettingsUserReportAlertsViewController : SettingsBaseViewController
@end
NS_ASSUME_NONNULL_END
@@ -1,119 +0,0 @@
//
// SettingsUserReportAlertsViewController.m
// Earthquake Network
//
// Refactored by Andrea Busi 25/08/2020.
// Copyright © 2020 Earthquake Network. All rights reserved.
//
#import "SettingsUserReportAlertsViewController.h"
#import "EQNNotificheSegnalazioniUtente.h"
@interface SettingsUserReportAlertsViewController ()
@property (nonatomic, strong) NSArray<SettingItem *> *settings;
@property (nonatomic, assign) BOOL notificationsEnabled;
@property (nonatomic, strong) EQNGenericValue *currentRadius;
@end
@implementation SettingsUserReportAlertsViewController
#pragma mark - View Lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
[self setupUI];
self.settings = @[
[[SettingItem alloc] initWithType:SettingTypeEnable title:NSLocalizedString(@"options_notification_enable_manual", @"")],
[[SettingItem alloc] initWithType:SettingTypeSlider title:NSLocalizedString(@"options_radius", @"")]
];
[self updateUI];
}
#pragma mark - Private
- (void)setupUI
{
self.navigationItem.largeTitleDisplayMode = UINavigationItemLargeTitleDisplayModeNever;
self.tableView.estimatedRowHeight = 100.0;
self.tableView.rowHeight = UITableViewAutomaticDimension;
[self.tableView registerClass:[SettingSectionHeaderView class] forHeaderFooterViewReuseIdentifier:SettingSectionHeaderView.Identifier];
[self.tableView registerClass:[SettingEnableTableViewCell class] forCellReuseIdentifier:SettingEnableTableViewCell.Identifier];
[self.tableView registerClass:[SettingSliderTableViewCell class] forCellReuseIdentifier:SettingSliderTableViewCell.Identifier];
}
- (void)updateUI
{
self.notificationsEnabled = [EQNNotificheSegnalazioniUtente sharedInstance].isAbilitato;
EQNGenericValue *distanzaPosizione = [EQNData raggioSismaFor:[EQNNotificheSegnalazioniUtente sharedInstance].distanzaPosizione];
self.currentRadius = distanzaPosizione;
[[EQNNotificheSegnalazioniUtente sharedInstance] saveUserInfo];
[self.tableView reloadData];
}
- (void)updateRadius:(EQNGenericValue *)radius
{
self.currentRadius = radius;
[EQNNotificheSegnalazioniUtente sharedInstance].distanzaPosizione = radius.value;
[[EQNNotificheSegnalazioniUtente sharedInstance] saveUserInfo];
[self.tableView reloadData];
}
#pragma mark - Table view data source
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
SettingSectionHeaderView *headerView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:SettingSectionHeaderView.Identifier];
headerView.titleLabel.text = NSLocalizedString(@"options_notification_manual", @"titolo impostazioni notifiche");
return headerView;
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return SettingSectionHeaderView.Height;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.settings.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
SettingItem *setting = self.settings[indexPath.row];
if (setting.type == SettingTypeEnable) {
SettingEnableTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SettingEnableTableViewCell.Identifier forIndexPath:indexPath];
cell.toggleSwitch.on = self.notificationsEnabled;
cell.titleLabel.text = setting.displayTitle;
cell.descriptionLabel.text = setting.subtitle;
cell.valueChanged = ^(BOOL enabled) {
self.notificationsEnabled = enabled;
[EQNNotificheSegnalazioniUtente sharedInstance].isAbilitato = self.notificationsEnabled;
[[EQNNotificheSegnalazioniUtente sharedInstance] saveUserInfo];
[self.tableView reloadData];
};
return cell;
} else if (setting.type == SettingTypeSlider) {
SettingSliderTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SettingSliderTableViewCell.Identifier forIndexPath:indexPath];
cell.isDisabled = !self.notificationsEnabled;
cell.titleLabel.text = setting.displayTitle;
[cell configureSliderWith:[EQNData raggioSismi] current:self.currentRadius];
cell.valueChanged = ^(EQNGenericValue *item) {
[self updateRadius:item];
};
return cell;
}
return nil;
}
@end
@@ -0,0 +1,145 @@
//
// SettingsUserReportNotificationsViewController.swift
// Earthquake Network
//
// Created by Andrea Busi on 10/06/24.
// Copyright © 2024 Earthquake Network. All rights reserved.
//
import UIKit
import Shogun
class SettingsUserReportNotificationsViewController: SettingsBaseTableViewController {
private enum RowIdentifier: Int {
case abilitaNotifiche
case distanzaMassima
}
private var isNotificationEnabled = false
private var currentMaximumDistance = EQNData.DefaultSettingUserReportNotificationRadius
private let dataSourceMaximumDistance = EQNData.settingUserReportNotificationRadius
private let settings: [SettingItem] = [
.init(type: .enable, title: NSLocalizedString("options_notification_enable_manual", comment: "")),
.init(type: .slider, title: NSLocalizedString("options_radius", comment: ""))
]
// MARK: - View Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
tableView.reloadData()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
loadDataSource()
tableView.reloadData()
}
// MARK: - Private
private func setupUI() {
navigationItem.largeTitleDisplayMode = .never
tableView.estimatedRowHeight = 200.0
tableView.rowHeight = UITableView.automaticDimension
tableView.registerHeaderFooterView(for: SettingSectionHeaderView.self)
tableView.registerCell(for: SettingEnableTableViewCell.self)
tableView.registerCell(for: SettingSliderTableViewCell.self)
}
private func loadDataSource() {
let saved = EQNSettingUserReportNotification.shared
isNotificationEnabled = saved.isAbilitato
let distanzaMassima = EQNData.getSettingUserReportNotificationRadius(for: saved.distanzaMassima)
currentMaximumDistance = distanzaMassima
}
// MARK: - Table view delegate and data source
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let view = tableView.dequeueHeaderFooterView(cellIdentifiable: SettingSectionHeaderView.self)
view.titleLabel.text = NSLocalizedString("options_notification_manual", comment: "")
return view
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
SettingSectionHeaderView.Height
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return settings.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let identifier = RowIdentifier(rawValue: indexPath.row) else {
return UITableViewCell()
}
let setting = settings[indexPath.row]
switch setting.type {
case .enable:
let cell = tableView.dequeueReusableCell(cellIdentifiable: SettingEnableTableViewCell.self, for: indexPath)
cell.titleLabel.text = setting.displayTitle
cell.descriptionLabel.text = setting.subtitle
switch identifier {
case .abilitaNotifiche:
cell.toggleSwitch.isOn = isNotificationEnabled
cell.valueChanged = { [weak self] enabled in
self?.onChangeNotificationEnabled(enabled)
}
default:
break
}
return cell
case .slider:
let cell = tableView.dequeueReusableCell(cellIdentifiable: SettingSliderTableViewCell.self, for: indexPath)
cell.titleLabel.text = setting.displayTitle
switch identifier {
case .distanzaMassima:
cell.isDisabled = !isNotificationEnabled
cell.configureSlider(with: dataSourceMaximumDistance, current: currentMaximumDistance)
cell.valueChanged = { [weak self] item in
self?.onChangeMaximumDistance(item)
}
default:
break
}
return cell
default:
fatalError()
}
}
// MARK: - Private
private func onChangeNotificationEnabled(_ enabled: Bool) {
isNotificationEnabled = enabled
EQNSettingUserReportNotification.shared.isAbilitato = isNotificationEnabled
EQNSettingUserReportNotification.shared.saveUserInfo()
tableView.reloadData()
}
private func onChangeMaximumDistance(_ item: EQNGenericValue) {
EQNSettingUserReportNotification.shared.distanzaMassima = item.value
EQNSettingUserReportNotification.shared.saveUserInfo()
loadDataSource()
}
}
@@ -35,7 +35,7 @@ class EQNBaseMapViewController: EQNBaseViewController, MKMapViewDelegate {
// MARK: - Internal
/// Annotations displayed on the map
private var mapAnnotations = [MKAnnotation]()
private(set) var mapAnnotations = [MKAnnotation]()
/// If `true`, the initial filter has been already evaluated
private var initialFilterEvaluated = false
@@ -104,6 +104,34 @@ class EQNBaseMapViewController: EQNBaseViewController, MKMapViewDelegate {
return label
}()
// app icon and name displayed on the screenshot
private lazy var watermarkView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
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
}()
// MARK: - Init
init() {
@@ -155,6 +183,18 @@ class EQNBaseMapViewController: EQNBaseViewController, MKMapViewDelegate {
extraUI()
}
private func addWatermarkView() {
view.addSubview(watermarkView)
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
}
private func removeWatermarkView() {
watermarkView.removeFromSuperview()
}
// MARK: - View Lifecycle
override func viewDidLoad() {
@@ -238,6 +278,12 @@ class EQNBaseMapViewController: EQNBaseViewController, MKMapViewDelegate {
elaborateMapCenter()
}
func reloadMap() {
// remove and re-add annotations, to force redrawn
mapView.removeAnnotations(mapAnnotations)
mapView.addAnnotations(mapAnnotations)
}
/// Changes the center coordinate of the map to a given location
func setMapCenter(for location: CLLocation, span: MKCoordinateSpan = MKCoordinateSpan(latitudeDelta: 8, longitudeDelta: 8)) {
let region = MKCoordinateRegion(center: location.coordinate, span: span)
@@ -249,6 +295,31 @@ class EQNBaseMapViewController: EQNBaseViewController, MKMapViewDelegate {
// nope, subclass will implement logic
}
func zPriority(for annotation: MKAnnotation) -> MKAnnotationViewZPriority {
// subclass will impelement its own logic to define annotation priority
.min
}
func createSnapshot(
prepare: () -> Void = { },
restore: () -> Void = { }
) -> UIImage {
prepare()
addWatermarkView()
// 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)
}
restore()
removeWatermarkView()
return image
}
// MARK: - Private
private func updateUI() {
@@ -286,6 +357,7 @@ class EQNBaseMapViewController: EQNBaseViewController, MKMapViewDelegate {
}
let annotationView = setupAnnotationView(for: annotation, on: mapView)
annotationView?.zPriority = zPriority(for: annotation)
return annotationView
}
@@ -244,7 +244,7 @@ class AlertSimulatorViewController: UIViewController, MKMapViewDelegate {
}
private func navigateToSubscriptions() {
let controller = SubscriptionsViewController.makeViewController()
let controller = SubscriptionsViewController()
let navigationController = UINavigationController(rootViewController: controller)
present(navigationController, animated: true)
}
@@ -6,16 +6,12 @@
#import "Costanti.h"
#import "EQNUser.h"
#import "EQNManager.h"
#import "EQNNotificheReteSismiche.h"
#import "EQNNotificheSegnalazioniUtente.h"
#import "EQNSisma.h"
#import "EQNBaseViewController.h"
#import "SettingsBaseViewController.h"
#import "EQNGeneratoreURLServer.h"
#import "ServerRequest.h"
#import "EQNSegnalazione.h"
#import "EQNPastquakes.h"
#import "EQNAllertaSismica.h"
#import "GADTTemplateView.h"
#import "GADTMediumTemplateView.h"
+6 -4
View File
@@ -55,9 +55,9 @@
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSCalendarsUsageDescription</key>
<string>L'accesso al calendario è richiesto per poter salvare le informazioni dei sismi di interesse</string>
<string>L&apos;accesso al calendario è richiesto per poter salvare le informazioni dei sismi di interesse</string>
<key>NSContactsUsageDescription</key>
<string>L'accesso ai contatti è richiesto per poter aggiungere persone agli eventi creati</string>
<string>L&apos;accesso ai contatti è richiesto per poter aggiungere persone agli eventi creati</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string> Ci occorre la tua posizione per inviare messaggi precisi in caso di terremoto</string>
<key>NSLocationAlwaysUsageDescription</key>
@@ -65,9 +65,9 @@
<key>NSLocationWhenInUseUsageDescription</key>
<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>
<string>L&apos;accesso alla libreria è richiesto per poter salvare le immagini generate dall&apos;app</string>
<key>NSUserTrackingUsageDescription</key>
<string>Il tracciamento serve a capire se la pubblicità dell'app è efficace</string>
<string>Il tracciamento serve a capire se la pubblicità dell&apos;app è efficace</string>
<key>SKAdNetworkItems</key>
<array>
<dict>
@@ -104,5 +104,7 @@
</array>
<key>UIUserInterfaceStyle</key>
<string>Light</string>
<key>LSMinimumSystemVersion</key>
<string>12.0</string>
</dict>
</plist>
@@ -26,3 +26,14 @@ extension NSDate {
return (self as Date).isBeforeInterval(interval)
}
}
extension CGFloat {
var negative: CGFloat {
-self
}
var x2: CGFloat {
self*2.0
}
}
@@ -12,9 +12,7 @@ import UIKit
extension UIView {
func eqn_applyShadowAndRoundedCorners() {
// rounded corners
layer.cornerRadius = AppTheme.shared.cardCornerRadius
layer.masksToBounds = false
eqn_applyRoundedCorners()
// apply a shadow to the current view
layer.shadowColor = UIColor.black.cgColor
@@ -22,4 +20,10 @@ extension UIView {
layer.shadowOffset = CGSize(width: 0, height: 2)
layer.shadowRadius = 2
}
func eqn_applyRoundedCorners() {
// rounded corners
layer.cornerRadius = AppTheme.shared.cardCornerRadius
layer.masksToBounds = false
}
}
@@ -14,7 +14,7 @@ public class EQNAppearanceCommand: EQNCommandProtocol {
// MARK: - Public
func execute() {
print("EQNAppearanceCommand: start execute")
print("[EQNAppearanceCommand] Start execute")
applyAppearance()
}
@@ -32,7 +32,7 @@ public class EQNAppearanceCommand: EQNCommandProtocol {
navAppearance.largeTitleTextAttributes = [
NSAttributedString.Key.foregroundColor: AppTheme.Colors.darkGray
]
navAppearance.backgroundColor = AppTheme.Colors.primary
navAppearance.backgroundColor = AppTheme.Colors.navBar
navAppearance.shadowColor = UIColor.clear
proxyNavBar.isTranslucent = false
@@ -15,13 +15,13 @@ public class EQNUserDefaultsCommand: EQNCommandProtocol {
// MARK: - Public
func execute() {
print("EQNUserDefaultsCommand: start execute")
print("[EQNUserDefaultsCommand] Start execute")
applyDefaultSettings()
saveMissingValues()
migrationV5_3()
migrationV5_4()
migrationV5_8()
migrationFirstAppStat()
migrationCriticalAlerts()
}
// MARK: - Private
@@ -35,47 +35,76 @@ public class EQNUserDefaultsCommand: EQNCommandProtocol {
}
}
private func saveMissingValues() {
// `raggio sismi forti` was not saved before v2.3
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)
private func migrationV5_8() {
let migrationPerformed = UserDefaults.standard.bool(forKey: UserDefaults.AppMigrationV5_8)
if migrationPerformed {
print("[EQNUserDefaultsCommand] Migration v5.3 already performed")
print("[EQNUserDefaultsCommand] Migration v5.8 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
// delete old notification settings
let userDefaults = UserDefaults.standard
let groupUserDefaults = UserDefaults.appGroup
if let encodedLocation = userDefaults.object(forKey: UserDefaults.UserDataLastLocation) as? Data {
groupUserDefaults?.set(encodedLocation, forKey: UserDefaults.UserDataLastLocation)
[
"NOTIFICHE_ATTIVA_RETI_SISMICHE_VICINE", "NOTIFICHE_ATTIVA_RETI_TERREMOTI_FORTI",
"NOTIFICHE_ATTIVA_RETI_ENERGIA_FORTI", "NOTIFICHE_ATTIVA_RETI_LISTA_ENTI"
].forEach { key in
userDefaults.removeObject(forKey: key)
}
userDefaults.set(true, forKey: UserDefaults.AppMigrationV5_4)
// delete old filter values
[
"EQN_ETA_MASSIMA", "EQN_SISMI_FORTI_ABILITATI", "EQN_SISMI_FORTI",
"EQN_SISMI_QUALSIASI_MAGNITUDO", "EQN_SISMI_MODIFICA_IMPOSTAZIONI"
].forEach { key in
userDefaults.removeObject(forKey: key)
}
// delete old "real time alert" settings
[
"NOTIFICHE_ALLERA_SISMICA_IMPOSTA_VOLUME", "NOTIFICHE_ALLERA_SISMICA_TESTA_ALLARME",
"NOTIFICHE_ALLERA_SISMICA_ABILITA_INTERVALLO", "NOTIFICHE_ALLERA_SISMICA_ORA_INIZIO",
"NOTIFICHE_ALLERA_SISMICA_ORA_INIZIO"
].forEach { key in
userDefaults.removeObject(forKey: key)
}
userDefaults.set(true, forKey: UserDefaults.AppMigrationV5_8)
}
private func migrationFirstAppStat() {
// before v5.8.2, first app start was defined using Firebase Token
let userDefaults = UserDefaults.standard
let firstAppStartExecuted = userDefaults.bool(forKey: UserDefaults.FirstAppStartExecuted)
if firstAppStartExecuted {
print("[EQNUserDefaultsCommand] First app start already executed")
return
}
let firebaseToken = userDefaults.object(forKey: UserDefaults.UserDataFirebaseToken) as? String
if firebaseToken != nil {
print("[EQNUserDefaultsCommand] First app start migrated")
userDefaults.set(true, forKey: UserDefaults.FirstAppStartExecuted)
}
}
private func migrationCriticalAlerts() {
let userDefaults = UserDefaults.standard
let migrationPerformed = userDefaults.bool(forKey: UserDefaults.AppMigrationV5_8_2)
if migrationPerformed {
print("[EQNUserDefaultsCommand] Migration v5.8.2 already performed")
return
}
UNUserNotificationCenter.current().getNotificationSettings { settings in
if settings.criticalAlertSetting != .enabled {
print("[EQNUserDefaultsCommand] Critical alerts not enabled, disable settings")
EQNSettingRealTimeAlert.shared.isCriticalAlertsEnabled = false
EQNSettingRealTimeAlert.shared.saveUserInfo()
} else {
print("[EQNUserDefaultsCommand] Critical alerts enabled, do nothing")
}
}
userDefaults.set(true, forKey: UserDefaults.AppMigrationV5_8_2)
}
}
+95 -84
View File
@@ -10,89 +10,121 @@ import Foundation
@objc class EQNData: NSObject {
@objc public static let MaxRaggioSisma = "100000"
@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 DefaultPeriodoTemporale = EQNGenericValue(value: "1440", display: "report_timeframe_one_day")
static let MaxRaggioSisma = "100000"
static let DefaultSettingSeismicNetworkNotificationRadius = EQNGenericValue(value:"500", display:"500 km")
static let DefaultSettingSeismicNetworkNotificationMagitude = EQNGenericValue(value:"2.0", display:"official_magnitude_value_20")
static let DefaultSettingUserReportNotificationRadius = EQNGenericValue(value:"1000", display:"1000 km")
static let DefaultFilterRadius = EQNGenericValue(value:"250", display:"250 km")
static let DefaultFilterMagnitude = EQNGenericValue(value:"0.0", display:"official_magnitude_value_00")
// MARK: - Public
@objc class func raggioSismi() -> [EQNGenericValue] {
[
EQNGenericValue(value:"50", display:"50 km"),
EQNGenericValue(value:"100", display:"100 km"),
EQNGenericValue(value:"200", display:"200 km"),
EQNGenericValue(value:"300", display:"300 km"),
EQNGenericValue(value:"400", display:"400 km"),
EQNGenericValue(value:"500", display:"500 km"),
EQNGenericValue(value:"600", display:"600 km"),
EQNGenericValue(value:"800", display:"800 km"),
EQNGenericValue(value:"1000", display:"1000 km"),
EQNGenericValue(value:"2000", display:"2000 km"),
EQNGenericValue(value:"4000", display:"4000 km"),
EQNGenericValue(value:Self.MaxRaggioSisma, display:"radius_any_distance"),
]
}
// Distances for "seismic network notifications"
static let settingSeismicNetworkNotificationRadius: [EQNGenericValue] = [
EQNGenericValue(value:"100", display:"100 km"),
EQNGenericValue(value:"250", display:"250 km"),
EQNGenericValue(value:"500", display:"500 km"),
EQNGenericValue(value:"1000", display:"1000 km")
]
// Magnitudes for "seismic network notifications"
static let settingSeismicNetworkNotificationMagnitudes: [EQNGenericValue] = [
EQNGenericValue(value:"0.0", display:"0.0"),
EQNGenericValue(value:"0.5", display:"0.5"),
EQNGenericValue(value:"1.0", display:"1.0"),
EQNGenericValue(value:"1.5", display:"1.5"),
EQNGenericValue(value:"2.0", display:"2.0"),
EQNGenericValue(value:"2.5", display:"2.5"),
EQNGenericValue(value:"3.0", display:"3.0"),
EQNGenericValue(value:"3.5", display:"3.5"),
EQNGenericValue(value:"4.0", display:"4.0"),
EQNGenericValue(value:"4.5", display:"4.5"),
EQNGenericValue(value:"5.0", display:"5.0"),
EQNGenericValue(value:"5.5", display:"5.5")
]
// Distances for "user report notifications"
static let settingUserReportNotificationRadius: [EQNGenericValue] = [
EQNGenericValue(value:"100", display:"100 km"),
EQNGenericValue(value:"250", display:"250 km"),
EQNGenericValue(value:"500", display:"500 km"),
EQNGenericValue(value:"1000", display:"1000 km")
]
// Misure raggio utilizzate nei filtri
static let filterRadius: [EQNGenericValue] = [
EQNGenericValue(value:"100", display:"100 km"),
EQNGenericValue(value:"250", display:"250 km"),
EQNGenericValue(value:"500", display:"500 km"),
EQNGenericValue(value:"750", display:"750 km"),
EQNGenericValue(value:"1000", display:"1000 km"),
EQNGenericValue(value:"1500", display:"1500 km"),
EQNGenericValue(value:"2000", display:"2000 km")
]
// Magnitudo utilizzate nei filtri
static let filterMagnitude: [EQNGenericValue] = [
EQNGenericValue(value:"0.0", display:"0.0"),
EQNGenericValue(value:"1.0", display:"1.0"),
EQNGenericValue(value:"2.0", display:"2.0"),
EQNGenericValue(value:"3.0", display:"3.0"),
EQNGenericValue(value:"4.0", display:"4.0"),
EQNGenericValue(value:"5.0", display:"5.0"),
EQNGenericValue(value:"6.0", display:"6.0")
]
/// Returns the EQNGenericValue for the given value, or the default one
/// - Parameter value: Sisma value to search
/// - Parameter value: Sisma radius to search
/// - Returns: Found value or default
@objc class func raggioSisma(for value: String?) -> EQNGenericValue {
if let value = value, let genericValue = Self.raggioSismi().first(where: { $0.value == value }) {
@objc(getSettingSeismicNetworkAlertRadiusForValue:)
class func getSettingSeismicNetworkNotificationRadius(for value: String?) -> EQNGenericValue {
if let value = value, let genericValue = settingSeismicNetworkNotificationRadius.first(where: { $0.value == value }) {
return genericValue
}
return Self.DefaultRaggioSisma
}
@objc class func magitudoDeboli() -> [EQNGenericValue] {
[
EQNGenericValue(value:"0.0", display:"official_magnitude_value_00"),
EQNGenericValue(value:"0.5", display:"official_magnitude_value_05"),
EQNGenericValue(value:"1.0", display:"official_magnitude_value_10"),
EQNGenericValue(value:"1.5", display:"official_magnitude_value_15"),
EQNGenericValue(value:"2.0", display:"official_magnitude_value_20"),
EQNGenericValue(value:"2.5", display:"official_magnitude_value_25"),
EQNGenericValue(value:"3.0", display:"official_magnitude_value_30"),
EQNGenericValue(value:"3.5", display:"official_magnitude_value_35"),
EQNGenericValue(value:"4.0", display:"official_magnitude_value_40"),
EQNGenericValue(value:"4.5", display:"official_magnitude_value_45"),
EQNGenericValue(value:"5.0", display:"official_magnitude_value_50"),
EQNGenericValue(value:"5.5", display:"official_magnitude_value_55")
]
return DefaultSettingSeismicNetworkNotificationRadius
}
/// Returns the EQNGenericValue for the given value, or the default one
/// - Parameter value: Magnitudo value to search
/// - Returns: Found value or default
@objc class func magitudoDebole(for value: String?) -> EQNGenericValue {
if let value = value, let genericValue = Self.magitudoDeboli().first(where: { $0.value == value }) {
class func getSettingSeismicNetworkNotificationMagnitudes(for value: String?) -> EQNGenericValue {
if let value = value, let genericValue = settingSeismicNetworkNotificationMagnitudes.first(where: { $0.value == value }) {
return genericValue
}
return Self.DefaultMagitudoDebole
}
@objc class func magitudoForti() -> [EQNGenericValue] {
[
EQNGenericValue(value:"5.5", display:"official_magnitude_value_55"),
EQNGenericValue(value:"6.0", display:"official_magnitude_value_60"),
EQNGenericValue(value:"6.5", display:"official_magnitude_value_65"),
EQNGenericValue(value:"7.0", display:"official_magnitude_value_70"),
EQNGenericValue(value:"7.5", display:"official_magnitude_value_75")
]
return DefaultSettingSeismicNetworkNotificationMagitude
}
/// Returns the EQNGenericValue for the given value, or the default one
/// - Parameter value: Magnitudo value to search
/// - Parameter value: Sisma radius to search
/// - Returns: Found value or default
@objc class func magitudoForte(for value: String?) -> EQNGenericValue {
if let value = value, let genericValue = Self.magitudoForti().first(where: { $0.value == value }) {
class func getSettingUserReportNotificationRadius(for value: String?) -> EQNGenericValue {
if let value = value, let genericValue = settingUserReportNotificationRadius.first(where: { $0.value == value }) {
return genericValue
}
return Self.DefaultMagitudoForte
return DefaultSettingUserReportNotificationRadius
}
@objc class func seismicNetworks() -> [EQNSeismicNetwork] {
/// Returns the EQNGenericValue for the given value, or the default one
/// - Parameter value: Sisma radius to search
/// - Returns: Found value or default
class func filterRadius(for value: String?) -> EQNGenericValue {
if let value = value, let genericValue = filterRadius.first(where: { $0.value == value }) {
return genericValue
}
return DefaultFilterRadius
}
/// Returns the EQNGenericValue for the given value, or the default one
/// - Parameter value: Magnitudo value to search
/// - Returns: Found value or default
class func filterMagnitude(for value: String?) -> EQNGenericValue {
if let value = value, let genericValue = filterMagnitude.first(where: { $0.value == value }) {
return genericValue
}
return DefaultFilterMagnitude
}
class func seismicNetworks() -> [EQNSeismicNetwork] {
[
EQNSeismicNetwork(acronym: "USGS", country: NSLocalizedString("configuration_countries_united_states", comment: ""), extended: ""),
EQNSeismicNetwork(acronym: "INGV", country: NSLocalizedString("configuration_countries_italy", comment: ""), extended: ""),
@@ -120,37 +152,16 @@ import Foundation
]
}
@objc class func seismicNetworkAcronyms() -> [String] {
class func seismicNetworkAcronyms() -> [String] {
Self.seismicNetworks().map { $0.acronym }
}
@objc class func seismicNetworkCountries() -> [String] {
class func seismicNetworkCountries() -> [String] {
Self.seismicNetworks().map { $0.country }
}
@objc class func seismic(for acronym: String?) -> EQNSeismicNetwork? {
class func seismic(for acronym: String?) -> EQNSeismicNetwork? {
guard let acronym = acronym else { return nil }
return Self.seismicNetworks().first(where: { $0.acronym == acronym })
}
@objc class func periodiTemporali() -> [EQNGenericValue] {
[
EQNGenericValue(value: "10", display: "10 minuti"),
EQNGenericValue(value: "60", display: "report_timeframe_one_hour"),
EQNGenericValue(value: "120", display: "report_timeframe_two_hours"),
EQNGenericValue(value: "360", display: "report_timeframe_six_hours"),
EQNGenericValue(value: "720", display: "report_timeframe_twelve_hours"),
EQNGenericValue(value: "1440", display: "report_timeframe_one_day")
]
}
/// Returns the EQNGenericValue for the given value, or the default one
/// - Parameter value: Temporal unit value to search
/// - Returns: Found value or default
@objc class func periodoTemporale(for value: String?) -> EQNGenericValue {
if let value = value, let genericValue = Self.periodiTemporali().first(where: { $0.value == value }) {
return genericValue
}
return Self.DefaultPeriodoTemporale
}
}
@@ -27,10 +27,110 @@
/// THE SOFTWARE.
import Foundation
import StoreKit
public struct VersioneProProducts {
public struct EQNInAppProducts {
public enum Identifier {
enum Plan: CaseIterable {
case monthly
case yearly
case perpetual
var localizedTitle: String {
switch self {
case .monthly: NSLocalizedString("subscription_plan_monthly", comment: "")
case .yearly: NSLocalizedString("subscription_plan_yearly", comment: "")
case .perpetual: NSLocalizedString("subscription_plan_perpetual", comment: "")
}
}
}
enum Category {
case pro
case top10k
case top100k
var image: UIImage? {
switch self {
case .pro: nil
case .top10k: UIImage(named: "top_10k")
case .top100k: UIImage(named: "top_100k")
}
}
var localizedTitle: String {
switch self {
case .pro: return NSLocalizedString("network_pro", comment: "")
case .top10k: return "Top 10k"
case .top100k: return "Top 100k"
}
}
}
let plan: Plan
let category: Category
let isDiscounted: Bool
let product: SKProduct
// MARK: - Init
private init(plan: Plan, category: Category, isDiscounted: Bool, product: SKProduct) {
self.plan = plan
self.category = category
self.isDiscounted = isDiscounted
self.product = product
}
// MARK: - Accessories
var isTop10k: Bool {
category == .top10k
}
var isTop100k: Bool {
category == .top100k
}
var isSubscription: Bool {
category != .pro
}
var productIdentifier: String {
product.productIdentifier
}
// MARK: - Static
static func from(product: SKProduct) -> EQNInAppProducts? {
switch product.productIdentifier {
case Identifier.ProVersionFullPrice:
.init(plan: .perpetual, category: .pro, isDiscounted: false, product: product)
case Identifier.ProVersionDiscounted:
.init(plan: .perpetual, category: .pro, isDiscounted: true, product: product)
case Identifier.Subscription10kMonthly:
.init(plan: .monthly, category: .top10k, isDiscounted: false, product: product)
case Identifier.Subscription10kYearly:
.init(plan: .yearly, category: .top10k, isDiscounted: false, product: product)
case Identifier.Subscription10kYearlyDiscounted:
.init(plan: .yearly, category: .top10k, isDiscounted: true, product: product)
case Identifier.Subscription10kPerpetual:
.init(plan: .perpetual, category: .top10k, isDiscounted: false, product: product)
case Identifier.Subscription100kMonthly:
.init(plan: .monthly, category: .top100k, isDiscounted: false, product: product)
case Identifier.Subscription100kYearly:
.init(plan: .yearly, category: .top100k, isDiscounted: false, product: product)
case Identifier.Subscription100kYearlyDiscounted:
.init(plan: .yearly, category: .top100k, isDiscounted: true, product: product)
case Identifier.Subscription100kPerpetual:
.init(plan: .perpetual, category: .top100k, isDiscounted: false, product: product)
default:
nil
}
}
enum Identifier {
static let ProVersionFullPrice = "com.finazzi.distquake.ProPrezzoPieno"
static let ProVersionDiscounted = "com.finazzi.distquake.VersioneProScontata"
@@ -66,40 +166,9 @@ public struct VersioneProProducts {
static let identifiersForTop100k: Set<ProductIdentifier> = [
Subscription100kMonthly, Subscription100kYearly, Subscription100kYearlyDiscounted, Subscription100kPerpetual
]
static let identifierForSubscriptions: Set<ProductIdentifier> = [
Subscription10kMonthly, Subscription100kMonthly,
Subscription10kYearly, Subscription10kYearlyDiscounted,
Subscription100kYearly, Subscription100kYearlyDiscounted,
Subscription10kPerpetual, Subscription100kPerpetual
]
}
static func isSubscription(for identifier: String) -> Bool {
Identifier.identifierForSubscriptions.contains(identifier)
}
static func is10kSubscription(for identifier: String) -> Bool {
Identifier.identifiersForTop10k.contains(identifier)
}
static func is100kSubscription(for identifier: String) -> Bool {
Identifier.identifiersForTop100k.contains(identifier)
}
static func image(for productIdentifier: String) -> UIImage? {
if is100kSubscription(for: productIdentifier){
return UIImage(named: "top_100k")
}
if is10kSubscription(for: productIdentifier) {
return UIImage(named: "top_10k")
}
return nil
}
public static let store = IAPHelper(productIds: VersioneProProducts.Identifier.identifiers)
public static let store = IAPHelper(productIds: EQNInAppProducts.Identifier.identifiers)
}
func resourceNameForProductIdentifier(_ productIdentifier: String) -> String? {
+8 -12
View File
@@ -120,19 +120,15 @@
- (void)scaricaReteSismica
{
// 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 {
filterProvider = @"ALL";
}
// L'endpoint per lo scaricamento dei dati prende due parametri:
// - `pro` per il provider selezionato,
// - `mag` per la magnitudo minima.
// Dalla v5.8 non esiste più la selezione delle reti, quindi passiamo sempre "ALL".
// Per la magnitudo minima, invece, passiamo 0 per i filtri "raggio" e "rilevanti,
// altrimenti passiamo 2 per il filtro "mondo"
NSString *filterMagnitude = [EQNSeismic shared].magnitudoMinima;
NSString *filterProvider = @"ALL";
NSString *filterMagnitude = [EQNSeismic shared].filterOption == FilterTypeWorldWide ? @"2.0" : @"0.0";
NSString *queryString = [NSString stringWithFormat:@"?pro=%@&mag=%@", filterProvider, filterMagnitude];
NSString *urlString = [NSString stringWithFormat:EQNServerUrlDownloadRetiSismiche, queryString];
@@ -0,0 +1,130 @@
//
// EQNOfficialPushNotification.swift
// Earthquake Network
//
// Created by Andrea Busi on 29/06/24.
// Copyright © 2024 Earthquake Network. All rights reserved.
//
import Foundation
import Shogun
@objc
class EQNOfficialPushNotification: NSObject, Codable {
private enum CodingKeys: String, CodingKey {
case latitude
case longitude
case magnitude
case date
}
private let latitude: Double
private let longitude: Double
let magnitude: Double
let date: Date?
var coordinate: CLLocation {
.init(latitude: latitude, longitude: longitude)
}
// MARK: - Init
init(
latitude: Double,
longitude: Double,
magnitude: Double,
date: Date?
) {
self.latitude = latitude
self.longitude = longitude
self.magnitude = magnitude
self.date = date
}
// MARK: - Class
/// Remove any saved notification
static func removeStored() {
UserDefaults.standard.removeObject(forKey: UserDefaults.OfficialAlertPayload)
}
/// Retrieve stored notification (if any)
static func stored() -> EQNOfficialPushNotification? {
guard let data = UserDefaults.standard.object(forKey: UserDefaults.OfficialAlertPayload) as? Data else {
print("[EQNOfficialPushNotification] No notification saved for key '\(UserDefaults.OfficialAlertPayload)'")
return nil
}
guard let notification = try? JSONDecoder().decode(EQNOfficialPushNotification.self, from: data) else {
print("[EQNOfficialPushNotification] Unable to decode given notification")
return nil
}
return notification
}
/// Convert and store a push notification payload.
/// Expected payload has the following structure:
/// ```
/// {
/// "title": "Segnalazione da rete sismica",
/// "body": "Sisma rilevato a 4km S Valfabbrica (PG)",
/// "userInfo": {
/// "data" : "2024-06-29 11:21:30",
/// ...
/// "aps": {
/// "alert" : {
/// "loc-key" : "Sisma rilevato a",
/// "title-loc-key" : "Segnalazione da rete sismica",
/// "title" : "Segnalazione da rete sismica",
/// "action-loc-key" : "",
/// "loc-args" : [
/// "6 km S Acate (RG) - M1.9"
/// ]
/// },
/// }
/// }
/// }
/// ```
/// - 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("[EQNOfficialPushNotification] Unable to convert received notification")
return false
}
guard let data = try? JSONEncoder().encode(notification) else {
print("[EQNOfficialPushNotification] Unable to encode given notification")
return false
}
UserDefaults.standard.set(data, forKey: UserDefaults.OfficialAlertPayload)
return true
}
@objc
private static func from(payload: [String: Any]) -> EQNOfficialPushNotification? {
guard let userInfo = payload["userInfo"] as? [String: Any] else {
print("[EQNOfficialPushNotification] Missing required info to parse push notification")
return nil
}
let latitude = userInfo.double(forKey: "latitude") ?? 0
let longitude = userInfo.double(forKey: "longitude") ?? 0
let magnitude = userInfo.double(forKey: "magnitude") ?? 0
let dateString = userInfo.string(forKey: "data") ?? ""
let date = EQNUtility.getDateFrom(dateString)
return .init(
latitude: latitude,
longitude: longitude,
magnitude: magnitude,
date: date
)
}
}
@@ -57,21 +57,21 @@ public class EQNPurchaseUtility: NSObject {
/// Check if user has bought pro app version
/// Pro version is enabled also if a yearly subscription is enabled
@objc public static func isProVersionEnabled() -> Bool {
VersioneProProducts.Identifier.identifierForProVersion.reduce(false) { (result, identifier) -> Bool in
EQNInAppProducts.Identifier.identifierForProVersion.reduce(false) { (result, identifier) -> Bool in
return result || UserDefaults.standard.bool(forKey: identifier)
}
}
/// Check if user has bought Top 10k subscription
@objc public static func isTop10kEnabled() -> Bool {
VersioneProProducts.Identifier.identifiersForTop10k.reduce(false) { (result, identifier) -> Bool in
EQNInAppProducts.Identifier.identifiersForTop10k.reduce(false) { (result, identifier) -> Bool in
return result || UserDefaults.standard.bool(forKey: identifier)
}
}
/// Check if user has bought Top 100k subscription
@objc public static func isTop100kEnabled() -> Bool {
VersioneProProducts.Identifier.identifiersForTop100k.reduce(false) { (result, identifier) -> Bool in
EQNInAppProducts.Identifier.identifiersForTop100k.reduce(false) { (result, identifier) -> Bool in
return result || UserDefaults.standard.bool(forKey: identifier)
}
}
@@ -79,7 +79,7 @@ public class EQNPurchaseUtility: NSObject {
/// Remove saved in-app purchases flags.
/// Used only during development
@objc public static func resetInAppPurchases() {
VersioneProProducts.Identifier.identifiers.forEach { (identifier) in
EQNInAppProducts.Identifier.identifiers.forEach { (identifier) in
UserDefaults.standard.removeObject(forKey: identifier)
}
NotificationCenter.default.post(name: .EQNInAppPurchaseDidComplete, object: nil)
@@ -11,31 +11,36 @@ import Foundation
@objc class EQNSeismic: NSObject {
@objc enum FilterType: Int {
case inRadius
case positionRelevant
case worldWide
}
enum Sort: Int {
case time
case position
case magnitude
}
@objc static let shared = EQNSeismic()
@objc var magnitudoMinima: String
@objc var distanzaMassima: String
@objc var periodoTemporale: String
@objc var sismiFortiAbilitati: Bool
@objc var sismiFortiMagnitudo: String
@objc var sismiQualsiasiAbilitati: Bool
@objc var modificaImpostazioniAbilitato: Bool
@objc var filterOption: FilterType
var sort: Sort
var maximumDistance: String
@objc var minimumMagnitude: String
// MARK: - Init
override init() {
Self.migrateOldDistanza()
Self.migrateOldPeriodo()
Self.migrate_v5_8()
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)
filterOption = defaults.enumObject(forKey: UserDefaults.SeismicFilterOption, or: .positionRelevant)
sort = defaults.enumObject(forKey: UserDefaults.SeismicSort, or: .time)
maximumDistance = defaults.object(forKey: UserDefaults.SeismicDistanzaMassima, or: EQNData.DefaultFilterRadius.value)
minimumMagnitude = defaults.object(forKey: UserDefaults.SeismicMagnitudoMinima, or: EQNData.DefaultFilterMagnitude.value)
super.init()
}
@@ -44,114 +49,136 @@ import Foundation
// MARK: - Public
public func saveFilters() {
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)
let defaults = UserDefaults.standard
defaults.set(filterOption.rawValue, forKey: UserDefaults.SeismicFilterOption)
defaults.set(sort.rawValue, forKey: UserDefaults.SeismicSort)
defaults.set(maximumDistance, forKey: UserDefaults.SeismicDistanzaMassima)
defaults.set(minimumMagnitude, forKey: UserDefaults.SeismicMagnitudoMinima)
}
// MARK: - Private
private static func migrateOldDistanza() {
guard let savedValue = UserDefaults.standard.object(forKey: UserDefaults.SeismicDistanzaMassima) as? String else {
print("[EQNSeismic] Distanza massima: nessun valore da convertire")
private class func migrate_v5_8() {
let defaults = UserDefaults.standard
let alreadyMigrated = defaults.bool(forKey: UserDefaults.SismicFiltersMigrationV5_8)
if alreadyMigrated {
return
}
if savedValue.lowercased() == NSLocalizedString("radius_any_distance", comment: "").lowercased() {
print("[EQNSeismic] Distanza massima: trovato qualsiasi distanza, salvo valore")
UserDefaults.standard.set("100000", forKey: UserDefaults.SeismicDistanzaMassima)
} else {
print("[EQNSeismic] Distanza massima: valore da non convertire (value: \(savedValue))")
}
}
private static func migrateOldPeriodo() {
guard let savedValue = UserDefaults.standard.object(forKey: UserDefaults.SeismicEtaMassima) as? String else {
print("[EQNSeismic] Età massima: nessun valore da convertire");
return
if let savedMagnitude = defaults.object(forKey: UserDefaults.SeismicMagnitudoMinima) as? String {
let minMagnitude = switch savedMagnitude {
case "0.0": "0.0"
case "0.5", "1.0": "1.0"
case "1.5", "2.0": "2.0"
case "2.5", "3.0": "3.0"
case "3.5", "4.0": "4.0"
case "4.5", "5.0": "5.0"
case "5.5", "6.0": "6.0"
default: "0.0"
}
defaults.set(minMagnitude, forKey: UserDefaults.SeismicMagnitudoMinima)
}
var convertedValue: String?
if savedValue.lowercased() == NSLocalizedString("report_timeframe_one_day", comment: "").lowercased() {
convertedValue = "1440"
} else if savedValue.lowercased() == NSLocalizedString("report_timeframe_twelve_hours", comment: "").lowercased() {
convertedValue = "720"
} else if savedValue.lowercased() == NSLocalizedString("report_timeframe_six_hours", comment: "").lowercased() {
convertedValue = "360"
} else if savedValue.lowercased() == NSLocalizedString("report_timeframe_two_hours", comment: "").lowercased() {
convertedValue = "120"
} else if savedValue.lowercased() == NSLocalizedString("report_timeframe_one_hour", comment: "").lowercased() {
convertedValue = "60"
} else if savedValue.lowercased() == NSLocalizedString("report_timeframe_ten_minutes", comment: "").lowercased() {
convertedValue = "10"
if let savedDistance = defaults.object(forKey: UserDefaults.SeismicDistanzaMassima) as? String {
let maxDistance = switch savedDistance {
case "50", "100": "100"
case "200", "300": "250"
case "400", "500", "600": "500"
case "800": "750"
case "1000": "1000"
default:
// 2000, 4000, qualsiasi distanza
"2000"
}
defaults.set(maxDistance, forKey: UserDefaults.SeismicDistanzaMassima)
}
if let convertedValue = convertedValue {
print("[EQNSeismic] Età massima: salvo valore convertito (old: \(savedValue) - new: \(convertedValue)")
UserDefaults.standard.set(convertedValue, forKey: UserDefaults.SeismicEtaMassima)
} else {
print("[EQNSeismic] Età massima: valore già convertito")
}
defaults.set(true, forKey: UserDefaults.SismicFiltersMigrationV5_8)
}
// MARK: - Class
@objc func filterSeismicList(_ list: [EQNSisma]) -> [EQNSisma] {
// enti abilitati
var networks = EQNUserData.shared.seismicNetworksSelected()
if networks.isEmpty {
networks = EQNData.seismicNetworkAcronyms()
}
networks = networks.map { $0.lowercased() }
// filtri
let filterDistance = Double(distanzaMassima)
let filterMagnitude = Double(magnitudoMinima)
let filterShowNear = sismiQualsiasiAbilitati
let filterShowNearRadius: Double = 50.0
let filterStrongEarthquake = Double(sismiFortiMagnitudo)
let filterStrongEarthquakeEnabled = sismiFortiAbilitati
let filterTime = Double(periodoTemporale)
let filter_radius = Double(maximumDistance)
let filter_min_magnitude = Double(minimumMagnitude)
// filter seismic list
var filtered = [EQNSisma]()
for seismic in list {
for (i, seismic) in list.enumerated() {
var keep = true
if !networks.contains(seismic.provider.lowercased()) {
keep = false
}
let latitude = seismic.coordinate.coordinate.latitude
let longitude = seismic.coordinate.coordinate.longitude
let provider = seismic.provider.uppercased()
// filtro distanza massima
if let filterDistance = filterDistance, seismic.userDistance > filterDistance {
keep = false
}
// filtro magnitudo minima e mostra sismi di qualsiasi magnitudo
if let filterMagnitude = filterMagnitude, seismic.magnitude.doubleValue < filterMagnitude {
if !filterShowNear {
keep = false
} else {
if seismic.userDistance > filterShowNearRadius {
keep = false
//Ricerca di sismi duplicati in lista
for j in -3...3 {
if (j != 0) && (i + j > 0) && (i+j < list.count) {
let seismic_j = list[i+j]
let latitude_j = seismic_j.coordinate.coordinate.latitude
let longitude_j = seismic_j.coordinate.coordinate.longitude
let provider_j = seismic_j.provider.uppercased()
let delta_lat = abs(latitude - latitude_j)
let delta_lon = abs(longitude - longitude_j)
let delta_time = abs(EQNUtility.getDeltaMinute(seismic.date ?? Date()) - EQNUtility.getDeltaMinute(seismic_j.date ?? Date()))
let ratio = seismic.magnitude.doubleValue/seismic_j.magnitude.doubleValue
if ratio > 0.8 && ratio < 1.2 && delta_lat < 0.5 && delta_lon < 0.5 && delta_time <= 2 {
if provider == "EMSC" {
keep = false
}
if provider == "USGS" && !(provider_j == "EMSC") {
keep = false
}
}
}
}
// filtro sismi forti
if let filterStrongEarthquake = filterStrongEarthquake, seismic.provider == "EMSC" && filterStrongEarthquakeEnabled && seismic.magnitude.doubleValue > filterStrongEarthquake {
keep = true
}
// filtro tempo
if let filterTime = filterTime, seismic.timeDifference > filterTime {
keep = false
if keep {
let distance = seismic.userDistance
let magnitude = seismic.magnitude.doubleValue
if filterOption == .inRadius {
//filtro basato su magnitudo e raggio
if let filter_radius, let filter_min_magnitude, distance > filter_radius || magnitude < filter_min_magnitude {
keep = false
}
} else if filterOption == .positionRelevant {
//filtro sismi significativi
if magnitude < 7.0 && distance > 2000 {
keep = false
} else if magnitude < 6.5 && distance > 1600 {
keep = false
} else if magnitude < 6.0 && distance > 1300 {
keep = false
} else if magnitude < 5.5 && distance > 1000 {
keep = false
} else if magnitude < 5.0 && distance > 700 {
keep = false
} else if magnitude < 4.5 && distance > 500 {
keep = false
} else if magnitude < 4.0 && distance > 350 {
keep = false
} else if magnitude < 3.5 && distance > 200 {
keep = false
} else if magnitude < 3.0 && distance > 125 {
keep = false
} else if magnitude < 2.5 && distance > 70 {
keep = false
} else if magnitude < 2.0 && distance > 35 {
keep = false
} else if magnitude < 1.5 && distance > 20 {
keep = false
}
} else {
//filtro che mostra tutti i sismi a livello mondiale di magnitudo>=2
if magnitude < 2 {
keep = false
}
}
}
if keep {
@@ -159,6 +186,29 @@ import Foundation
}
}
switch sort {
case .time:
filtered.sort(by: { seismic1, seismic2 in
guard let date1 = seismic1.date else {
return false
}
guard let date2 = seismic2.date else {
return true
}
return date1 > date2
})
case .position:
filtered.sort { seismic1, seismic2 in
seismic1.userDistance < seismic2.userDistance
}
case .magnitude:
filtered.sort { seismic1, seismic2 in
seismic1.magnitude.doubleValue > seismic2.magnitude.doubleValue
}
}
return filtered
}
}
@@ -42,6 +42,7 @@
self.registrationInProgress = NO;
self.user_ID = EQNUserData.sharedData.userId;
self.lastPosition = EQNUserData.sharedData.lastLocation;
self.currentFirebaseToken = EQNUserData.sharedData.firebaseToken;
[self registerForLocationUpdates];
}
@@ -122,6 +123,13 @@
return;
}
if (token == nil) {
NSLog(@"[EQNUser] Firebase token is null, abort registration");
self.registrationInProgress = NO;
[[NSNotificationCenter defaultCenter] postNotificationName:EQNServerRegistrationDidFailNotification object:nil];
return;
}
self.registrationInProgress = YES;
NSURL *url = [EQNGeneratoreURLServer urlRegistrazioneFirebaseToken:token existingUserId:userId];
[[ServerRequest defaultServerConnectionSingleton] inviaInformazioniAlServerWithURL:url richiesta:EQNTipoChiamataRegistrazione success:^(id result) {
@@ -19,7 +19,8 @@ import CoreLocation
@objc
var isFirstStart: Bool {
firebaseToken == nil
let firstAppStartExecuted = UserDefaults.standard.bool(forKey: UserDefaults.FirstAppStartExecuted)
return !firstAppStartExecuted
}
// MARK: - Firebase Token
@@ -61,4 +61,9 @@ extension EQNUtility {
class func formattedDate(from date: Date) -> String {
Self.dateFormatter.string(from: date)
}
/// Convert a date to minutes
class func getDeltaMinute(_ date: Date) -> TimeInterval {
Date().timeIntervalSince(date) / 60.0
}
}
@@ -6,15 +6,16 @@
// Copyright © 2021 Earthquake Network. All rights reserved.
//
import Foundation
import UIKit
import MapKit
class EQNMapAnnotationSeismic: NSObject, MKAnnotation {
var coordinate: CLLocationCoordinate2D
var title: String?
var image: UIImage?
let coordinate: CLLocationCoordinate2D
let title: String?
let subtitle: String?
let textColor: UIColor
let seismic: EQNSisma
@@ -22,58 +23,60 @@ class EQNMapAnnotationSeismic: NSObject, MKAnnotation {
init(seismic: EQNSisma) {
self.seismic = seismic
self.coordinate = CLLocationCoordinate2D(latitude: seismic.coordinate.coordinate.latitude, longitude: seismic.coordinate.coordinate.longitude)
self.image = Self.image(for: seismic)
self.coordinate = CLLocationCoordinate2D(latitude: seismic.coordinate.coordinate.latitude,
longitude: seismic.coordinate.coordinate.longitude)
self.title = seismic.provider
if let date = seismic.date {
self.title = EQNUtility.formattedTimeDifference(from: date)
self.subtitle = EQNUtility.formattedTimeDifference(from: date)
} else {
self.subtitle = nil
}
self.textColor = Self.textColor(for: seismic)
super.init()
}
// MARK: - Helpers
func image(
height: CGFloat,
isUserSelection: Bool
) -> UIImage? {
let color = Self.imageColor(for: seismic)
let borderColor = isUserSelection ? AppTheme.Colors.red : AppTheme.Colors.darkGray
return UIImage.circle(diameter: height, color: color, borderWidth: 2.0, borderColor: borderColor)
}
// MARK: - Private
private class func image(for seismic: EQNSisma) -> UIImage? {
private class func textColor(for seismic: EQNSisma) -> UIColor {
let magnitude = seismic.magnitude.doubleValue
let color: String
if magnitude < 2.0 {
color = "white"
return UIColor(red: 0.0, green: 200.0/255.0, blue: 200.0/255.0, alpha: 1.0)
} else if magnitude < 3.5 {
color = "green"
return UIColor(red: 0.0, green: 200.0/255.0, blue: 0.0, alpha: 1.0)
} else if magnitude < 4.5 {
color = "yellow"
return UIColor(red: 240.0/255.0, green: 190.0/255.0, blue: 0.0, alpha: 1.0)
} else if magnitude < 5.5 {
color = "red"
return UIColor(red: 200.0/255.0, green: 0.0, blue: 0.0, alpha: 1.0)
} else {
color = "purple"
return UIColor(red: 200.0/255.0, green: 0.0, blue: 200.0/255.0, alpha: 1.0)
}
let icon: String
switch seismic.provider.uppercased() {
case "USGS": icon = "star"
case "SGC": icon = "star3"
case "CSN": icon = "star3f"
case "SSN": icon = "star4"
case "INPRES": icon = "star4r"
case "FUNVISIS": icon = "star6"
case "Ineter": icon = "triangle"
case "RSN": icon = "triangle2"
case "PHIVOLCS": icon = "triround_inner"
case "IGEPN": icon = "triround"
case "INGV": icon = "circle"
case "EMSC": icon = "dyamond"
case "IGP": icon = "dyamond_round"
case "JMA": icon = "esa"
case "GEONET": icon = "oct"
case "CSI": icon = "penta"
case "IGN": icon = "square"
case "UASD", "BDTIM", "NCS": icon = "thick_star"
case "RSPR": icon = "star6f"
case "UOA": icon = "triangle"
default: icon = ""
}
private class func imageColor(for seismic: EQNSisma) -> UIColor {
let magnitude = seismic.magnitude.doubleValue
if magnitude < 2.0 {
return UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0) // white
} else if magnitude < 3.5 {
return UIColor(red: 0.0, green: 1.0, blue: 0.0, alpha: 1.0) // green
} else if magnitude < 4.5 {
return UIColor(red: 1.0, green: 1.0, blue: 0.0, alpha: 1.0) // yellow
} else if magnitude < 5.5 {
return UIColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0) // red
} else {
return UIColor(red: 1.0, green: 0.0, blue: 1.0, alpha: 1.0) // magenta
}
return UIImage(named: "\(icon)_\(color)")
}
}
@@ -1,35 +0,0 @@
//
// EQNAllertaSismica.h
// Earthquake Network
//
// Refactored by Andrea Busi on 21/09/2020
// Copyright © 2020 Earthquake Network. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface EQNAllertaSismica : NSObject
@property (nonatomic, assign) BOOL isAbilitato;
@property (nonatomic, assign) BOOL isCriticalAlertsEnabled;
@property (nonatomic, assign) BOOL isintervalloAllarme;
@property (nonatomic, strong) NSString *sismiDaNotificare;
@property (nonatomic, strong) NSString *raggioSismiLievi;
@property (nonatomic, strong) NSString *raggioSismiForti;
@property (nonatomic, strong) NSDate *oraioInizio;
@property (nonatomic, strong) NSDate *orarioFine;
@property (nonatomic, strong) NSArray *listaMessaggi;
@property (nonatomic, strong) NSArray *listaAreeInteresse;
+ (instancetype)sharedInstance NS_SWIFT_NAME(shared());
- (void)saveUserInfo;
+ (void)saveDefaultValues;
@end
NS_ASSUME_NONNULL_END
@@ -1,77 +0,0 @@
//
// EQNAllertaSismica.m
// Earthquake Network
//
// Refactored by Andrea Busi on 21/09/2020
// Copyright © 2020 Earthquake Network. All rights reserved.
//
#import "EQNAllertaSismica.h"
#import "Costanti.h"
@implementation EQNAllertaSismica
#pragma mark - Singleton
+ (instancetype)sharedInstance
{
static EQNAllertaSismica *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
#pragma mark - Init
- (instancetype)init
{
self = [super init];
if (self) {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
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 appGroupUserDefaults];
self.isCriticalAlertsEnabled = [sharedDefaults boolForKey:NSUserDefaults.AllertaSismicaCriticalAlerts];
}
return self;
}
#pragma mark - Public
- (void)saveUserInfo
{
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[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 appGroupUserDefaults];
[sharedDefaults setBool:self.isCriticalAlertsEnabled forKey:NSUserDefaults.AllertaSismicaCriticalAlerts];
}
#pragma mark - Class
+ (void)saveDefaultValues
{
[EQNAllertaSismica sharedInstance].isAbilitato = YES;
[EQNAllertaSismica sharedInstance].isCriticalAlertsEnabled = NO;
[EQNAllertaSismica sharedInstance].sismiDaNotificare = @"0";
[EQNAllertaSismica sharedInstance].raggioSismiLievi = @"300";
[EQNAllertaSismica sharedInstance].raggioSismiForti = @"600";
[[EQNAllertaSismica sharedInstance] saveUserInfo];
}
@end
@@ -1,30 +0,0 @@
//
// EQNNotificheReteSismiche.h
// Earthquake Network
//
// Refactored by Andrea Busi on 21/09/2020
// Copyright © 2020 Earthquake Network. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface EQNNotificheReteSismiche : NSObject
@property (nonatomic, assign) BOOL isAbilitato;
@property (nonatomic, assign) BOOL isAbilitaVicini;
@property (nonatomic, assign) BOOL isTerremortiForti;
@property (nonatomic, strong) NSArray<NSString *> *listaEnti;
@property (nonatomic, strong) NSString *distanzaPosizione;
@property (nonatomic, strong) NSString *energiaSisma;
@property (nonatomic, strong) NSString *energiaTerremotiForti;
+ (instancetype)sharedInstance NS_SWIFT_NAME(shared());
- (void)saveUserInfo;
+ (void)saveDefaultValues;
@end
NS_ASSUME_NONNULL_END
@@ -1,72 +0,0 @@
//
// EQNNotificheReteSismiche.m
// Earthquake Network
//
// Refactored by Andrea Busi on 21/09/2020
// Copyright © 2020 Earthquake Network. All rights reserved.
//
#import "EQNNotificheReteSismiche.h"
#import "Costanti.h"
#import "EQNUtility.h"
@implementation EQNNotificheReteSismiche
#pragma mark - Singleton
+ (instancetype)sharedInstance
{
static EQNNotificheReteSismiche *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
#pragma mark - Init
- (instancetype)init
{
self = [super init];
if (self) {
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;
}
#pragma mark - Public
- (void)saveUserInfo
{
[[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];
}
#pragma mark - Class
+ (void)saveDefaultValues
{
[EQNNotificheReteSismiche sharedInstance].isAbilitato = YES;
[EQNNotificheReteSismiche sharedInstance].distanzaPosizione = @"1000";
[EQNNotificheReteSismiche sharedInstance].energiaSisma = @"3.5";
[EQNNotificheReteSismiche sharedInstance].isAbilitaVicini = NO;
[EQNNotificheReteSismiche sharedInstance].isTerremortiForti = NO;
[EQNNotificheReteSismiche sharedInstance].energiaTerremotiForti = @"7.5";
[[EQNNotificheReteSismiche sharedInstance] saveUserInfo];
}
@end
@@ -1,26 +0,0 @@
//
// EQNNotificheSegnalazioniUtente.h
// Earthquake Network
//
// Refactored by Andrea Busi on 21/09/2020
// Copyright © 2020 Earthquake Network. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface EQNNotificheSegnalazioniUtente : NSObject
@property (nonatomic, assign) BOOL isAbilitato;
@property (nonatomic, strong) NSString *distanzaPosizione;
+ (instancetype)sharedInstance NS_SWIFT_NAME(shared());
- (void)saveUserInfo;
+ (void)saveDefaultValues;
@end
NS_ASSUME_NONNULL_END
@@ -1,56 +0,0 @@
//
// EQNNotificheSegnalazioniUtente.m
// Earthquake Network
//
// Refactored by Andrea Busi on 21/09/2020
// Copyright © 2020 Earthquake Network. All rights reserved.
//
#import "EQNNotificheSegnalazioniUtente.h"
#import "Costanti.h"
@implementation EQNNotificheSegnalazioniUtente
#pragma mark - Singleton
+ (instancetype)sharedInstance
{
static EQNNotificheSegnalazioniUtente *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
#pragma mark - Init
- (instancetype)init
{
self = [super init];
if (self) {
self.isAbilitato = [[NSUserDefaults standardUserDefaults] boolForKey:NSUserDefaults.NotificheSegnalazioniUtenteAbilitato];
self.distanzaPosizione = [[NSUserDefaults standardUserDefaults] objectForKey:NSUserDefaults.NotificheSegnalazioniUtenteDistanzaPosizione];
}
return self;
}
#pragma mark - Public
- (void)saveUserInfo
{
[[NSUserDefaults standardUserDefaults] setBool:self.isAbilitato forKey:NSUserDefaults.NotificheSegnalazioniUtenteAbilitato];
[[NSUserDefaults standardUserDefaults] setObject:self.distanzaPosizione forKey:NSUserDefaults.NotificheSegnalazioniUtenteDistanzaPosizione];
[[NSUserDefaults standardUserDefaults] synchronize];
}
#pragma mark - Class
+ (void)saveDefaultValues
{
[EQNNotificheSegnalazioniUtente sharedInstance].isAbilitato = YES;
[EQNNotificheSegnalazioniUtente sharedInstance].distanzaPosizione = @"300";
[[EQNNotificheSegnalazioniUtente sharedInstance] saveUserInfo];
}
@end
@@ -0,0 +1,64 @@
//
// EQNSettingRealTimeAlert.swift
// Earthquake Network
//
// Created by Andrea Busi on 10/06/24.
// Copyright © 2024 Earthquake Network. All rights reserved.
//
import Foundation
@objc
class EQNSettingRealTimeAlert: NSObject {
@objc(sharedInstance)
static let shared = EQNSettingRealTimeAlert()
@objc var isAbilitato: Bool
@objc var isCriticalAlertsEnabled: Bool
@objc var sismiDaNotificare: String
@objc var raggioSismiLievi: String
@objc var raggioSismiForti: String
private static let DefaultSismiDaNotificare = "0"
private static let DefaultRaggioSismiLievi = "250"
private static let DefaultRaggioSismiForti = "500"
private static let DefaultDistanzaMassima = EQNData.DefaultSettingUserReportNotificationRadius.value
// MARK: - Init
override init() {
let defaults = UserDefaults.standard
self.isAbilitato = defaults.bool(forKey: UserDefaults.AllertaSismicaAbilitato)
self.sismiDaNotificare = defaults.object(forKey: UserDefaults.AllertaSismicaSismiDaNotificare, or: Self.DefaultSismiDaNotificare)
self.raggioSismiLievi = defaults.object(forKey: UserDefaults.AllertaSismicaRaggioSismiLievi, or: Self.DefaultRaggioSismiLievi)
self.raggioSismiForti = defaults.object(forKey: UserDefaults.AllertaSismicaRaggioSismiForti, or: Self.DefaultRaggioSismiForti)
let sharedDefaults = UserDefaults.appGroup
isCriticalAlertsEnabled = sharedDefaults?.bool(forKey: UserDefaults.AllertaSismicaCriticalAlerts) ?? false
}
// MARK: - Public
func saveUserInfo() {
let defaults = UserDefaults.standard
defaults.set(isAbilitato, forKey: UserDefaults.AllertaSismicaAbilitato)
defaults.set(sismiDaNotificare, forKey: UserDefaults.AllertaSismicaSismiDaNotificare)
defaults.set(raggioSismiLievi, forKey: UserDefaults.AllertaSismicaRaggioSismiLievi)
defaults.set(raggioSismiForti, forKey: UserDefaults.AllertaSismicaRaggioSismiForti)
if let sharedDefaults = UserDefaults.appGroup {
sharedDefaults.set(isCriticalAlertsEnabled, forKey: UserDefaults.AllertaSismicaCriticalAlerts)
}
}
@objc class func saveDefaultValues() {
shared.isAbilitato = true
shared.isCriticalAlertsEnabled = false
shared.sismiDaNotificare = Self.DefaultSismiDaNotificare
shared.raggioSismiLievi = Self.DefaultRaggioSismiLievi
shared.raggioSismiForti = Self.DefaultRaggioSismiForti
shared.saveUserInfo()
}
}
@@ -0,0 +1,79 @@
//
// EQNSettingSeismicNetworkNotification.swift
// Earthquake Network
//
// Created by Andrea Busi on 06/06/24.
// Copyright © 2024 Earthquake Network. All rights reserved.
//
import Foundation
@objc
class EQNSettingSeismicNetworkNotification: NSObject {
@objc(sharedInstance)
static let shared = EQNSettingSeismicNetworkNotification()
@objc var isAbilitato: Bool
@objc var magnitudoMinima: String
@objc var distanzaMassima: String
private static let DefaultMagnitudoMinima = EQNData.DefaultFilterMagnitude.value
private static let DefaultDistanzaMassima = EQNData.DefaultFilterRadius.value
// MARK: - Init
override init() {
Self.migrate_v5_8()
let defaults = UserDefaults.standard
self.isAbilitato = defaults.bool(forKey: UserDefaults.NotificheRetiSismicheAbilitato)
self.magnitudoMinima = defaults.object(forKey: UserDefaults.NotificheRetiSismicheMagnitudoMinima, or: Self.DefaultMagnitudoMinima)
self.distanzaMassima = defaults.object(forKey: UserDefaults.NotificheRetiSismicheDistanzaMassima, or: Self.DefaultDistanzaMassima)
}
// MARK: - Public
func saveUserInfo() {
let defaults = UserDefaults.standard
defaults.set(isAbilitato, forKey: UserDefaults.NotificheRetiSismicheAbilitato)
defaults.set(magnitudoMinima, forKey: UserDefaults.NotificheRetiSismicheMagnitudoMinima)
defaults.set(distanzaMassima, forKey: UserDefaults.NotificheRetiSismicheDistanzaMassima)
}
@objc class func saveDefaultValues() {
shared.isAbilitato = true
shared.magnitudoMinima = DefaultMagnitudoMinima
shared.distanzaMassima = DefaultDistanzaMassima
shared.saveUserInfo()
}
// MARK: - Private
private class func migrate_v5_8() {
// migriamo i valori della distanza massima che sono diminuti nella v5.8
// In precedenzai valori ammessi erano: 50, 100, 200, 300, 400, 500, 600, 800, 1000, 2000, 4000, qualsiasi distanza
// ora invece sono solamente 100, 250, 500, 1000
let defaults = UserDefaults.standard
let alreadyMigrated = defaults.bool(forKey: UserDefaults.SettingsSeismicNetworkNotificationMigrationV5_8)
if alreadyMigrated {
return
}
guard let savedDistance = defaults.object(forKey: UserDefaults.NotificheRetiSismicheDistanzaMassima) as? String else {
defaults.set(true, forKey: UserDefaults.SettingsSeismicNetworkNotificationMigrationV5_8)
return
}
let maxDistance = switch savedDistance {
case "50", "100": "100"
case "200", "300": "250"
case "400", "500", "600": "500"
default:
// 800, 1000, 2000, 4000, qualsiasi distanza
"1000"
}
defaults.set(maxDistance, forKey: UserDefaults.NotificheRetiSismicheDistanzaMassima)
defaults.set(true, forKey: UserDefaults.SettingsSeismicNetworkNotificationMigrationV5_8)
}
}
@@ -0,0 +1,75 @@
//
// EQNSettingUserReportNotification.swift
// Earthquake Network
//
// Created by Andrea Busi on 10/06/24.
// Copyright © 2024 Earthquake Network. All rights reserved.
//
import Foundation
@objc
class EQNSettingUserReportNotification: NSObject {
@objc(sharedInstance)
static let shared = EQNSettingUserReportNotification()
@objc var isAbilitato: Bool
@objc var distanzaMassima: String
private static let DefaultDistanzaMassima = EQNData.DefaultSettingUserReportNotificationRadius.value
// MARK: - Init
override init() {
Self.migrate_v5_8()
let defaults = UserDefaults.standard
self.isAbilitato = defaults.bool(forKey: UserDefaults.NotificheSegnalazioniUtenteAbilitato)
self.distanzaMassima = defaults.object(forKey: UserDefaults.NotificheSegnalazioniUtenteDistanzaPosizione, or: Self.DefaultDistanzaMassima)
}
// MARK: - Public
func saveUserInfo() {
let defaults = UserDefaults.standard
defaults.set(isAbilitato, forKey: UserDefaults.NotificheSegnalazioniUtenteAbilitato)
defaults.set(distanzaMassima, forKey: UserDefaults.NotificheSegnalazioniUtenteDistanzaPosizione)
}
@objc class func saveDefaultValues() {
shared.isAbilitato = true
shared.distanzaMassima = DefaultDistanzaMassima
shared.saveUserInfo()
}
// MARK: - Private
private class func migrate_v5_8() {
// migriamo i valori della distanza massima che sono diminuti nella v5.8
// In precedenzai valori ammessi erano: 50, 100, 200, 300, 400, 500, 600, 800, 1000, 2000, 4000, qualsiasi distanza
// ora invece sono solamente 100, 250, 500, 1000
let defaults = UserDefaults.standard
let alreadyMigrated = defaults.bool(forKey: UserDefaults.SettingsUserReportNotificationMigrationV5_8)
if alreadyMigrated {
return
}
guard let savedDistance = defaults.object(forKey: UserDefaults.NotificheSegnalazioniUtenteDistanzaPosizione) as? String else {
defaults.set(true, forKey: UserDefaults.SettingsUserReportNotificationMigrationV5_8)
return
}
let maxDistance = switch savedDistance {
case "50", "100": "100"
case "200", "300": "250"
case "400", "500", "600": "500"
default:
// 800, 1000, 2000, 4000, qualsiasi distanza
"1000"
}
defaults.set(maxDistance, forKey: UserDefaults.NotificheSegnalazioniUtenteDistanzaPosizione)
defaults.set(true, forKey: UserDefaults.SettingsUserReportNotificationMigrationV5_8)
}
}
@@ -5,9 +5,9 @@
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "130",
"green" : "212",
"red" : "148"
"blue" : "196",
"green" : "226",
"red" : "200"
}
},
"idiom" : "universal"
@@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "167",
"green" : "231",
"red" : "255"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
@@ -5,9 +5,9 @@
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.318",
"green" : "0.565",
"red" : "0.000"
"blue" : "35",
"green" : "160",
"red" : "12"
}
},
"idiom" : "universal"
@@ -0,0 +1,23 @@
{
"images" : [
{
"filename" : "navbar-icon-sort.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "navbar-icon-sort@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "navbar-icon-sort@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 118 B

@@ -11,9 +11,6 @@
#import "EQNUser.h"
#import "EQNCalibrazione.h"
#import "EQNRilevamento.h"
#import "EQNNotificheSegnalazioniUtente.h"
#import "EQNNotificheReteSismiche.h"
#import "EQNAllertaSismica.h"
#import <MapKit/MapKit.h>
@implementation EQNGeneratoreURLServer
@@ -127,109 +124,15 @@
+ (NSURL *)urlInvioImpostazioniNotifiche
{
NSString *n_e = [NSString stringWithFormat:@"&n_e=%i",[EQNAllertaSismica sharedInstance].isAbilitato];
NSString *n_m = [NSString stringWithFormat:@"&n_m=%i",[EQNNotificheSegnalazioniUtente sharedInstance].isAbilitato];
NSString *n_o = [NSString stringWithFormat:@"&n_o=%i",[EQNNotificheReteSismiche sharedInstance].isAbilitato];
NSString *n_o_near = [NSString stringWithFormat:@"&n_o_near=%i",[EQNNotificheReteSismiche sharedInstance].isAbilitaVicini];
NSString *n_o_mag = [NSString stringWithFormat:@"&n_o_mag=%@",[EQNNotificheReteSismiche sharedInstance].energiaSisma];
EQNSettingRealTimeAlert *allerta = [EQNSettingRealTimeAlert sharedInstance];
EQNSettingUserReportNotification *utente = [EQNSettingUserReportNotification sharedInstance];
EQNSettingSeismicNetworkNotification *reti = [EQNSettingSeismicNetworkNotification sharedInstance];
CGFloat latitude = [EQNUser defaultUser].lastPosition.coordinate.latitude;
CGFloat longitude = [EQNUser defaultUser].lastPosition.coordinate.longitude;
NSString *n_o_usgs = @"&n_o_usgs=0";
NSString *n_o_emsc = @"&n_o_emsc=0";
NSString *n_o_ingv = @"&n_o_ingv=0";
NSString *n_o_ign = @"&n_o_ign=0";
NSString *n_o_csi = @"&n_o_csi=0";
NSString *n_o_jma = @"&n_o_jma=0";
NSString *n_o_ineter = @"&n_o_ineter=0";
NSString *n_o_ssn = @"&n_o_ssn=0";
NSString *n_o_sgc = @"&n_o_sgc=0";
NSString *n_o_rsn = @"&n_o_rsn=0";
NSString *n_o_csn = @"&n_o_csn=0";
NSString *n_o_funvisis = @"&n_o_funvisis=0";
NSString *n_o_geonet = @"&n_o_geonet=0";
NSString *n_o_inpres = @"&n_o_inpres=0";
NSString *n_o_igepn = @"&n_o_igepn=0";
NSString *n_o_phivolcs = @"&n_o_phivolcs=0";
NSString *n_o_igp = @"&n_o_igp=0";
NSString *n_o_uasd = @"&n_o_uasd=0";
NSString *n_o_rspr = @"&n_o_rspr=0";
NSString *n_o_bdtim = @"&n_o_bdtim=0";
NSString *n_o_ncs = @"&n_o_ncs=0";
NSString *n_o_uao = @"&n_o_uao=0";
for (NSString *ente in [EQNNotificheReteSismiche sharedInstance].listaEnti) {
if ([ente isEqualToString:@"USGS"]) {
n_o_usgs = @"&n_o_usgs=1";
} else if ([ente isEqualToString:@"EMSC"]) {
n_o_emsc = @"&n_o_emsc=1";
} else if ([ente isEqualToString:@"INGV"]) {
n_o_ingv = @"&n_o_ingv=1";
} else if ([ente isEqualToString:@"IGN"]) {
n_o_ign = @"&n_o_ign=1";
} else if ([ente isEqualToString:@"CSI"]) {
n_o_csi = @"&n_o_csi=1";
} else if ([ente isEqualToString:@"JMA"]) {
n_o_jma = @"&n_o_jma=1";
} else if ([ente isEqualToString:@"Ineter"]) {
n_o_ineter = @"&n_o_ineter=1";
} else if ([ente isEqualToString:@"SSN"]) {
n_o_ssn = @"&n_o_ssn=1";
} else if ([ente isEqualToString:@"SGC"]) {
n_o_sgc = @"&n_o_sgc=1";
} else if ([ente isEqualToString:@"RSN"]) {
n_o_rsn = @"&n_o_rsn=1";
} else if ([ente isEqualToString:@"CSN"]) {
n_o_csn = @"&n_o_csn=1";
} else if ([ente isEqualToString:@"FUNVISIS"]) {
n_o_funvisis = @"&n_o_funvisis=1";
} else if ([ente isEqualToString:@"GeoNet"]) {
n_o_geonet = @"&n_o_geonet=1";
} else if ([ente isEqualToString:@"INPRES"]) {
n_o_inpres = @"&n_o_inpres=1";
} else if ([ente isEqualToString:@"IGEPN"]) {
n_o_igepn = @"&n_o_igepn=1";
} else if ([ente isEqualToString:@"PHIVOLCS"]) {
n_o_phivolcs = @"&n_o_phivolcs=1";
} else if ([ente isEqualToString:@"IGP"]) {
n_o_igp = @"&n_o_igp=1";
} else if ([ente isEqualToString:@"UASD"]) {
n_o_uasd = @"&n_o_uasd=1";
} else if ([ente isEqualToString:@"RSPR"]) {
n_o_rspr = @"&n_o_rspr=1";
} else if ([ente isEqualToString:@"BDTIM"]) {
n_o_bdtim = @"&n_o_bdtim=1";
} else if ([ente isEqualToString:@"NCS"]) {
n_o_ncs = @"&n_o_ncs=1";
} else if ([ente isEqualToString:@"XXX"]) {
n_o_uao = @"&n_o_uao=1";
}
}
NSString *enti = [NSString stringWithFormat:@"%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@", n_o_usgs, n_o_emsc, n_o_ingv, n_o_ign, n_o_csi, n_o_jma, n_o_ineter, n_o_ssn, n_o_sgc, n_o_rsn, n_o_csn, n_o_funvisis, n_o_geonet, n_o_inpres, n_o_igepn, n_o_phivolcs, n_o_igp, n_o_uasd, n_o_rspr, n_o_bdtim, n_o_ncs, n_o_uao];
NSString *n_o_strong = [NSString stringWithFormat:@"&n_o_strong=%i",[EQNNotificheReteSismiche sharedInstance].isTerremortiForti];
// andrea.busi - 27/09/2020: manteniamo il controllo su eventuali valori "Qualsiasi intensità/distanza" salvati
// non sembrano servire, perchè i valori sono salvati come 100.000 anche nelle versioni prcedenti alla 2.0
// per sicurezza, però, teniamo i controlli
NSString *n_o_strmag = [NSString stringWithFormat:@"&n_o_strmag=%@", [EQNNotificheReteSismiche sharedInstance].energiaTerremotiForti];
// andrea.busi - 18/05/2023: filtri rimossi, inviamo al WS dei valori fissi
NSString *r_e_mild = @"&r_e_mild=100000";
NSString *r_e_strong = @"&r_e_strong=100000";
NSString *n_e_type = @"&n_e_type=0";
NSString *r_m = [NSString stringWithFormat:@"&r_m=%@", [EQNNotificheSegnalazioniUtente sharedInstance].distanzaPosizione];
if ([[EQNNotificheSegnalazioniUtente sharedInstance].distanzaPosizione isEqualToString:NSLocalizedString(@"radius_any_distance", @"")]) {
r_m = @"&r_m=100000";
}
NSString *r_o = [NSString stringWithFormat:@"&r_o=%@", [EQNNotificheReteSismiche sharedInstance].distanzaPosizione];
if ([[EQNNotificheReteSismiche sharedInstance].distanzaPosizione isEqualToString:NSLocalizedString(@"radius_any_distance", @"")]) {
r_o = @"&r_o=100000";
}
NSString *stringUrl = [NSString stringWithFormat:@"%@?u_id=%@&lat=%f&lon=%f%@%@%@%@%@%@%@%@%@%@%@%@%@", EQNServerUrlUploadSettings, [EQNUser defaultUser].user_ID, [EQNUser defaultUser].lastPosition.coordinate.latitude, [EQNUser defaultUser].lastPosition.coordinate.longitude, n_e, n_m, n_o, n_o_near,n_o_mag, enti, n_o_strong, n_e_type, n_o_strmag, r_e_mild, r_e_strong, r_m, r_o];
NSString *stringUrl = [NSString stringWithFormat:@"%@?u_id=%@&lat=%f&lon=%f&n_e=%i&n_m=%i&n_o=%i&n_o_mag=%@&r_m=%@&r_o=%@", EQNServerUrlUploadSettings, [EQNUser defaultUser].user_ID, latitude, longitude, allerta.isAbilitato, utente.isAbilitato, reti.isAbilitato, reti.magnitudoMinima, utente.distanzaMassima, reti.distanzaMassima ];
NSLog(@"[DEBUG] url %@", stringUrl);
return [NSURL URLWithString:stringUrl];
}
@@ -249,7 +152,7 @@
+ (NSURL *)urlAlertPushTest
{
EQNGenericValue *radius = [EQNData raggioSismaFor:[EQNAllertaSismica sharedInstance].raggioSismiLievi];
EQNGenericValue *radius = [EQNData getSettingSeismicNetworkAlertRadiusForValue:[EQNSettingRealTimeAlert sharedInstance].raggioSismiLievi];
CLLocationCoordinate2D lastPosition = [EQNUser defaultUser].lastPosition.coordinate;
NSString *stringUrl = [NSString stringWithFormat:@"%@?u_id=%@&radius=%@&lat=%f&lon=%f", EQNServerUrlTestAlarm, [EQNUser defaultUser].user_ID, radius.value, lastPosition.latitude, lastPosition.longitude ];
return [NSURL URLWithString:stringUrl];
@@ -134,6 +134,7 @@
case EQNTipoChiamataCalibrazione:
case EQNTipoChiamataImpostazioniNotifiche:
case EQNTipoChiamataAlertSimulator:
case EQNTipoChiamataAlertPushTest:
case EQNTipoChiamataRegisterSubscription:
onSuccess(newStr);
default:

Some files were not shown because too many files have changed in this diff Show More