Dactyloidae/mobile/ios/Client/Application/QuickActions.swift
2026-06-26 21:04:09 -07:00

142 lines
6.4 KiB
Swift

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import Foundation
import Storage
import Shared
import XCGLogger
enum ShortcutType: String {
case newTab = "NewTab"
case newPrivateTab = "NewPrivateTab"
case openLastBookmark = "OpenLastBookmark"
case qrCode = "QRCode"
init?(fullType: String) {
guard let last = fullType.components(separatedBy: ".").last else { return nil }
self.init(rawValue: last)
}
var type: String {
return Bundle.main.bundleIdentifier! + ".\(self.rawValue)"
}
}
protocol QuickActionHandlerDelegate {
func handleShortCutItemType(_ type: ShortcutType, userData: [String: NSSecureCoding]?)
}
class QuickActions: NSObject {
fileprivate let log = Logger.browserLogger
static let QuickActionsVersion = "1.0"
static let QuickActionsVersionKey = "dynamicQuickActionsVersion"
static let TabURLKey = "url"
static let TabTitleKey = "title"
fileprivate let lastBookmarkTitle = NSLocalizedString("Open Last Bookmark", tableName: "3DTouchActions", comment: "String describing the action of opening the last added bookmark from the home screen Quick Actions via 3D Touch")
fileprivate let _lastTabTitle = NSLocalizedString("Open Last Tab", tableName: "3DTouchActions", comment: "String describing the action of opening the last tab sent to Firefox from the home screen Quick Actions via 3D Touch")
static var sharedInstance = QuickActions()
var launchedShortcutItem: UIApplicationShortcutItem?
// MARK: Administering Quick Actions
func addDynamicApplicationShortcutItemOfType(_ type: ShortcutType, fromShareItem shareItem: ShareItem, toApplication application: UIApplication) {
var userData = [QuickActions.TabURLKey: shareItem.url]
if let title = shareItem.title {
userData[QuickActions.TabTitleKey] = title
}
QuickActions.sharedInstance.addDynamicApplicationShortcutItemOfType(type, withUserData: userData, toApplication: application)
}
@discardableResult func addDynamicApplicationShortcutItemOfType(_ type: ShortcutType, withUserData userData: [AnyHashable: Any] = [AnyHashable: Any](), toApplication application: UIApplication) -> Bool {
// add the quick actions version so that it is always in the user info
var userData: [AnyHashable: Any] = userData
userData[QuickActions.QuickActionsVersionKey] = QuickActions.QuickActionsVersion
var dynamicShortcutItems = application.shortcutItems ?? [UIApplicationShortcutItem]()
switch type {
case .openLastBookmark:
let openLastBookmarkShortcut = UIMutableApplicationShortcutItem(type: ShortcutType.openLastBookmark.type,
localizedTitle: lastBookmarkTitle,
localizedSubtitle: userData[QuickActions.TabTitleKey] as? String,
icon: UIApplicationShortcutIcon(templateImageName: "quick_action_last_bookmark"),
userInfo: userData
)
if let index = (dynamicShortcutItems.index { $0.type == ShortcutType.openLastBookmark.type }) {
dynamicShortcutItems[index] = openLastBookmarkShortcut
} else {
dynamicShortcutItems.append(openLastBookmarkShortcut)
}
default:
log.warning("Cannot add static shortcut item of type \(type)")
return false
}
application.shortcutItems = dynamicShortcutItems
return true
}
func removeDynamicApplicationShortcutItemOfType(_ type: ShortcutType, fromApplication application: UIApplication) {
guard var dynamicShortcutItems = application.shortcutItems,
let index = (dynamicShortcutItems.index { $0.type == type.type }) else { return }
dynamicShortcutItems.remove(at: index)
application.shortcutItems = dynamicShortcutItems
}
// MARK: Handling Quick Actions
@discardableResult func handleShortCutItem(_ shortcutItem: UIApplicationShortcutItem, withBrowserViewController bvc: BrowserViewController ) -> Bool {
// Verify that the provided `shortcutItem`'s `type` is one handled by the application.
guard let shortCutType = ShortcutType(fullType: shortcutItem.type) else { return false }
DispatchQueue.main.async {
self.handleShortCutItemOfType(shortCutType, userData: shortcutItem.userInfo, browserViewController: bvc)
}
return true
}
fileprivate func handleShortCutItemOfType(_ type: ShortcutType, userData: [String: NSSecureCoding]?, browserViewController: BrowserViewController) {
switch type {
case .newTab:
handleOpenNewTab(withBrowserViewController: browserViewController, isPrivate: false)
case .newPrivateTab:
handleOpenNewTab(withBrowserViewController: browserViewController, isPrivate: true)
// even though we're removing OpenLastTab, it's possible that someone will use an existing last tab quick action to open the app
// the first time after upgrading, so we should still handle it
case .openLastBookmark:
if let urlToOpen = (userData?[QuickActions.TabURLKey] as? String)?.asURL {
handleOpenURL(withBrowserViewController: browserViewController, urlToOpen: urlToOpen)
}
case .qrCode:
handleQRCode(with: browserViewController)
}
}
fileprivate func handleOpenNewTab(withBrowserViewController bvc: BrowserViewController, isPrivate: Bool) {
bvc.openBlankNewTab(focusLocationField: true, isPrivate: isPrivate)
}
fileprivate func handleOpenURL(withBrowserViewController bvc: BrowserViewController, urlToOpen: URL) {
// open bookmark in a non-private browsing tab
bvc.switchToPrivacyMode(isPrivate: false)
// find out if bookmarked URL is currently open
// if so, open to that tab,
// otherwise, create a new tab with the bookmarked URL
bvc.switchToTabForURLOrOpen(urlToOpen, isPrivileged: true)
}
fileprivate func handleQRCode(with vc: QRCodeViewControllerDelegate & UIViewController) {
let qrCodeViewController = QRCodeViewController()
qrCodeViewController.qrCodeDelegate = vc
let controller = UINavigationController(rootViewController: qrCodeViewController)
vc.present(controller, animated: true, completion: nil)
}
}