Add NSUserActivity for reading articles

This commit is contained in:
Maurice Parker 2019-08-24 14:57:51 -05:00
parent 1ba60e2846
commit 5b1c0485c5
7 changed files with 130 additions and 31 deletions

View File

@ -47,6 +47,8 @@
5183CCED22711DCE0010922C /* Settings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5183CCEC22711DCE0010922C /* Settings.storyboard */; };
5183CCEF227125970010922C /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCEE227125970010922C /* SettingsViewController.swift */; };
51934CCB230F599B006127BE /* ThemedNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51934CC1230F5963006127BE /* ThemedNavigationController.swift */; };
51934CCE2310792F006127BE /* ActivityFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51934CCD2310792F006127BE /* ActivityFactory.swift */; };
51934CD023108953006127BE /* ActivityID.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51934CCF23108953006127BE /* ActivityID.swift */; };
519B8D332143397200FA689C /* SharingServiceDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519B8D322143397200FA689C /* SharingServiceDelegate.swift */; };
519E743D22C663F900A78E47 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519E743422C663F900A78E47 /* SceneDelegate.swift */; };
51C451A9226377C200C03939 /* ArticlesDatabase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8407167F2262A61100344432 /* ArticlesDatabase.framework */; };
@ -701,6 +703,8 @@
5183CCEC22711DCE0010922C /* Settings.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Settings.storyboard; sourceTree = "<group>"; };
5183CCEE227125970010922C /* SettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = "<group>"; };
51934CC1230F5963006127BE /* ThemedNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemedNavigationController.swift; sourceTree = "<group>"; };
51934CCD2310792F006127BE /* ActivityFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityFactory.swift; sourceTree = "<group>"; };
51934CCF23108953006127BE /* ActivityID.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityID.swift; sourceTree = "<group>"; };
5194B5ED22B6965300144881 /* SettingsSubscriptionsImportDocumentPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSubscriptionsImportDocumentPickerView.swift; sourceTree = "<group>"; };
5194B5F122B69FCC00144881 /* SettingsSubscriptionsExportDocumentPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSubscriptionsExportDocumentPickerView.swift; sourceTree = "<group>"; };
519B8D322143397200FA689C /* SharingServiceDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharingServiceDelegate.swift; sourceTree = "<group>"; };
@ -1084,6 +1088,15 @@
path = Settings;
sourceTree = "<group>";
};
51934CCC231078DC006127BE /* Activity */ = {
isa = PBXGroup;
children = (
51934CCD2310792F006127BE /* ActivityFactory.swift */,
51934CCF23108953006127BE /* ActivityID.swift */,
);
path = Activity;
sourceTree = "<group>";
};
5194B5E222B693EC00144881 /* Wrappers */ = {
isa = PBXGroup;
children = (
@ -1630,6 +1643,7 @@
842E45CD1ED8C308000A8B52 /* AppNotifications.swift */,
51C452AD2265102800C03939 /* Timeline */,
84702AB31FA27AE8006B8943 /* Commands */,
51934CCC231078DC006127BE /* Activity */,
51C452A822650DA100C03939 /* Article Rendering */,
849A97861ED9ECEF007D329B /* Article Styles */,
84DAEE201F86CAE00058304B /* Importers */,
@ -2434,6 +2448,7 @@
51C452762265091600C03939 /* MasterTimelineViewController.swift in Sources */,
5183CCE9226F68D90010922C /* AccountRefreshTimer.swift in Sources */,
51C452882265093600C03939 /* AddFeedViewController.swift in Sources */,
51934CCE2310792F006127BE /* ActivityFactory.swift in Sources */,
DF999FF722B5AEFA0064B687 /* SafariView.swift in Sources */,
51C4529B22650A1000C03939 /* FaviconDownloader.swift in Sources */,
84DEE56622C32CA4005FC42C /* SmartFeedDelegate.swift in Sources */,
@ -2445,6 +2460,7 @@
51E595AD228E1C2100FCC42B /* AddAccountViewController.swift in Sources */,
51C452A322650A1E00C03939 /* HTMLMetadataDownloader.swift in Sources */,
51C4528D2265095F00C03939 /* AddFolderViewController.swift in Sources */,
51934CD023108953006127BE /* ActivityID.swift in Sources */,
51C452782265091600C03939 /* MasterTimelineCellData.swift in Sources */,
51C45259226508D300C03939 /* AppDefaults.swift in Sources */,
51C45293226509C800C03939 /* StarredFeedDelegate.swift in Sources */,

View File

@ -0,0 +1,50 @@
//
// ActivityFactory.swift
// NetNewsWire-iOS
//
// Created by Maurice Parker on 8/23/19.
// Copyright © 2019 Ranchero Software. All rights reserved.
//
import Foundation
import CoreSpotlight
import CoreServices
import Articles
class ActivityFactory {
static func make(_ article: Article) -> NSUserActivity {
let activity = NSUserActivity(activityType: "com.ranchero.NetNewsWire.ReadArticle")
activity.title = article.title
let feedNameKeywords = article.feed?.nameForDisplay.components(separatedBy: " ").filter { $0.count > 2 } ?? []
let articleTitleKeywords = article.title?.components(separatedBy: " ").filter { $0.count > 2 } ?? []
let keywords = feedNameKeywords + articleTitleKeywords
activity.keywords = Set(keywords)
activity.userInfo = [
ActivityID.accountID.rawValue: article.accountID,
ActivityID.feedID.rawValue: article.feedID,
ActivityID.articleID.rawValue: article.articleID
]
activity.isEligibleForSearch = true
activity.isEligibleForPrediction = false
activity.isEligibleForHandoff = true
// CoreSpotlight
let attributeSet = CSSearchableItemAttributeSet(itemContentType: kUTTypeCompositeContent as String)
attributeSet.title = article.title
attributeSet.contentDescription = article.summary
attributeSet.keywords = keywords
if let image = article.avatarImage() {
attributeSet.thumbnailData = image.pngData()
}
activity.contentAttributeSet = attributeSet
return activity
}
}

View File

@ -0,0 +1,15 @@
//
// ActivityID.swift
// NetNewsWire-iOS
//
// Created by Maurice Parker on 8/23/19.
// Copyright © 2019 Ranchero Software. All rights reserved.
//
import Foundation
enum ActivityID: String {
case accountID = "accountID"
case feedID = "feedID"
case articleID = "articleID"
}

View File

@ -7,6 +7,7 @@
//
import Foundation
import RSCore
import Articles
import Account
@ -56,4 +57,30 @@ extension Article {
var logicalDatePublished: Date {
return datePublished ?? dateModified ?? status.dateArrived
}
func avatarImage() -> RSImage? {
if let authors = authors {
for author in authors {
if let image = appDelegate.authorAvatarDownloader.image(for: author) {
return image
}
}
}
guard let feed = feed else {
return nil
}
let feedIconImage = appDelegate.feedIconDownloader.icon(for: feed)
if feedIconImage != nil {
return feedIconImage
}
if let faviconImage = appDelegate.faviconDownloader.faviconAsAvatar(for: feed) {
return faviconImage
}
return FaviconGenerator.favicon(feed)
}
}

View File

@ -210,6 +210,8 @@ class AppCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
}
}
}
private(set) var readActivity: NSUserActivity? = nil
override init() {
super.init()
@ -508,7 +510,8 @@ class AppCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
func selectArticle(_ indexPath: IndexPath?) {
currentArticleIndexPath = indexPath
updateReadArticleUserActivity()
if indexPath == nil {
if !rootSplitViewController.isCollapsed {
let systemMessageViewController = UIStoryboard.main.instantiateController(ofType: SystemMessageViewController.self)
@ -1168,4 +1171,16 @@ private extension AppCoordinator {
}
// MARK: NSUserActivity
func updateReadArticleUserActivity() {
readActivity?.invalidate()
readActivity = nil
guard let article = currentArticle else { return }
readActivity = ActivityFactory.make(article)
readActivity?.becomeCurrent()
}
}

View File

@ -463,39 +463,11 @@ private extension MasterTimelineViewController {
}
func avatarFor(_ article: Article) -> UIImage? {
func avatarFor(_ article: Article) -> RSImage? {
if !coordinator.showAvatars {
return nil
}
if let authors = article.authors {
for author in authors {
if let image = avatarForAuthor(author) {
return image
}
}
}
guard let feed = article.feed else {
return nil
}
let feedIconImage = appDelegate.feedIconDownloader.icon(for: feed)
if feedIconImage != nil {
return feedIconImage
}
if let feed = article.feed, let faviconImage = appDelegate.faviconDownloader.faviconAsAvatar(for: feed) {
return faviconImage
}
return FaviconGenerator.favicon(feed)
}
func avatarForAuthor(_ author: Author) -> UIImage? {
return appDelegate.authorAvatarDownloader.image(for: author)
return article.avatarImage()
}
func featuredImageFor(_ article: Article) -> UIImage? {

View File

@ -47,6 +47,10 @@
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSUserActivityTypes</key>
<array>
<string>com.ranchero.NetNewsWire.ReadArticle</string>
</array>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>