|
@@ -15,229 +15,367 @@ struct WidgetAction: Identifiable, Hashable {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-enum WidgetTemplateKind: String, CaseIterable, Codable, Hashable {
|
|
|
|
|
- case quickActions
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
enum WidgetLayoutMode: Hashable {
|
|
enum WidgetLayoutMode: Hashable {
|
|
|
case iconOnly(title: String)
|
|
case iconOnly(title: String)
|
|
|
- case actionsRow(maxActions: Int, columns: Int = 3)
|
|
|
|
|
- case actionsGrid(columns: Int, maxActions: Int)
|
|
|
|
|
|
|
+ case actionsRow
|
|
|
|
|
+ case actionsGrid(columns: Int)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-struct WidgetLayoutConfig: Hashable {
|
|
|
|
|
- let mode: WidgetLayoutMode
|
|
|
|
|
|
|
+struct WidgetVariant: Identifiable, Hashable {
|
|
|
|
|
+ let id: String
|
|
|
|
|
+ let title: String
|
|
|
|
|
+ let size: WidgetSize
|
|
|
let showHeader: Bool
|
|
let showHeader: Bool
|
|
|
|
|
+ let layoutMode: WidgetLayoutMode
|
|
|
|
|
+ let actions: [WidgetAction]
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
enum WidgetTemplates {
|
|
enum WidgetTemplates {
|
|
|
- static func actions(for app: LauncherApp, kind: WidgetTemplateKind = .quickActions) -> [WidgetAction] {
|
|
|
|
|
- guard kind == .quickActions else { return defaultActions(for: app) }
|
|
|
|
|
-
|
|
|
|
|
- let name = app.name.lowercased()
|
|
|
|
|
- let host = app.webURL?.host?.lowercased() ?? ""
|
|
|
|
|
|
|
+ static func variants(for app: LauncherApp) -> [WidgetVariant] {
|
|
|
|
|
+ let base = app.webURL
|
|
|
|
|
|
|
|
- if name.contains("photos") || host.contains("photos.google.com") {
|
|
|
|
|
- // Matches the example UI.
|
|
|
|
|
- return [
|
|
|
|
|
- WidgetAction(id: "photos", title: "Photos", systemImage: "photo.on.rectangle", urlPath: "/"),
|
|
|
|
|
- WidgetAction(id: "albums", title: "Albums", systemImage: "rectangle.stack", urlPath: "/albums"),
|
|
|
|
|
- WidgetAction(id: "explore", title: "Explore", systemImage: "magnifyingglass", urlPath: "/search"),
|
|
|
|
|
- WidgetAction(id: "archive", title: "Archive", systemImage: "archivebox", urlPath: "/archive"),
|
|
|
|
|
- WidgetAction(id: "sharing", title: "Sharing", systemImage: "person.2", urlPath: "/sharing"),
|
|
|
|
|
- WidgetAction(id: "locked", title: "Locked Folder", systemImage: "lock", urlPath: "/lockedfolder"),
|
|
|
|
|
- WidgetAction(id: "favorites", title: "Favorites", systemImage: "star", urlPath: "/favorites"),
|
|
|
|
|
- WidgetAction(id: "trash", title: "Trash", systemImage: "trash", urlPath: "/trash"),
|
|
|
|
|
- ]
|
|
|
|
|
|
|
+ func v(_ id: String, _ title: String, _ size: WidgetSize, _ showHeader: Bool, _ layout: WidgetLayoutMode, _ actions: [WidgetAction]) -> WidgetVariant {
|
|
|
|
|
+ WidgetVariant(id: id, title: title, size: size, showHeader: showHeader, layoutMode: layout, actions: actions)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if name.contains("gmail") || host.contains("mail.google.com") {
|
|
|
|
|
|
|
+ let profile = appProfile(for: app)
|
|
|
|
|
+ switch profile {
|
|
|
|
|
+ case .photos:
|
|
|
return [
|
|
return [
|
|
|
- WidgetAction(id: "compose", title: "Compose", systemImage: "square.and.pencil", urlPath: "/mail/u/0/#inbox?compose=new"),
|
|
|
|
|
- WidgetAction(id: "inbox", title: "Inbox", systemImage: "tray", urlPath: "/mail/u/0/#inbox"),
|
|
|
|
|
- WidgetAction(id: "starred", title: "Starred", systemImage: "star", urlPath: "/mail/u/0/#starred"),
|
|
|
|
|
- WidgetAction(id: "sent", title: "Sent", systemImage: "paperplane", urlPath: "/mail/u/0/#sent"),
|
|
|
|
|
- WidgetAction(id: "drafts", title: "Drafts", systemImage: "doc.plaintext", urlPath: "/mail/u/0/#drafts"),
|
|
|
|
|
- WidgetAction(id: "snoozed", title: "Snoozed", systemImage: "clock", urlPath: "/mail/u/0/#snoozed"),
|
|
|
|
|
- WidgetAction(id: "important", title: "Important", systemImage: "exclamationmark.circle", urlPath: "/mail/u/0/#imp"),
|
|
|
|
|
- WidgetAction(id: "trash", title: "Trash", systemImage: "trash", urlPath: "/mail/u/0/#trash"),
|
|
|
|
|
|
|
+ v("photos_small", "Photo", .small, false, .iconOnly(title: "Photo"), [a("photos", "Photos", "photo.on.rectangle", "/")]),
|
|
|
|
|
+ v("photos_medium", "Google Photo", .medium, false, .iconOnly(title: "Google Photo"), [a("albums", "Albums", "rectangle.stack", "/albums")]),
|
|
|
|
|
+ v("photos_large", "Photos Library", .large, true, .actionsGrid(columns: 2), [
|
|
|
|
|
+ a("photos", "Photos", "photo.on.rectangle", "/"),
|
|
|
|
|
+ a("albums", "Albums", "rectangle.stack", "/albums"),
|
|
|
|
|
+ a("explore", "Explore", "magnifyingglass", "/search"),
|
|
|
|
|
+ a("archive", "Archive", "archivebox", "/archive"),
|
|
|
|
|
+ a("sharing", "Sharing", "person.2", "/sharing"),
|
|
|
|
|
+ a("locked", "Locked Folder", "lock", "/lockedfolder"),
|
|
|
|
|
+ a("favorites", "Favorites", "star", "/favorites"),
|
|
|
|
|
+ a("trash", "Trash", "trash", "/trash"),
|
|
|
|
|
+ ]),
|
|
|
]
|
|
]
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if name.contains("drive") || host.contains("drive.google.com") {
|
|
|
|
|
|
|
+ case .gmail:
|
|
|
return [
|
|
return [
|
|
|
- WidgetAction(id: "myDrive", title: "My Drive", systemImage: "externaldrive", urlPath: "/drive/my-drive"),
|
|
|
|
|
- WidgetAction(id: "recent", title: "Recent", systemImage: "clock.arrow.circlepath", urlPath: "/drive/recent"),
|
|
|
|
|
- WidgetAction(id: "starred", title: "Starred", systemImage: "star", urlPath: "/drive/starred"),
|
|
|
|
|
- WidgetAction(id: "shared", title: "Shared", systemImage: "person.2", urlPath: "/drive/shared-with-me"),
|
|
|
|
|
- WidgetAction(id: "trash", title: "Trash", systemImage: "trash", urlPath: "/drive/trash"),
|
|
|
|
|
- WidgetAction(id: "new", title: "New", systemImage: "plus", urlPath: "/drive/my-drive"),
|
|
|
|
|
|
|
+ v("gmail_small", "Inbox Quick", .small, false, .actionsRow, [
|
|
|
|
|
+ a("compose", "Compose", "square.and.pencil", "/mail/u/0/#inbox?compose=new"),
|
|
|
|
|
+ a("inbox", "Inbox", "tray", "/mail/u/0/#inbox"),
|
|
|
|
|
+ a("starred", "Starred", "star", "/mail/u/0/#starred"),
|
|
|
|
|
+ ]),
|
|
|
|
|
+ v("gmail_medium", "Mailbox", .medium, true, .actionsGrid(columns: 2), [
|
|
|
|
|
+ a("inbox", "Inbox", "tray", "/mail/u/0/#inbox"),
|
|
|
|
|
+ a("important", "Important", "exclamationmark.circle", "/mail/u/0/#imp"),
|
|
|
|
|
+ a("sent", "Sent", "paperplane", "/mail/u/0/#sent"),
|
|
|
|
|
+ a("drafts", "Drafts", "doc.plaintext", "/mail/u/0/#drafts"),
|
|
|
|
|
+ ]),
|
|
|
|
|
+ v("gmail_large", "Gmail Actions", .large, true, .actionsGrid(columns: 2), [
|
|
|
|
|
+ a("compose", "Compose", "square.and.pencil", "/mail/u/0/#inbox?compose=new"),
|
|
|
|
|
+ a("inbox", "Inbox", "tray", "/mail/u/0/#inbox"),
|
|
|
|
|
+ a("starred", "Starred", "star", "/mail/u/0/#starred"),
|
|
|
|
|
+ a("sent", "Sent", "paperplane", "/mail/u/0/#sent"),
|
|
|
|
|
+ a("drafts", "Drafts", "doc.plaintext", "/mail/u/0/#drafts"),
|
|
|
|
|
+ a("snoozed", "Snoozed", "clock", "/mail/u/0/#snoozed"),
|
|
|
|
|
+ a("important", "Important", "exclamationmark.circle", "/mail/u/0/#imp"),
|
|
|
|
|
+ a("trash", "Trash", "trash", "/mail/u/0/#trash"),
|
|
|
|
|
+ ]),
|
|
|
]
|
|
]
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if name.contains("docs") || host.contains("docs.google.com") {
|
|
|
|
|
|
|
+ case .drive:
|
|
|
|
|
+ return contentWorkspaceVariants(prefix: "drive", appName: "Drive", rootPath: "/drive/my-drive", searchPath: "/drive/search")
|
|
|
|
|
+ case .docs:
|
|
|
|
|
+ return contentWorkspaceVariants(prefix: "docs", appName: "Docs", rootPath: "/document/u/0/", searchPath: "/document/u/0/")
|
|
|
|
|
+ case .sheets:
|
|
|
|
|
+ return contentWorkspaceVariants(prefix: "sheets", appName: "Sheets", rootPath: "/spreadsheets/u/0/", searchPath: "/spreadsheets/u/0/")
|
|
|
|
|
+ case .slides:
|
|
|
|
|
+ return contentWorkspaceVariants(prefix: "slides", appName: "Slides", rootPath: "/presentation/u/0/", searchPath: "/presentation/u/0/")
|
|
|
|
|
+ case .forms:
|
|
|
|
|
+ return contentWorkspaceVariants(prefix: "forms", appName: "Forms", rootPath: "/forms/u/0/", searchPath: "/forms/u/0/")
|
|
|
|
|
+ case .calendar:
|
|
|
return [
|
|
return [
|
|
|
- WidgetAction(id: "newDoc", title: "New Doc", systemImage: "doc.badge.plus", urlPath: "/document/u/0/"),
|
|
|
|
|
- WidgetAction(id: "recent", title: "Recent", systemImage: "clock.arrow.circlepath", urlPath: "/document/u/0/"),
|
|
|
|
|
- WidgetAction(id: "templates", title: "Templates", systemImage: "square.grid.2x2", urlPath: "/document/u/0/"),
|
|
|
|
|
- WidgetAction(id: "shared", title: "Shared", systemImage: "person.2", urlPath: "/document/u/0/"),
|
|
|
|
|
|
|
+ v("calendar_small", "Today", .small, false, .actionsRow, [
|
|
|
|
|
+ a("today", "Today", "calendar", "/calendar/u/0/r"),
|
|
|
|
|
+ a("create", "Create", "plus.circle", "/calendar/u/0/r/eventedit"),
|
|
|
|
|
+ ]),
|
|
|
|
|
+ v("calendar_medium", "Schedule", .medium, true, .actionsGrid(columns: 2), [
|
|
|
|
|
+ a("today", "Today", "calendar", "/calendar/u/0/r"),
|
|
|
|
|
+ a("week", "Week", "calendar.badge.clock", "/calendar/u/0/r"),
|
|
|
|
|
+ a("create", "New Event", "plus.circle", "/calendar/u/0/r/eventedit"),
|
|
|
|
|
+ a("tasks", "Tasks", "checkmark.square", "/calendar/u/0/r"),
|
|
|
|
|
+ ]),
|
|
|
]
|
|
]
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if name.contains("sheets") || host.contains("sheets.google.com") {
|
|
|
|
|
|
|
+ case .maps:
|
|
|
return [
|
|
return [
|
|
|
- WidgetAction(id: "newSheet", title: "New Sheet", systemImage: "tablecells.badge.ellipsis", urlPath: "/spreadsheets/u/0/"),
|
|
|
|
|
- WidgetAction(id: "recent", title: "Recent", systemImage: "clock.arrow.circlepath", urlPath: "/spreadsheets/u/0/"),
|
|
|
|
|
- WidgetAction(id: "templates", title: "Templates", systemImage: "square.grid.2x2", urlPath: "/spreadsheets/u/0/"),
|
|
|
|
|
|
|
+ v("maps_small", "Places", .small, false, .actionsRow, [
|
|
|
|
|
+ a("search", "Search", "magnifyingglass", "/"),
|
|
|
|
|
+ a("nearby", "Nearby", "mappin.and.ellipse", "/"),
|
|
|
|
|
+ ]),
|
|
|
|
|
+ v("maps_medium", "Routing", .medium, true, .actionsGrid(columns: 2), [
|
|
|
|
|
+ a("search", "Search", "magnifyingglass", "/"),
|
|
|
|
|
+ a("directions", "Directions", "arrow.triangle.turn.up.right.diamond", "/"),
|
|
|
|
|
+ a("saved", "Saved", "bookmark", "/"),
|
|
|
|
|
+ a("traffic", "Traffic", "car", "/"),
|
|
|
|
|
+ ]),
|
|
|
]
|
|
]
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if name.contains("calendar") || host.contains("calendar.google.com") {
|
|
|
|
|
|
|
+ case .youtube:
|
|
|
return [
|
|
return [
|
|
|
- WidgetAction(id: "today", title: "Today", systemImage: "calendar", urlPath: "/calendar/u/0/r"),
|
|
|
|
|
- WidgetAction(id: "create", title: "Create", systemImage: "plus.circle", urlPath: "/calendar/u/0/r/eventedit"),
|
|
|
|
|
- WidgetAction(id: "schedule", title: "Schedule", systemImage: "list.bullet.rectangle", urlPath: "/calendar/u/0/r"),
|
|
|
|
|
|
|
+ v("youtube_small", "Quick Watch", .small, false, .actionsRow, [
|
|
|
|
|
+ a("home", "Home", "house", "/"),
|
|
|
|
|
+ a("shorts", "Shorts", "bolt", "/shorts"),
|
|
|
|
|
+ a("subs", "Subs", "play.rectangle", "/feed/subscriptions"),
|
|
|
|
|
+ ]),
|
|
|
|
|
+ v("youtube_large", "Library", .large, true, .actionsGrid(columns: 2), [
|
|
|
|
|
+ a("home", "Home", "house", "/"),
|
|
|
|
|
+ a("subscriptions", "Subscriptions", "play.rectangle", "/feed/subscriptions"),
|
|
|
|
|
+ a("library", "Library", "books.vertical", "/feed/library"),
|
|
|
|
|
+ a("history", "History", "clock", "/feed/history"),
|
|
|
|
|
+ a("watchLater", "Watch Later", "bookmark", "/playlist?list=WL"),
|
|
|
|
|
+ a("shorts", "Shorts", "bolt", "/shorts"),
|
|
|
|
|
+ ]),
|
|
|
]
|
|
]
|
|
|
|
|
+ case .translate:
|
|
|
|
|
+ return utilityVariants(prefix: "translate", appName: "Translate", basePath: "/")
|
|
|
|
|
+ case .search:
|
|
|
|
|
+ return utilityVariants(prefix: "search", appName: "Search", basePath: "/")
|
|
|
|
|
+ case .earth:
|
|
|
|
|
+ return utilityVariants(prefix: "earth", appName: "Earth", basePath: "/web/")
|
|
|
|
|
+ case .shopping:
|
|
|
|
|
+ return mediaVariants(prefix: "shopping", appName: "Shopping", basePath: "/")
|
|
|
|
|
+ case .keep:
|
|
|
|
|
+ return utilityVariants(prefix: "keep", appName: "Keep", basePath: "/")
|
|
|
|
|
+ case .travel:
|
|
|
|
|
+ return mediaVariants(prefix: "travel", appName: "Travel", basePath: "/")
|
|
|
|
|
+ case .meet:
|
|
|
|
|
+ return communicationVariants(prefix: "meet", appName: "Meet", basePath: "/")
|
|
|
|
|
+ case .contacts:
|
|
|
|
|
+ return utilityVariants(prefix: "contacts", appName: "Contacts", basePath: "/")
|
|
|
|
|
+ case .blogger:
|
|
|
|
|
+ return mediaVariants(prefix: "blogger", appName: "Blogger", basePath: "/")
|
|
|
|
|
+ case .play:
|
|
|
|
|
+ return mediaVariants(prefix: "play", appName: "Play", basePath: "/")
|
|
|
|
|
+ case .news:
|
|
|
|
|
+ return mediaVariants(prefix: "news", appName: "News", basePath: "/")
|
|
|
|
|
+ case .chat:
|
|
|
|
|
+ return communicationVariants(prefix: "chat", appName: "Chat", basePath: "/")
|
|
|
|
|
+ case .finance:
|
|
|
|
|
+ return mediaVariants(prefix: "finance", appName: "Finance", basePath: "/finance")
|
|
|
|
|
+ case .jamboard:
|
|
|
|
|
+ return communicationVariants(prefix: "jamboard", appName: "Jamboard", basePath: "/")
|
|
|
|
|
+ case .classroom:
|
|
|
|
|
+ return communicationVariants(prefix: "classroom", appName: "Classroom", basePath: "/")
|
|
|
|
|
+ case .artsCulture:
|
|
|
|
|
+ return mediaVariants(prefix: "arts", appName: "Arts & Culture", basePath: "/")
|
|
|
|
|
+ case .voice:
|
|
|
|
|
+ return communicationVariants(prefix: "voice", appName: "Voice", basePath: "/")
|
|
|
|
|
+ case .chromeWebStore:
|
|
|
|
|
+ return mediaVariants(prefix: "webstore", appName: "Web Store", basePath: "/")
|
|
|
|
|
+ case .fi:
|
|
|
|
|
+ return utilityVariants(prefix: "fi", appName: "Fi", basePath: "/")
|
|
|
|
|
+ case .ads:
|
|
|
|
|
+ return utilityVariants(prefix: "ads", appName: "Ads", basePath: "/")
|
|
|
|
|
+ case .remoteDesktop:
|
|
|
|
|
+ return utilityVariants(prefix: "remote", appName: "Remote Desktop", basePath: "/")
|
|
|
|
|
+ case .gemini:
|
|
|
|
|
+ return communicationVariants(prefix: "gemini", appName: "Gemini", basePath: "/")
|
|
|
|
|
+ case .books:
|
|
|
|
|
+ return mediaVariants(prefix: "books", appName: "Books", basePath: "/")
|
|
|
|
|
+ case .unknown:
|
|
|
|
|
+ return utilityVariants(prefix: "generic", appName: app.name, basePath: "/")
|
|
|
}
|
|
}
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- if name.contains("youtube") || host.contains("youtube.com") {
|
|
|
|
|
- return [
|
|
|
|
|
- WidgetAction(id: "home", title: "Home", systemImage: "house", urlPath: "/"),
|
|
|
|
|
- WidgetAction(id: "subscriptions", title: "Subs", systemImage: "play.rectangle", urlPath: "/feed/subscriptions"),
|
|
|
|
|
- WidgetAction(id: "library", title: "Library", systemImage: "books.vertical", urlPath: "/feed/library"),
|
|
|
|
|
- WidgetAction(id: "history", title: "History", systemImage: "clock", urlPath: "/feed/history"),
|
|
|
|
|
- WidgetAction(id: "watchLater", title: "Watch Later", systemImage: "bookmark", urlPath: "/playlist?list=WL"),
|
|
|
|
|
- WidgetAction(id: "shorts", title: "Shorts", systemImage: "bolt", urlPath: "/shorts"),
|
|
|
|
|
- ]
|
|
|
|
|
|
|
+ static func variant(for app: LauncherApp, variantID: String?) -> WidgetVariant {
|
|
|
|
|
+ let variants = variants(for: app)
|
|
|
|
|
+ if let variantID, let match = variants.first(where: { $0.id == variantID }) {
|
|
|
|
|
+ return match
|
|
|
}
|
|
}
|
|
|
|
|
+ return variants.first ?? WidgetVariant(
|
|
|
|
|
+ id: "fallback_small",
|
|
|
|
|
+ title: "Quick Access",
|
|
|
|
|
+ size: .small,
|
|
|
|
|
+ showHeader: false,
|
|
|
|
|
+ layoutMode: .actionsRow,
|
|
|
|
|
+ actions: [a("open", "Open", "arrow.up.right", nil)]
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
- if name.contains("maps") || host.contains("maps.google.com") {
|
|
|
|
|
- return [
|
|
|
|
|
- WidgetAction(id: "search", title: "Search", systemImage: "magnifyingglass", urlPath: "/"),
|
|
|
|
|
- WidgetAction(id: "directions", title: "Directions", systemImage: "arrow.triangle.turn.up.right.diamond", urlPath: "/"),
|
|
|
|
|
- WidgetAction(id: "saved", title: "Saved", systemImage: "bookmark", urlPath: "/"),
|
|
|
|
|
- ]
|
|
|
|
|
- }
|
|
|
|
|
|
|
+private extension WidgetTemplates {
|
|
|
|
|
+ enum AppWidgetProfile {
|
|
|
|
|
+ case photos, gmail, drive, docs, sheets, slides, forms
|
|
|
|
|
+ case calendar, maps, youtube, translate, search, earth, shopping
|
|
|
|
|
+ case keep, travel, meet, contacts, blogger, play, news, chat, finance
|
|
|
|
|
+ case jamboard, classroom, artsCulture, voice, chromeWebStore, fi, ads
|
|
|
|
|
+ case remoteDesktop, gemini, books
|
|
|
|
|
+ case unknown
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- return defaultActions(for: app)
|
|
|
|
|
|
|
+ static func appProfile(for app: LauncherApp) -> AppWidgetProfile {
|
|
|
|
|
+ let host = app.webURL?.host?.lowercased() ?? ""
|
|
|
|
|
+ let name = app.name.lowercased()
|
|
|
|
|
+ if host.contains("photos.google.com") || name.contains("photos") { return .photos }
|
|
|
|
|
+ if host.contains("mail.google.com") || name == "gmail" { return .gmail }
|
|
|
|
|
+ if host.contains("drive.google.com") { return .drive }
|
|
|
|
|
+ if host.contains("docs.google.com") { return .docs }
|
|
|
|
|
+ if host.contains("sheets.google.com") { return .sheets }
|
|
|
|
|
+ if host.contains("slides.google.com") { return .slides }
|
|
|
|
|
+ if host.contains("forms.google.com") { return .forms }
|
|
|
|
|
+ if host.contains("calendar.google.com") { return .calendar }
|
|
|
|
|
+ if host.contains("maps.google.com") { return .maps }
|
|
|
|
|
+ if host.contains("youtube.com") { return .youtube }
|
|
|
|
|
+ if host.contains("translate.google.com") { return .translate }
|
|
|
|
|
+ if host == "www.google.com" || host == "google.com" { return .search }
|
|
|
|
|
+ if host.contains("earth.google.com") { return .earth }
|
|
|
|
|
+ if host.contains("shopping.google.com") { return .shopping }
|
|
|
|
|
+ if host.contains("keep.google.com") { return .keep }
|
|
|
|
|
+ if host.contains("travel.google.com") { return .travel }
|
|
|
|
|
+ if host.contains("meet.google.com") { return .meet }
|
|
|
|
|
+ if host.contains("contacts.google.com") { return .contacts }
|
|
|
|
|
+ if host.contains("blogger.com") { return .blogger }
|
|
|
|
|
+ if host.contains("play.google.com") { return .play }
|
|
|
|
|
+ if host.contains("news.google.com") { return .news }
|
|
|
|
|
+ if host.contains("chat.google.com") { return .chat }
|
|
|
|
|
+ if host.contains("/finance") || name.contains("finance") { return .finance }
|
|
|
|
|
+ if host.contains("jamboard.google.com") { return .jamboard }
|
|
|
|
|
+ if host.contains("classroom.google.com") { return .classroom }
|
|
|
|
|
+ if host.contains("artsandculture.google.com") { return .artsCulture }
|
|
|
|
|
+ if host.contains("voice.google.com") { return .voice }
|
|
|
|
|
+ if host.contains("chrome.google.com") { return .chromeWebStore }
|
|
|
|
|
+ if host.contains("fi.google.com") { return .fi }
|
|
|
|
|
+ if host.contains("ads.google.com") { return .ads }
|
|
|
|
|
+ if host.contains("remotedesktop.google.com") { return .remoteDesktop }
|
|
|
|
|
+ if host.contains("gemini.google.com") { return .gemini }
|
|
|
|
|
+ if host.contains("books.google.com") { return .books }
|
|
|
|
|
+ return .unknown
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- static func defaultActions(for app: LauncherApp) -> [WidgetAction] {
|
|
|
|
|
|
|
+ static func contentWorkspaceVariants(prefix: String, appName: String, rootPath: String, searchPath: String) -> [WidgetVariant] {
|
|
|
[
|
|
[
|
|
|
- WidgetAction(id: "open", title: "Open", systemImage: "arrow.up.right", urlPath: nil),
|
|
|
|
|
- WidgetAction(id: "search", title: "Search", systemImage: "magnifyingglass", urlPath: nil),
|
|
|
|
|
- WidgetAction(id: "favorites", title: "Favorites", systemImage: "star", urlPath: nil),
|
|
|
|
|
|
|
+ WidgetVariant(
|
|
|
|
|
+ id: "\(prefix)_small",
|
|
|
|
|
+ title: appName,
|
|
|
|
|
+ size: .small,
|
|
|
|
|
+ showHeader: false,
|
|
|
|
|
+ layoutMode: .iconOnly(title: appName),
|
|
|
|
|
+ actions: [a("open", "Open", "arrow.up.right", rootPath)]
|
|
|
|
|
+ ),
|
|
|
|
|
+ WidgetVariant(
|
|
|
|
|
+ id: "\(prefix)_medium",
|
|
|
|
|
+ title: "Recent",
|
|
|
|
|
+ size: .medium,
|
|
|
|
|
+ showHeader: true,
|
|
|
|
|
+ layoutMode: .actionsGrid(columns: 2),
|
|
|
|
|
+ actions: [
|
|
|
|
|
+ a("open", "Open", "arrow.up.right", rootPath),
|
|
|
|
|
+ a("search", "Search", "magnifyingglass", searchPath),
|
|
|
|
|
+ a("favorites", "Favorites", "star", rootPath),
|
|
|
|
|
+ a("shared", "Shared", "person.2", rootPath),
|
|
|
|
|
+ ]
|
|
|
|
|
+ ),
|
|
|
|
|
+ WidgetVariant(
|
|
|
|
|
+ id: "\(prefix)_large",
|
|
|
|
|
+ title: "Workspace",
|
|
|
|
|
+ size: .large,
|
|
|
|
|
+ showHeader: true,
|
|
|
|
|
+ layoutMode: .actionsGrid(columns: 2),
|
|
|
|
|
+ actions: [
|
|
|
|
|
+ a("open", "Open", "arrow.up.right", rootPath),
|
|
|
|
|
+ a("search", "Search", "magnifyingglass", searchPath),
|
|
|
|
|
+ a("new", "New", "plus", rootPath),
|
|
|
|
|
+ a("recent", "Recent", "clock.arrow.circlepath", rootPath),
|
|
|
|
|
+ a("starred", "Starred", "star", rootPath),
|
|
|
|
|
+ a("shared", "Shared", "person.2", rootPath),
|
|
|
|
|
+ ]
|
|
|
|
|
+ ),
|
|
|
]
|
|
]
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- static func layout(for app: LauncherApp, size: WidgetSize) -> WidgetLayoutConfig {
|
|
|
|
|
- let name = app.name.lowercased()
|
|
|
|
|
- let host = app.webURL?.host?.lowercased() ?? ""
|
|
|
|
|
- let isPhotos = name.contains("photos") || host.contains("photos.google.com")
|
|
|
|
|
- let isGmail = name.contains("gmail") || host.contains("mail.google.com")
|
|
|
|
|
- let isDrive = name.contains("drive") || host.contains("drive.google.com")
|
|
|
|
|
- let isDocs = name.contains("docs") || host.contains("docs.google.com")
|
|
|
|
|
- let isSheets = name.contains("sheets") || host.contains("sheets.google.com")
|
|
|
|
|
- let isCalendar = name.contains("calendar") || host.contains("calendar.google.com")
|
|
|
|
|
- let isYouTube = name.contains("youtube") || host.contains("youtube.com")
|
|
|
|
|
- let isMaps = name.contains("maps") || host.contains("maps.google.com")
|
|
|
|
|
-
|
|
|
|
|
- if isPhotos {
|
|
|
|
|
- switch size {
|
|
|
|
|
- case .small:
|
|
|
|
|
- return WidgetLayoutConfig(mode: .iconOnly(title: "Photo"), showHeader: false)
|
|
|
|
|
- case .medium:
|
|
|
|
|
- return WidgetLayoutConfig(mode: .iconOnly(title: "Google Photo"), showHeader: false)
|
|
|
|
|
- case .large:
|
|
|
|
|
- return WidgetLayoutConfig(mode: .actionsGrid(columns: 2, maxActions: 8), showHeader: true)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if isGmail {
|
|
|
|
|
- switch size {
|
|
|
|
|
- case .small:
|
|
|
|
|
- return WidgetLayoutConfig(mode: .actionsRow(maxActions: 3), showHeader: false)
|
|
|
|
|
- case .medium:
|
|
|
|
|
- return WidgetLayoutConfig(mode: .actionsGrid(columns: 2, maxActions: 6), showHeader: true)
|
|
|
|
|
- case .large:
|
|
|
|
|
- return WidgetLayoutConfig(mode: .actionsGrid(columns: 3, maxActions: 9), showHeader: true)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if isDrive {
|
|
|
|
|
- switch size {
|
|
|
|
|
- case .small:
|
|
|
|
|
- return WidgetLayoutConfig(mode: .iconOnly(title: "Drive"), showHeader: false)
|
|
|
|
|
- case .medium:
|
|
|
|
|
- return WidgetLayoutConfig(mode: .actionsRow(maxActions: 3), showHeader: true)
|
|
|
|
|
- case .large:
|
|
|
|
|
- return WidgetLayoutConfig(mode: .actionsGrid(columns: 2, maxActions: 6), showHeader: true)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if isDocs {
|
|
|
|
|
- switch size {
|
|
|
|
|
- case .small:
|
|
|
|
|
- return WidgetLayoutConfig(mode: .iconOnly(title: "Docs"), showHeader: false)
|
|
|
|
|
- case .medium:
|
|
|
|
|
- return WidgetLayoutConfig(mode: .actionsGrid(columns: 2, maxActions: 4), showHeader: true)
|
|
|
|
|
- case .large:
|
|
|
|
|
- return WidgetLayoutConfig(mode: .actionsGrid(columns: 3, maxActions: 6), showHeader: true)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if isSheets {
|
|
|
|
|
- switch size {
|
|
|
|
|
- case .small:
|
|
|
|
|
- return WidgetLayoutConfig(mode: .iconOnly(title: "Sheets"), showHeader: false)
|
|
|
|
|
- case .medium:
|
|
|
|
|
- return WidgetLayoutConfig(mode: .actionsRow(maxActions: 3), showHeader: true)
|
|
|
|
|
- case .large:
|
|
|
|
|
- return WidgetLayoutConfig(mode: .actionsGrid(columns: 2, maxActions: 6), showHeader: true)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if isCalendar {
|
|
|
|
|
- switch size {
|
|
|
|
|
- case .small:
|
|
|
|
|
- return WidgetLayoutConfig(mode: .actionsRow(maxActions: 2), showHeader: false)
|
|
|
|
|
- case .medium:
|
|
|
|
|
- return WidgetLayoutConfig(mode: .actionsGrid(columns: 2, maxActions: 3), showHeader: true)
|
|
|
|
|
- case .large:
|
|
|
|
|
- return WidgetLayoutConfig(mode: .actionsGrid(columns: 3, maxActions: 6), showHeader: true)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ static func communicationVariants(prefix: String, appName: String, basePath: String) -> [WidgetVariant] {
|
|
|
|
|
+ [
|
|
|
|
|
+ WidgetVariant(
|
|
|
|
|
+ id: "\(prefix)_small",
|
|
|
|
|
+ title: "Quick",
|
|
|
|
|
+ size: .small,
|
|
|
|
|
+ showHeader: false,
|
|
|
|
|
+ layoutMode: .actionsRow,
|
|
|
|
|
+ actions: [
|
|
|
|
|
+ a("open", "Open", "arrow.up.right", basePath),
|
|
|
|
|
+ a("recent", "Recent", "clock", basePath),
|
|
|
|
|
+ a("contacts", "Contacts", "person.2", basePath),
|
|
|
|
|
+ ]
|
|
|
|
|
+ ),
|
|
|
|
|
+ WidgetVariant(
|
|
|
|
|
+ id: "\(prefix)_large",
|
|
|
|
|
+ title: appName,
|
|
|
|
|
+ size: .large,
|
|
|
|
|
+ showHeader: true,
|
|
|
|
|
+ layoutMode: .actionsGrid(columns: 2),
|
|
|
|
|
+ actions: [
|
|
|
|
|
+ a("open", "Open", "arrow.up.right", basePath),
|
|
|
|
|
+ a("messages", "Messages", "bubble.left.and.bubble.right", basePath),
|
|
|
|
|
+ a("meetings", "Meetings", "video", basePath),
|
|
|
|
|
+ a("contacts", "Contacts", "person.2", basePath),
|
|
|
|
|
+ ]
|
|
|
|
|
+ ),
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- if isYouTube {
|
|
|
|
|
- switch size {
|
|
|
|
|
- case .small:
|
|
|
|
|
- return WidgetLayoutConfig(mode: .iconOnly(title: "YouTube"), showHeader: false)
|
|
|
|
|
- case .medium:
|
|
|
|
|
- return WidgetLayoutConfig(mode: .actionsGrid(columns: 2, maxActions: 4), showHeader: true)
|
|
|
|
|
- case .large:
|
|
|
|
|
- return WidgetLayoutConfig(mode: .actionsGrid(columns: 3, maxActions: 6), showHeader: true)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ static func mediaVariants(prefix: String, appName: String, basePath: String) -> [WidgetVariant] {
|
|
|
|
|
+ [
|
|
|
|
|
+ WidgetVariant(
|
|
|
|
|
+ id: "\(prefix)_small",
|
|
|
|
|
+ title: appName,
|
|
|
|
|
+ size: .small,
|
|
|
|
|
+ showHeader: false,
|
|
|
|
|
+ layoutMode: .iconOnly(title: appName),
|
|
|
|
|
+ actions: [a("open", "Open", "arrow.up.right", basePath)]
|
|
|
|
|
+ ),
|
|
|
|
|
+ WidgetVariant(
|
|
|
|
|
+ id: "\(prefix)_medium",
|
|
|
|
|
+ title: "Browse",
|
|
|
|
|
+ size: .medium,
|
|
|
|
|
+ showHeader: true,
|
|
|
|
|
+ layoutMode: .actionsGrid(columns: 2),
|
|
|
|
|
+ actions: [
|
|
|
|
|
+ a("open", "Open", "arrow.up.right", basePath),
|
|
|
|
|
+ a("trending", "Trending", "chart.line.uptrend.xyaxis", basePath),
|
|
|
|
|
+ a("saved", "Saved", "bookmark", basePath),
|
|
|
|
|
+ a("search", "Search", "magnifyingglass", basePath),
|
|
|
|
|
+ ]
|
|
|
|
|
+ ),
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- if isMaps {
|
|
|
|
|
- switch size {
|
|
|
|
|
- case .small:
|
|
|
|
|
- return WidgetLayoutConfig(mode: .actionsRow(maxActions: 3), showHeader: false)
|
|
|
|
|
- case .medium:
|
|
|
|
|
- return WidgetLayoutConfig(mode: .actionsGrid(columns: 2, maxActions: 4), showHeader: true)
|
|
|
|
|
- case .large:
|
|
|
|
|
- return WidgetLayoutConfig(mode: .actionsGrid(columns: 3, maxActions: 6), showHeader: true)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ static func utilityVariants(prefix: String, appName: String, basePath: String) -> [WidgetVariant] {
|
|
|
|
|
+ [
|
|
|
|
|
+ WidgetVariant(
|
|
|
|
|
+ id: "\(prefix)_small",
|
|
|
|
|
+ title: "Quick Access",
|
|
|
|
|
+ size: .small,
|
|
|
|
|
+ showHeader: false,
|
|
|
|
|
+ layoutMode: .actionsRow,
|
|
|
|
|
+ actions: [
|
|
|
|
|
+ a("open", "Open", "arrow.up.right", basePath),
|
|
|
|
|
+ a("search", "Search", "magnifyingglass", basePath),
|
|
|
|
|
+ a("favorites", "Favorites", "star", basePath),
|
|
|
|
|
+ ]
|
|
|
|
|
+ ),
|
|
|
|
|
+ WidgetVariant(
|
|
|
|
|
+ id: "\(prefix)_medium",
|
|
|
|
|
+ title: appName,
|
|
|
|
|
+ size: .medium,
|
|
|
|
|
+ showHeader: true,
|
|
|
|
|
+ layoutMode: .actionsGrid(columns: 2),
|
|
|
|
|
+ actions: [
|
|
|
|
|
+ a("open", "Open", "arrow.up.right", basePath),
|
|
|
|
|
+ a("search", "Search", "magnifyingglass", basePath),
|
|
|
|
|
+ a("favorites", "Favorites", "star", basePath),
|
|
|
|
|
+ a("recent", "Recent", "clock.arrow.circlepath", basePath),
|
|
|
|
|
+ ]
|
|
|
|
|
+ ),
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- switch size {
|
|
|
|
|
- case .small:
|
|
|
|
|
- return WidgetLayoutConfig(mode: .actionsRow(maxActions: 3), showHeader: false)
|
|
|
|
|
- case .medium:
|
|
|
|
|
- return WidgetLayoutConfig(mode: .actionsGrid(columns: 2, maxActions: 6), showHeader: true)
|
|
|
|
|
- case .large:
|
|
|
|
|
- return WidgetLayoutConfig(mode: .actionsGrid(columns: 3, maxActions: 9), showHeader: true)
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ static func a(_ id: String, _ title: String, _ image: String, _ path: String?) -> WidgetAction {
|
|
|
|
|
+ WidgetAction(id: id, title: title, systemImage: image, urlPath: path)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|