diff --git a/Client/Application/AppDelegate.swift b/Client/Application/AppDelegate.swift index e500e379c18..6a031f6659d 100644 --- a/Client/Application/AppDelegate.swift +++ b/Client/Application/AppDelegate.swift @@ -238,6 +238,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UIViewControllerRestorati } func application(_ application: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool { + if let profile = profile, let _ = profile.prefs.boolForKey(PrefsKeys.AppExtensionTelemetryOpenUrl) { + profile.prefs.removeObjectForKey(PrefsKeys.AppExtensionTelemetryOpenUrl) + UnifiedTelemetry.recordEvent(category: .appExtensionAction, method: .applicationOpenUrl, object: .url) + } + guard let routerpath = NavigationPath(url: url) else { return false } diff --git a/Client/Telemetry/UnifiedTelemetry.swift b/Client/Telemetry/UnifiedTelemetry.swift index 840b28d1582..9109880f03a 100644 --- a/Client/Telemetry/UnifiedTelemetry.swift +++ b/Client/Telemetry/UnifiedTelemetry.swift @@ -68,22 +68,36 @@ class UnifiedTelemetry { return outputDict } - Telemetry.default.beforeSerializePing(pingType: MobileEventPingBuilder.PingType) { (inputDict) -> [String: Any?] in - var outputDict = inputDict + Telemetry.default.beforeSerializePing(pingType: MobileEventPingBuilder.PingType) { (inputDict) -> [String: Any?] in + var outputDict = inputDict - var settings: [String: String?] = inputDict["settings"] as? [String: String?] ?? [:] + var settings: [String: String?] = inputDict["settings"] as? [String: String?] ?? [:] - let searchEngines = SearchEngines(prefs: profile.prefs, files: profile.files) - settings["defaultSearchEngine"] = searchEngines.defaultEngine.engineID ?? "custom" + let searchEngines = SearchEngines(prefs: profile.prefs, files: profile.files) + settings["defaultSearchEngine"] = searchEngines.defaultEngine.engineID ?? "custom" - if let windowBounds = UIApplication.shared.keyWindow?.bounds { - settings["windowWidth"] = String(describing: windowBounds.width) - settings["windowHeight"] = String(describing: windowBounds.height) - } + if let windowBounds = UIApplication.shared.keyWindow?.bounds { + settings["windowWidth"] = String(describing: windowBounds.width) + settings["windowHeight"] = String(describing: windowBounds.height) + } + + outputDict["settings"] = settings - outputDict["settings"] = settings - return outputDict - } + // App Extension telemetry requires reading events stored in prefs, then clearing them from prefs. + if let extensionEvents = profile.prefs.arrayForKey(PrefsKeys.AppExtensionTelemetryEventArray) as? [[String: String]], + var pingEvents = outputDict["events"] as? [[Any?]] { + profile.prefs.removeObjectForKey(PrefsKeys.AppExtensionTelemetryEventArray) + + extensionEvents.forEach { extensionEvent in + let category = UnifiedTelemetry.EventCategory.appExtensionAction.rawValue + let newEvent = TelemetryEvent(category: category, method: extensionEvent["method"] ?? "", object: extensionEvent["object"] ?? "") + pingEvents.append(newEvent.toArray()) + } + outputDict["events"] = pingEvents + } + + return outputDict + } Telemetry.default.add(pingBuilderType: CorePingBuilder.self) Telemetry.default.add(pingBuilderType: MobileEventPingBuilder.self) @@ -99,6 +113,7 @@ class UnifiedTelemetry { extension UnifiedTelemetry { public enum EventCategory: String { case action = "action" + case appExtensionAction = "app-extension-action" } public enum EventMethod: String { @@ -114,6 +129,7 @@ extension UnifiedTelemetry { case scan = "scan" case tap = "tap" case view = "view" + case applicationOpenUrl = "application-open-url" } public enum EventObject: String { diff --git a/Extensions/ShareTo/SendToDevice.swift b/Extensions/ShareTo/SendToDevice.swift index c5a381c705b..7b252d47807 100644 --- a/Extensions/ShareTo/SendToDevice.swift +++ b/Extensions/ShareTo/SendToDevice.swift @@ -36,6 +36,8 @@ class SendToDevice: ClientPickerViewControllerDelegate, InstructionsViewControll profile.sendItems([item], toClients: clients).uponQueue(.main) { result in profile.shutdown() self.finish() + + addAppExtensionTelemetryEvent(forMethod: "send-to-device") } } diff --git a/Extensions/ShareTo/ShareViewController.swift b/Extensions/ShareTo/ShareViewController.swift index 9c3041af6ae..7ce5a9038b9 100644 --- a/Extensions/ShareTo/ShareViewController.swift +++ b/Extensions/ShareTo/ShareViewController.swift @@ -38,6 +38,16 @@ protocol ShareControllerDelegate: class { func hidePopupWhenShowingAlert() } +// Telemetry events are written to NSUserDefaults, and then the host app reads and clears this list. +func addAppExtensionTelemetryEvent(forMethod method: String) { + let profile = BrowserProfile(localName: "profile") + var events = profile.prefs.arrayForKey(PrefsKeys.AppExtensionTelemetryEventArray) ?? [[String]]() + // Currently, only URL objects are shared. + let event = ["method": method, "object": "url"] + events.append(event) + profile.prefs.setObject(events, forKey: PrefsKeys.AppExtensionTelemetryEventArray) +} + class ShareViewController: UIViewController { private var shareItem: ShareItem? private var viewsShownDuringDoneAnimation = [UIView]() @@ -276,6 +286,8 @@ extension ShareViewController { let profile = BrowserProfile(localName: "profile") _ = profile.bookmarks.shareItem(shareItem).value // Blocks until database has settled profile.shutdown() + + addAppExtensionTelemetryEvent(forMethod: "bookmark-this-page") } finish() @@ -289,6 +301,8 @@ extension ShareViewController { let profile = BrowserProfile(localName: "profile") profile.readingList.createRecordWithURL(shareItem.url, title: shareItem.title ?? "", addedBy: UIDevice.current.name) profile.shutdown() + + addAppExtensionTelemetryEvent(forMethod: "add-to-reading-list") } finish() @@ -307,6 +321,10 @@ extension ShareViewController { @objc func actionOpenInFirefoxNow(gesture: UIGestureRecognizer) { gesture.isEnabled = false + // Telemetry is handled in the app delegate that receives this event. + let profile = BrowserProfile(localName: "profile") + profile.prefs.setBool(true, forKey: PrefsKeys.AppExtensionTelemetryOpenUrl) + func firefoxUrl(_ url: String) -> String { let encoded = url.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.alphanumerics) ?? "" return "firefox://open-url?url=\(encoded)" diff --git a/Shared/Prefs.swift b/Shared/Prefs.swift index 2f89740cfa2..1a3db0a7a52 100644 --- a/Shared/Prefs.swift +++ b/Shared/Prefs.swift @@ -32,7 +32,9 @@ public struct PrefsKeys { public static let KeyCustomSyncAuth = "customSyncAuthServer" public static let KeyCustomSyncWeb = "customSyncWebServer" public static let UseStageServer = "useStageSyncService" - + + public static let AppExtensionTelemetryOpenUrl = "AppExtensionTelemetryOpenUrl" + public static let AppExtensionTelemetryEventArray = "AppExtensionTelemetryEvents" } public struct PrefsDefaults {