diff --git a/PocketKit/Sources/Analytics/AppEvents/Listen.swift b/PocketKit/Sources/Analytics/AppEvents/Listen.swift index b2e620589..a674816f5 100644 --- a/PocketKit/Sources/Analytics/AppEvents/Listen.swift +++ b/PocketKit/Sources/Analytics/AppEvents/Listen.swift @@ -50,11 +50,10 @@ public extension Events.Listen { ), extraEntities: [ ContentEntity(url: url), - // MediaPlayerEntity(currentTime: <#T##TimeInterval#>, duration: <#T##TimeInterval#>, ended: <#T##Bool#>, loop: <#T##Bool#>, muted: <#T##Bool#>, paused: <#T##Bool#>, playbackRate: <#T##Double#>, volume: <#T##Int#>) ] ) } - + /// Fired when a User resumes playback of an item /// - Parameters: /// - url: URL of the item @@ -70,15 +69,14 @@ public extension Events.Listen { ), extraEntities: [ ContentEntity(url: url), - // MediaPlayerEntity(currentTime: <#T##TimeInterval#>, duration: <#T##TimeInterval#>, ended: <#T##Bool#>, loop: <#T##Bool#>, muted: <#T##Bool#>, paused: <#T##Bool#>, playbackRate: <#T##Double#>, volume: <#T##Int#>) ] ) } - + /// Fired when a User pauses playback of an item /// - Parameters: /// - url: URL of the item - /// - controlType: How the playback resumed + /// - controlType: How the playback paused /// - Returns: Engagement event static func PausePlayback(url: URL, controlType: ControlType) -> Engagement { return Engagement( @@ -90,8 +88,219 @@ public extension Events.Listen { ), extraEntities: [ ContentEntity(url: url), - // MediaPlayerEntity(currentTime: <#T##TimeInterval#>, duration: <#T##TimeInterval#>, ended: <#T##Bool#>, loop: <#T##Bool#>, muted: <#T##Bool#>, paused: <#T##Bool#>, playbackRate: <#T##Double#>, volume: <#T##Int#>) ] ) } + + /// Fired when a User fast forwards an item + /// - Parameters: + /// - url: URL of the item + /// - controlType: How the playback fast forwarded + /// - Returns: Engagement event + static func FastForward(url: URL, controlType: ControlType) -> Engagement { + return Engagement( + .general, + uiEntity: UiEntity( + .button, + identifier: "listen.playback.fast_forward", + componentDetail: controlType.rawValue + ), + extraEntities: [ + ContentEntity(url: url), + ] + ) + } + + /// Fired when a User rewinds an item + /// - Parameters: + /// - url: URL of the item + /// - controlType: How the playback rewound + /// - Returns: Engagement event + static func Rewind(url: URL, controlType: ControlType) -> Engagement { + return Engagement( + .general, + uiEntity: UiEntity( + .button, + identifier: "listen.playback.rewind", + componentDetail: controlType.rawValue + ), + extraEntities: [ + ContentEntity(url: url), + ] + ) + } + + /// Fired when a User skip next an item + /// - Parameters: + /// - url: URL of the item + /// - controlType: How the playback skipped forward + /// - Returns: Engagement event + static func SkipNext(url: URL, controlType: ControlType) -> Engagement { + return Engagement( + .general, + uiEntity: UiEntity( + .button, + identifier: "listen.playback.skip_next", + componentDetail: controlType.rawValue + ), + extraEntities: [ + ContentEntity(url: url), + ] + ) + } + + /// Fired when a User skips back an item + /// - Parameters: + /// - url: URL of the item + /// - controlType: How the playback skipped backward + /// - Returns: Engagement event + static func SkipBack(url: URL, controlType: ControlType) -> Engagement { + return Engagement( + .general, + uiEntity: UiEntity( + .button, + identifier: "listen.playback.skip_back", + componentDetail: controlType.rawValue + ), + extraEntities: [ + ContentEntity(url: url), + ] + ) + } + + /// Fired when a User sets the speed of an item + /// - Parameters: + /// - url: URL of the item + /// - controlType: How the playback skipped backward + /// - speed: The speed of the playback + /// - Returns: Engagement event + static func SetSpeed(url: URL, controlType: ControlType, speed: Double) -> Engagement { + return Engagement( + .general, + uiEntity: UiEntity( + .button, + identifier: "listen.playback.set_speed", + componentDetail: controlType.rawValue, + value: String(speed) + ), + extraEntities: [ + ContentEntity(url: url), + ] + ) + } + + /// Fired when a User finishes the playback of an item + /// - Parameters: + /// - url: URL of the item + /// - controlType: How the playback was finished + /// - Returns: Engagement event + static func FinsihedListen(url: URL, controlType: ControlType) -> Engagement { + return Engagement( + .general, + uiEntity: UiEntity( + .button, + identifier: "listen.playback.finished", + componentDetail: controlType.rawValue + ), + extraEntities: [ + ContentEntity(url: url), + ] + ) + } + + /// Fired when a User opens listen with the item it was opened to + /// - Parameters: + /// - controlType: How the playback was finished + /// - Returns: Engagement event + static func Opened() -> Engagement { + return Engagement( + .general, + uiEntity: UiEntity( + .button, + identifier: "listen.opened" + ) + ) + } + + /// Fired when a User closes listen + /// - Returns: Engagement event + static func Closed() -> Engagement { + return Engagement( + .general, + uiEntity: UiEntity( + .button, + identifier: "listen.closed" + ) + ) + } + + /// Fired when a user collapses the player into mini mode + /// - Returns: Engagement event + static func Collapsed() -> Engagement { + return Engagement( + .general, + uiEntity: UiEntity( + .button, + identifier: "listen.player.collapsed" + ) + ) + } + + /// Fired when a user closes the mini player + /// - Returns: Engagement event + static func MiniClosed() -> Engagement { + return Engagement( + .general, + uiEntity: UiEntity( + .button, + identifier: "listen.player.mini.close" + ) + ) + } + + /// Fired when a user expands the mini player + /// - Returns: Engagement event + static func Expanded() -> Engagement { + return Engagement( + .general, + uiEntity: UiEntity( + .button, + identifier: "listen.player.expanded" + ) + ) + } + + /// Fired when a user archives an article via listen + /// - Parameters: + /// - url: URL of the item + /// - position: Position in the list + /// - Returns: Engagement event + static func Archived(url: URL, position: Int) -> Engagement { + return Engagement( + uiEntity: UiEntity( + .button, + identifier: "listen.archive", + index: position + ), + extraEntities: [ + ContentEntity(url: url) + ] + ) + } + + /// Fired when a user moves an item from archive to saves via listen + /// - Parameters: + /// - url: URL of the item + /// - position: Position in the list + /// - Returns: Engagement event + static func MoveFromArchiveToSaves(url: URL, position: Int) -> Engagement { + return Engagement( + .save(contentEntity: ContentEntity(url: url)), + uiEntity: UiEntity( + .button, + identifier: "listen.un-archive", + index: position + ) + ) + } } diff --git a/PocketKit/Sources/PocketKit/Listen/Listen.swift b/PocketKit/Sources/PocketKit/Listen/Listen.swift index 610a9d359..8ad0e050c 100644 --- a/PocketKit/Sources/PocketKit/Listen/Listen.swift +++ b/PocketKit/Sources/PocketKit/Listen/Listen.swift @@ -104,6 +104,12 @@ class Listen: NSObject { } extension Listen: PKTListenServiceDelegate { + + static let skippedActions = [ + "listen_opened", + "listen_closed" + ] + func postAction(_ actionName: String, kusari: PKTKusari?, data userInfo: [AnyHashable: Any]) { // cxt_progress will have the play progress // cxt_index will have the album position in the list @@ -115,62 +121,61 @@ extension Listen: PKTListenServiceDelegate { controlType = .system } + if Self.skippedActions.contains(actionName) { + // Skip logging action because we handle it elsewhere. + return + } + + Log.breadcrumb(category: "listen", level: .info, message: "Performed \(actionName) via \(controlType)") + + guard let url = kusari?.album?.givenURL else { + Log.capture(message: "Listen action occurred without an item url") + return + } + // TODO: If we use the Snowplow media element, we need to aquire the playback rate on all actions, which we dont have atm. // See MediaPlayerEntity for the options if actionName == "list_item_impression" { - guard let postiton = userInfo["cxt_index"] as? Int, let url = kusari?.album?.givenURL else { + guard let postiton = userInfo["cxt_index"] as? Int else { return } - self.tracker.track(event: Events.Listen.ItemImpression(url: url, positionInList: postiton)) + tracker.track(event: Events.Listen.ItemImpression(url: url, positionInList: postiton)) } else if actionName == "start_listen" { - guard let url = kusari?.album?.givenURL else { - return - } - self.tracker.track(event: Events.Listen.StartPlayback(url: url, controlType: controlType)) + tracker.track(event: Events.Listen.StartPlayback(url: url, controlType: controlType)) } else if actionName == "resume_listen" { - guard let url = kusari?.album?.givenURL else { - return - } - self.tracker.track(event: Events.Listen.ResumePlayback(url: url, controlType: controlType)) + tracker.track(event: Events.Listen.ResumePlayback(url: url, controlType: controlType)) } else if actionName == "pause_listen" { - guard let url = kusari?.album?.givenURL else { - return - } - self.tracker.track(event: Events.Listen.PausePlayback(url: url, controlType: controlType)) + tracker.track(event: Events.Listen.PausePlayback(url: url, controlType: controlType)) } else if actionName == "fast_forward_listen" { - Log.debug("Listen action: \(actionName)") + tracker.track(event: Events.Listen.FastForward(url: url, controlType: controlType)) } else if actionName == "rewind_listen" { - Log.debug("Listen action: \(actionName)") + tracker.track(event: Events.Listen.Rewind(url: url, controlType: controlType)) } else if actionName == "skip_next_listen" { - Log.debug("Listen action: \(actionName)") + tracker.track(event: Events.Listen.SkipNext(url: url, controlType: controlType)) } else if actionName == "skip_back_listen" { - Log.debug("Listen action: \(actionName)") + tracker.track(event: Events.Listen.SkipBack(url: url, controlType: controlType)) } else if actionName == "set_speed" { - guard let playbackSpeed = userInfo["event"] as? Double, let url = kusari?.album?.givenURL else { + guard let playbackSpeed = userInfo["event"] as? Double else { return } - // user set the speed of the playback - Log.debug("Listen action: \(actionName)") + // TODO: This is not currently being triggered by Listen + tracker.track(event: Events.Listen.SetSpeed(url: url, controlType: controlType, speed: playbackSpeed)) } else if actionName == "reach_end_listen" { - // user finised listening to an article - } else if actionName == "listen_opened" { - Log.debug("Listen action: \(actionName)") - } else if actionName == "listen_closed" { - Log.debug("Listen action: \(actionName)") - } else { - // Note there can be actions for Saving and Archiving, but we listen 😉 for those in their specific callbacks on PKTListenPocketProxy - Log.debug("Listen action: \(actionName)") + tracker.track(event: Events.Listen.FinsihedListen(url: url, controlType: controlType)) } + // Note there can be actions for Saving and Archiving, Closing, but we listen 😉 for those in their specific callbacks on PKTListenPocketProxy } func listenDidPresentPlayer(_ player: PKTListenAudibleQueuePresentationContext) { + tracker.track(event: Events.Listen.Opened()) } func listenDidDismissPlayer(_ player: PKTListenAudibleQueuePresentationContext) { } func listenDidDismiss() { + tracker.track(event: Events.Listen.Closed()) } func itemSessionService() -> PKTItemSessionService? { @@ -178,15 +183,15 @@ extension Listen: PKTListenServiceDelegate { } func listenDidCollapse(intoMiniPlayer player: PKTListenAudibleQueuePresentationContext) { - // TODO: Analytics + tracker.track(event: Events.Listen.Collapsed()) } func listenDidCloseMiniPlayer(_ player: PKTListenAudibleQueuePresentationContext) { - // TODO: Analytics + tracker.track(event: Events.Listen.MiniClosed()) } func listenDidExpand(fromMiniPlayer player: PKTListenAudibleQueuePresentationContext) { - // TODO: Analytics + tracker.track(event: Events.Listen.Expanded()) } func currentColors() -> PKTUITheme { @@ -206,7 +211,12 @@ extension Listen: PKTListenPocketProxy { } self.source.archive(item: savedItem) - // TODO: Track analytics + + guard let postiton = userInfo["cxt_index"] as? Int else { + Log.capture(message: "Tried to archive item from Listen where we dont have the Index, not logging analytics") + return + } + self.tracker.track(event: Events.Listen.Archived(url: savedItem.url, position: postiton)) } /// User clicked save in the listen controls @@ -219,7 +229,12 @@ extension Listen: PKTListenPocketProxy { return } _ = self.source.save(url: url) - // TODO: Track analytics + + guard let postiton = userInfo["cxt_index"] as? Int else { + Log.capture(message: "Tried to add item from Listen where we dont have the Index, not logging analytics") + return + } + self.tracker.track(event: Events.Listen.MoveFromArchiveToSaves(url: url, position: postiton)) } /// TODO: Ask nicole