浏览代码

hack notification status into Connectivity view (#1428)

* initial draft for notification status hack in Connectivity view

* add new translations to en Localizable.strings before we add them to Android string resources

* fix semaphore

* remove comment

* fix lint: use syntactic sugar

* implement delayed notification status in Connectivity view

* use red dot if notifications are delayed for more than 1 h

* take plurals into account for delayed notification status

* move notification-stats to 'Incoming Messages' area for less cluttering

* add 'gray dot' if notifications are disabled

* only track events that really happen in the background usually

* simplify timestamp count check

* streamline wording for notification stats

Co-authored-by: B. Petersen <r10s@b44t.com>
cyBerta 3 年之前
父节点
当前提交
7fdef4cac5

+ 14 - 0
deltachat-ios/AppDelegate.swift

@@ -536,6 +536,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
     private func increaseDebugCounter(_ name: String) {
         let nowDate = Date()
         let nowTimestamp = Double(nowDate.timeIntervalSince1970)
+        // Values calculated for debug log view
         let startTimestamp = UserDefaults.standard.double(forKey: name + "-start")
         if nowTimestamp > startTimestamp + 60*60*24 {
             let cal: Calendar = Calendar(identifier: .gregorian)
@@ -547,6 +548,19 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         let cnt = UserDefaults.standard.integer(forKey: name + "-count")
         UserDefaults.standard.set(cnt + 1, forKey: name + "-count")
         UserDefaults.standard.set(nowTimestamp, forKey: name + "-last")
+
+        // Values calculated for connectivity view
+        if name == "notify-remote-receive" || name == "notify-local-wakeup" {
+            let timestamps = UserDefaults.standard.array(forKey: Constants.Keys.notificationTimestamps)
+            var slidingTimeframe: [Double]
+            if timestamps != nil, let timestamps = timestamps as? [Double] {
+                slidingTimeframe = timestamps.filter({ nowTimestamp < $0 + 60 * 60 * 24 })
+            } else {
+                slidingTimeframe = [Double]()
+            }
+            slidingTimeframe.append(nowTimestamp)
+            UserDefaults.standard.set(slidingTimeframe, forKey: Constants.Keys.notificationTimestamps)
+        }
     }
 
     private func setStockTranslations() {

+ 89 - 1
deltachat-ios/Controller/ConnectivityViewController.swift

@@ -41,10 +41,88 @@ class ConnectivityViewController: WebViewViewController {
         }
     }
 
+    // this method needs to be run from a background thread
+    private func getNotificationStatus() -> String {
+        let title = " <b>" + String.localized("pref_notifications") + ":</b> "
+        let notificationsEnabledInDC = !UserDefaults.standard.bool(forKey: "notifications_disabled")
+        var notificationsEnabledInSystem = false
+        let semaphore = DispatchSemaphore(value: 0)
+        DispatchQueue.global(qos: .userInitiated).async {
+            NotificationManager.notificationEnabledInSystem { enabled in
+                notificationsEnabledInSystem = enabled
+                semaphore.signal()
+            }
+        }
+        if semaphore.wait(timeout: .now() + 1) == .timedOut {
+            return "<span class=\"red dot\"></span>"
+                .appending(title)
+                .appending("Timeout Error")
+        }
+
+        if !notificationsEnabledInDC {
+            return "<span class=\"disabled dot\"></span>"
+                .appending(title)
+                .appending(String.localized("notifications_disabled_dc"))
+        }
+
+        if !notificationsEnabledInSystem {
+            return "<span class=\"disabled dot\"></span>"
+                .appending(title)
+                .appending(String.localized("notifications_disabled"))
+        }
+
+        let timestamps = UserDefaults.standard.array(forKey: Constants.Keys.notificationTimestamps) as? [Double]
+        guard let timestamps = timestamps else {
+            return "<span class=\"yellow dot\"></span>"
+                .appending(title)
+                .appending(String.localized("no_data"))
+        }
+
+        if timestamps.isEmpty || timestamps.count == 1 {
+            // FIXME: for timestamp == 1, that is just okay if the timestamp is not too old
+            return "<span class=\"red dot\"></span>"
+                .appending(title)
+                .appending(String.localized("notifications_not_working"))
+        }
+
+        var timestampDeltas: Double = 0
+        for (index, element) in timestamps.enumerated() where index > 0 {
+            let diff = element - timestamps[index - 1]
+            timestampDeltas += diff
+        }
+
+        let averageDelta = timestampDeltas / Double(timestamps.count - 1)
+        let lastWakeup = DateUtils.getExtendedRelativeTimeSpanString(timeStamp: timestamps.last!)
+
+        if Int(averageDelta / Double(60 * 60)) > 1 {
+            // more than 1 hour in average
+            return "<span class=\"red dot\"></span>"
+                .appending(title)
+                .appending(String.localized(stringID: "notifications_stats_hours_delayed", count: Int(averageDelta / Double(60 * 60))))
+                .appending(" ")
+                .appending(String.localizedStringWithFormat(String.localized("notifications_stats_last_wakeup"), lastWakeup))
+        }
+
+        if averageDelta / Double(60 * 20) > 1 {
+            // more than 20 minutes in average
+            return  "<span class=\"yellow dot\"></span>"
+                .appending(title)
+                .appending(String.localized(stringID: "notifications_stats_minutes_delayed", count: Int(averageDelta / 60)))
+                .appending(" ")
+                .appending(String.localizedStringWithFormat(String.localized("notifications_stats_last_wakeup"), lastWakeup))
+        }
+
+        return  "<span class=\"green dot\"></span>"
+            .appending(title)
+            .appending(String.localized(stringID: "notifications_stats_minutes", count: Int(averageDelta / 60)))
+            .appending(" ")
+            .appending(String.localizedStringWithFormat(String.localized("notifications_stats_last_wakeup"), lastWakeup))
+    }
+
     private func loadHtml() {
         DispatchQueue.global(qos: .userInitiated).async { [weak self] in
             guard let self = self else { return }
-            let html = self.dcContext.getConnectivityHtml()
+            var html = self.dcContext.getConnectivityHtml()
                 .replacingOccurrences(of: "</style>", with:
                     """
                     body {
@@ -54,6 +132,10 @@ class ConnectivityViewController: WebViewViewController {
                         -webkit-text-size-adjust: none;
                     }
 
+                    .disabled {
+                        background-color: #aaaaaa;
+                    }
+
                     @media (prefers-color-scheme: dark) {
                       body {
                         background-color: black !important;
@@ -62,6 +144,12 @@ class ConnectivityViewController: WebViewViewController {
                     }
                     </style>
                     """)
+
+            let notificationStatus = self.getNotificationStatus()
+            if let range = html.range(of: "</ul>") {
+                html = html.replacingCharacters(in: range, with: "<li>" + notificationStatus + "</li></ul>")
+            }
+
             DispatchQueue.main.async {
                 self.webView.loadHTMLString(html, baseURL: nil)
             }

+ 1 - 0
deltachat-ios/Helper/Constants.swift

@@ -11,6 +11,7 @@ struct Constants {
         static let deltachatImapPasswordKey = "__DELTACHAT_IMAP_PASSWORD_KEY__"
         static let lastSelectedAccountKey = "__DELTACHAT_LAST_SELECTED_ACCOUNT_KEY__"
         static let backgroundImageUrl = "__BACKGROUND_IMAGE_URL__"
+        static let notificationTimestamps = "__NOTIFICATION_TIMESTAMPS__"
     }
 
     static let backgroundImageName = "BACKGROUND_IMAGE"

+ 6 - 0
deltachat-ios/Helper/NotificationManager.swift

@@ -35,6 +35,12 @@ public class NotificationManager {
         }
     }
 
+    public static func notificationEnabledInSystem(completionHandler: @escaping (Bool) -> Void) {
+        UNUserNotificationCenter.current().getNotificationSettings { settings in
+            return completionHandler(settings.authorizationStatus != .denied)
+        }
+    }
+
     public static func removeAllNotifications() {
         let nc = UNUserNotificationCenter.current()
         nc.removeAllDeliveredNotifications()

+ 8 - 0
deltachat-ios/en.lproj/Localizable.strings

@@ -859,3 +859,11 @@
 
 "perm_ios_explain_access_to_camera_denied" = "To take photos, capture videos or use the QR-Code scanner, open the system settings and enable \"Camera\".";
 "open_settings" = "Open Settings";
+"no_data" = "No data";
+"notifications_disabled_dc" = "Disabled in Delta Chat.";
+"notifications_disabled" = "Disabled in system settings.";
+"notifications_not_working" = "No notifications received in last 24 hours.";
+"notifications_stats_minutes" = "In average every %1$d minutes.";
+"notifications_stats_minutes_delayed" = "Delayed. In average every %1$d minutes.";
+"notifications_stats_hours_delayed" = "Delayed. In average every %1$d hours.";
+"notifications_stats_last_wakeup" = "Last wakeup: %1$@";

+ 8 - 0
scripts/untranslated.xml

@@ -12,4 +12,12 @@
     
     <string name="perm_ios_explain_access_to_camera_denied">To take photos, capture videos or use the QR-Code scanner, open the system settings and enable \"Camera\".</string>
     <string name="open_settings">Open Settings</string>
+    <string name="no_data">No data</string>
+    <string name="notifications_disabled_dc">Disabled in Delta Chat.</string>
+    <string name="notifications_disabled">Disabled in system settings.</string>
+    <string name="notifications_not_working">No notifications received in last 24 hours.</string>
+    <string name="notifications_stats_minutes">In average every %1$d minutes.</string>
+    <string name="notifications_stats_minutes_delayed">Delayed. In average every %1$d minutes.</string>
+    <string name="notifications_stats_hours_delayed">Delayed. In average every %1$d hours.</string>
+    <string name="notifications_stats_last_wakeup">Last wakeup: %1$s</string>
 </resources>