mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-09 09:18:42 +09:00
Dactyloidae iOS initial commit
This commit is contained in:
parent
daa6179d22
commit
7154a0497e
2123 changed files with 197052 additions and 0 deletions
148
mobile/ios/Client/Frontend/Share/ShareExtensionHelper.swift
Normal file
148
mobile/ios/Client/Frontend/Share/ShareExtensionHelper.swift
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
/* 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 Shared
|
||||
import OnePasswordExtension
|
||||
|
||||
private let log = Logger.browserLogger
|
||||
|
||||
class ShareExtensionHelper: NSObject {
|
||||
fileprivate weak var selectedTab: Tab?
|
||||
|
||||
fileprivate let selectedURL: URL
|
||||
fileprivate var onePasswordExtensionItem: NSExtensionItem!
|
||||
fileprivate let browserFillIdentifier = "org.appextension.fill-browser-action"
|
||||
|
||||
init(url: URL, tab: Tab?) {
|
||||
self.selectedURL = tab?.canonicalURL?.displayURL ?? url
|
||||
self.selectedTab = tab
|
||||
}
|
||||
|
||||
func createActivityViewController(_ completionHandler: @escaping (_ completed: Bool, _ activityType: String?) -> Void) -> UIActivityViewController {
|
||||
var activityItems = [AnyObject]()
|
||||
|
||||
let printInfo = UIPrintInfo(dictionary: nil)
|
||||
|
||||
let absoluteString = selectedTab?.url?.absoluteString ?? selectedURL.absoluteString
|
||||
printInfo.jobName = absoluteString
|
||||
printInfo.outputType = .general
|
||||
activityItems.append(printInfo)
|
||||
|
||||
if let tab = selectedTab {
|
||||
activityItems.append(TabPrintPageRenderer(tab: tab))
|
||||
}
|
||||
|
||||
if let title = selectedTab?.title {
|
||||
activityItems.append(TitleActivityItemProvider(title: title))
|
||||
}
|
||||
activityItems.append(self)
|
||||
|
||||
let activityViewController = UIActivityViewController(activityItems: activityItems, applicationActivities: nil)
|
||||
|
||||
// Hide 'Add to Reading List' which currently uses Safari.
|
||||
// We would also hide View Later, if possible, but the exclusion list doesn't currently support
|
||||
// third-party activity types (rdar://19430419).
|
||||
activityViewController.excludedActivityTypes = [
|
||||
UIActivityType.addToReadingList,
|
||||
]
|
||||
|
||||
// This needs to be ready by the time the share menu has been displayed and
|
||||
// activityViewController(activityViewController:, activityType:) is called,
|
||||
// which is after the user taps the button. So a million cycles away.
|
||||
findLoginExtensionItem()
|
||||
|
||||
activityViewController.completionWithItemsHandler = { activityType, completed, returnedItems, activityError in
|
||||
if !completed {
|
||||
completionHandler(completed, activityType.map { $0.rawValue })
|
||||
return
|
||||
}
|
||||
// Bug 1392418 - When copying a url using the share extension there are 2 urls in the pasteboard.
|
||||
// Make sure the pasteboard only has one url.
|
||||
if let url = UIPasteboard.general.urls?.first {
|
||||
UIPasteboard.general.urls = [url]
|
||||
}
|
||||
|
||||
if self.isPasswordManagerActivityType(activityType.map { $0.rawValue }) {
|
||||
if let logins = returnedItems {
|
||||
self.fillPasswords(logins as [AnyObject])
|
||||
}
|
||||
}
|
||||
|
||||
completionHandler(completed, activityType.map { $0.rawValue })
|
||||
}
|
||||
return activityViewController
|
||||
}
|
||||
}
|
||||
|
||||
extension ShareExtensionHelper: UIActivityItemSource {
|
||||
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
|
||||
return selectedURL
|
||||
}
|
||||
|
||||
// IMPORTANT: This method needs Swift compiler optimization DISABLED to prevent a nasty
|
||||
// crash from happening in release builds. It seems as though the check for `nil` may
|
||||
// get removed by the optimizer which leads to a crash when that happens.
|
||||
@_semantics("optimize.sil.never") func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivityType) -> Any? {
|
||||
// activityType actually is nil sometimes (in the simulator at least)
|
||||
if activityType != nil && isPasswordManagerActivityType(activityType.rawValue) {
|
||||
return onePasswordExtensionItem
|
||||
} else {
|
||||
// Return the URL for the selected tab. If we are in reader view then decode
|
||||
// it so that we copy the original and not the internal localhost one.
|
||||
return selectedURL.isReaderModeURL ? selectedURL.decodeReaderModeURL : selectedURL
|
||||
}
|
||||
}
|
||||
|
||||
func activityViewController(_ activityViewController: UIActivityViewController, dataTypeIdentifierForActivityType activityType: UIActivityType?) -> String {
|
||||
if let type = activityType, isPasswordManagerActivityType(type.rawValue) {
|
||||
return browserFillIdentifier
|
||||
}
|
||||
return activityType == nil ? browserFillIdentifier : kUTTypeURL as String
|
||||
}
|
||||
}
|
||||
|
||||
private extension ShareExtensionHelper {
|
||||
|
||||
func isPasswordManagerActivityType(_ activityType: String?) -> Bool {
|
||||
// A 'password' substring covers the most cases, such as pwsafe and 1Password.
|
||||
// com.agilebits.onepassword-ios.extension
|
||||
// com.app77.ios.pwsafe2.find-login-action-password-actionExtension
|
||||
// If your extension's bundle identifier does not contain "password", simply submit a pull request by adding your bundle identifier.
|
||||
return (activityType?.range(of: "password") != nil)
|
||||
|| (activityType == "com.lastpass.ilastpass.LastPassExt")
|
||||
|| (activityType == "in.sinew.Walletx.WalletxExt")
|
||||
|| (activityType == "com.8bit.bitwarden.find-login-action-extension")
|
||||
|
||||
}
|
||||
|
||||
func findLoginExtensionItem() {
|
||||
guard let selectedWebView = selectedTab?.webView else {
|
||||
return
|
||||
}
|
||||
|
||||
// Add 1Password to share sheet
|
||||
OnePasswordExtension.shared().createExtensionItem(forWebView: selectedWebView, completion: {(extensionItem, error) -> Void in
|
||||
if extensionItem == nil {
|
||||
log.error("Failed to create the password manager extension item: \(error.debugDescription).")
|
||||
return
|
||||
}
|
||||
|
||||
// Set the 1Password extension item property
|
||||
self.onePasswordExtensionItem = extensionItem
|
||||
})
|
||||
}
|
||||
|
||||
func fillPasswords(_ returnedItems: [AnyObject]) {
|
||||
guard let selectedWebView = selectedTab?.webView else {
|
||||
return
|
||||
}
|
||||
|
||||
OnePasswordExtension.shared().fillReturnedItems(returnedItems, intoWebView: selectedWebView, completion: { (success, returnedItemsError) -> Void in
|
||||
if !success {
|
||||
log.error("Failed to fill item into webview: \(returnedItemsError ??? "nil").")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/* 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
|
||||
|
||||
/// This Activity Item Provider subclass does two things that are non-standard behaviour:
|
||||
///
|
||||
/// * We return NSNull if the calling activity is not supposed to see the title. For example the Copy action, which should only paste the URL. We also include Message and Mail to have parity with what Safari exposes.
|
||||
/// * We set the subject of the item to the title, this means it will correctly be used when sharing to for example Mail. Again parity with Safari.
|
||||
///
|
||||
/// Note that not all applications use the Subject. For example OmniFocus ignores it, so we need to do both.
|
||||
|
||||
class TitleActivityItemProvider: UIActivityItemProvider {
|
||||
static let activityTypesToIgnore = [UIActivityType.copyToPasteboard, UIActivityType.message, UIActivityType.mail]
|
||||
|
||||
init(title: String) {
|
||||
super.init(placeholderItem: title)
|
||||
}
|
||||
|
||||
override var item: Any {
|
||||
if let activityType = activityType {
|
||||
if TitleActivityItemProvider.activityTypesToIgnore.contains(activityType) {
|
||||
return NSNull()
|
||||
}
|
||||
}
|
||||
return placeholderItem! as AnyObject
|
||||
}
|
||||
|
||||
override func activityViewController(_ activityViewController: UIActivityViewController, subjectForActivityType activityType: UIActivityType?) -> String {
|
||||
return placeholderItem as! String
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue