Skip to content

Commit 8648339

Browse files
robbiet480zacwest
andauthored
Add support for opening links in different browsers (home-assistant#638)
Co-authored-by: Zac West <[email protected]>
1 parent 108cc84 commit 8648339

13 files changed

+156
-33
lines changed

HomeAssistant.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,7 @@
530530
B6DA3C7322691A5000DE811C /* AKConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6DA3C7222691A5000DE811C /* AKConverter.swift */; };
531531
B6DAC735215F069300727D2A /* NotificationCategory.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6DAC734215F069300727D2A /* NotificationCategory.swift */; };
532532
B6DAC737215F06B100727D2A /* NotificationAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6DAC736215F06B100727D2A /* NotificationAction.swift */; };
533+
B6DD5E6A24940F6F003A0154 /* OpenInFirefoxControllerSwift.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6DD5E6924940F6F003A0154 /* OpenInFirefoxControllerSwift.swift */; };
533534
B6DF8BC1221C890600370A59 /* UIImageView+UIActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6DF8BC0221C890600370A59 /* UIImageView+UIActivityIndicator.swift */; };
534535
B6DF8BC2221C890F00370A59 /* UIImageView+UIActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6DF8BC0221C890600370A59 /* UIImageView+UIActivityIndicator.swift */; };
535536
B6DF8BC4221D047400370A59 /* GetCameraImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6DF8BC3221D047400370A59 /* GetCameraImage.swift */; };
@@ -1293,6 +1294,7 @@
12931294
B6DA3C7222691A5000DE811C /* AKConverter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AKConverter.swift; sourceTree = "<group>"; };
12941295
B6DAC734215F069300727D2A /* NotificationCategory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationCategory.swift; sourceTree = "<group>"; };
12951296
B6DAC736215F06B100727D2A /* NotificationAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationAction.swift; sourceTree = "<group>"; };
1297+
B6DD5E6924940F6F003A0154 /* OpenInFirefoxControllerSwift.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenInFirefoxControllerSwift.swift; sourceTree = "<group>"; };
12961298
B6DF8BC0221C890600370A59 /* UIImageView+UIActivityIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImageView+UIActivityIndicator.swift"; sourceTree = "<group>"; };
12971299
B6DF8BC3221D047400370A59 /* GetCameraImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetCameraImage.swift; sourceTree = "<group>"; };
12981300
B6E2D4B92270406B00446DFA /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = "Base.lproj/Beta LaunchScreen.storyboard"; sourceTree = "<group>"; };
@@ -2119,6 +2121,7 @@
21192121
119D765E2492F8FA00183C5F /* UIApplication+BackgroundTask.swift */,
21202122
1100D51E2496F63400B1073C /* ThemeColors.swift */,
21212123
1100D51C2496AECE00B1073C /* PermissionStatusRow.swift */,
2124+
B6DD5E6924940F6F003A0154 /* OpenInFirefoxControllerSwift.swift */,
21222125
);
21232126
path = Utilities;
21242127
sourceTree = "<group>";
@@ -4131,6 +4134,7 @@
41314134
B68EDD09215F45EB00DD6B28 /* NotificationIdentifierEurekaRow.swift in Sources */,
41324135
B65B15012273184600635D5C /* Roboto.swift in Sources */,
41334136
B6B6B14A215B137C003DE2DD /* WatchComplicationConfigurator.swift in Sources */,
4137+
B6DD5E6A24940F6F003A0154 /* OpenInFirefoxControllerSwift.swift in Sources */,
41344138
B64BB3A81E9C6551001E8B46 /* WebViewController.swift in Sources */,
41354139
B66C58BD21509934004AB261 /* ShortcutServiceConfigurator.swift in Sources */,
41364140
B67CE8D02220EF1D0034C1D0 /* ObjectMapper+RealmList.swift in Sources */,

HomeAssistant/Resources/Info.plist

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,7 @@
532532
<string>tweetbot</string>
533533
<string>twitter</string>
534534
<string>shortcuts</string>
535+
<string>firefox</string>
535536
</array>
536537
<key>LSRequiresIPhoneOS</key>
537538
<true/>

HomeAssistant/Resources/en.lproj/Localizable.strings

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,11 @@
4444
"settings.reset_section.reset_alert.title" = "Reset";
4545
"settings.reset_section.reset_alert.message" = "Your settings will be reset and this device will be unregistered from push notifications as well as removed from your Home Assistant configuration.";
4646
"settings_details.general.title" = "General";
47-
"settings_details.general.chrome.title" = "Open links in Chrome";
47+
"settings_details.general.open_in_browser.title" = "Open Links In";
48+
"settings_details.general.open_in_browser.chrome" = "Google Chrome";
49+
"settings_details.general.open_in_browser.firefox" = "Mozilla Firefox";
50+
"settings_details.general.open_in_browser.safari" = "Apple Safari";
51+
"settings_details.general.open_in_browser.safari_in_app" = "Apple Safari (in app)";
4852
"settings_details.location.title" = "Location";
4953
"settings_details.location.notifications.header" = "Location Notifications";
5054
"settings_details.location.notifications.enter.title" = "Enter Zone Notifications";
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//
2+
// OpenInFirefoxControllerSwift.swift
3+
// HomeAssistant
4+
//
5+
// Created by Robert Trencheny on 6/12/20.
6+
//
7+
//
8+
9+
/* This Source Code Form is subject to the terms of the Mozilla Public
10+
* License, v. 2.0. If a copy of the MPL was not distributed with this
11+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
12+
13+
import Foundation
14+
import UIKit
15+
16+
open class OpenInFirefoxControllerSwift {
17+
let firefoxScheme = "firefox:"
18+
let basicURL = URL(string: "firefox://")!
19+
20+
// This would need to be changed if used from an extension… but you
21+
// can't open arbitrary URLs from an extension anyway.
22+
let app = UIApplication.shared
23+
24+
fileprivate func encodeByAddingPercentEscapes(_ input: String) -> String {
25+
return NSString(string: input).addingPercentEncoding(
26+
withAllowedCharacters: CharacterSet(charactersIn: "!*'();:@&=+$,/?%#[]")
27+
)!
28+
}
29+
30+
open func isFirefoxInstalled() -> Bool {
31+
return app.canOpenURL(basicURL)
32+
}
33+
34+
open func openInFirefox(_ url: URL) {
35+
let scheme = url.scheme
36+
if scheme == "http" || scheme == "https" {
37+
let escaped = encodeByAddingPercentEscapes(url.absoluteString)
38+
if let firefoxURL = URL(string: "firefox://open-url?url=\(escaped)") {
39+
app.open(firefoxURL, options: [:], completionHandler: nil)
40+
}
41+
}
42+
}
43+
}

HomeAssistant/Utilities/Utils.swift

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import Foundation
1010
import KeychainAccess
1111
import Shared
1212
import RealmSwift
13+
import SafariServices
1314

1415
func resetStores() {
1516
do {
@@ -28,12 +29,25 @@ func resetStores() {
2829
Realm.reset()
2930
}
3031

31-
func openURLInBrowser(urlToOpen: URL) {
32-
if OpenInChromeController.sharedInstance.isChromeInstalled() && prefs.bool(forKey: "openInChrome") {
33-
_ = OpenInChromeController.sharedInstance.openInChrome(urlToOpen, callbackURL: nil)
34-
} else {
35-
UIApplication.shared.open(urlToOpen, options: [:],
36-
completionHandler: nil)
32+
func openURLInBrowser(_ urlToOpen: URL, _ sender: UIViewController) {
33+
guard ["http", "https"].contains(urlToOpen.scheme?.lowercased()) else {
34+
UIApplication.shared.open(urlToOpen, options: [:], completionHandler: nil)
35+
return
36+
}
37+
38+
let browserPreference = prefs.string(forKey: "openInBrowser")
39+
.flatMap { OpenInBrowser(rawValue: $0) } ?? .Safari
40+
41+
switch browserPreference {
42+
case .Chrome where OpenInChromeController.sharedInstance.isChromeInstalled():
43+
OpenInChromeController.sharedInstance.openInChrome(urlToOpen, callbackURL: nil)
44+
case .Firefox where OpenInFirefoxControllerSwift().isFirefoxInstalled():
45+
OpenInFirefoxControllerSwift().openInFirefox(urlToOpen)
46+
case .SafariInApp:
47+
let sfv = SFSafariViewController(url: urlToOpen)
48+
sender.present(sfv, animated: true)
49+
default:
50+
UIApplication.shared.open(urlToOpen, options: [:], completionHandler: nil)
3751
}
3852
}
3953

@@ -67,8 +81,13 @@ func setDefaults() {
6781
prefs.set(combined, forKey: "lastInstalledVersion")
6882
}
6983

70-
if prefs.object(forKey: "openInChrome") == nil && OpenInChromeController().isChromeInstalled() {
71-
prefs.setValue(true, forKey: "openInChrome")
84+
if prefs.object(forKey: "openInBrowser") == nil {
85+
if prefs.bool(forKey: "openInChrome") {
86+
prefs.set(OpenInBrowser.Chrome.rawValue, forKey: "openInBrowser")
87+
prefs.removeObject(forKey: "openInChrome")
88+
} else {
89+
prefs.set(OpenInBrowser.Safari.rawValue, forKey: "openInBrowser")
90+
}
7291
}
7392

7493
if prefs.object(forKey: "confirmBeforeOpeningUrl") == nil {

HomeAssistant/Views/AboutViewController.swift

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,15 @@ class AboutViewController: FormViewController {
5959
$0.title = L10n.About.Donate.patreon
6060
}.onCellSelection { _, _ in
6161
let urlStr = "https://patreon.com/robbiet480/"
62-
openURLInBrowser(urlToOpen: URL(string: urlStr)!)
62+
openURLInBrowser(URL(string: urlStr)!, self)
6363
}
6464

6565
<<< ButtonRow {
6666
$0.title = L10n.About.Beta.title
6767
$0.disabled = Condition(booleanLiteral: Current.appConfiguration == .Beta)
6868
}.onCellSelection { _, _ in
6969
let urlStr = "https://home-assistant.io/ios/beta/"
70+
// We want to open this in Safari so the TestFlight redirect works.
7071
UIApplication.shared.open(URL(string: urlStr)!, options: [:], completionHandler: nil)
7172
}
7273

@@ -90,31 +91,31 @@ class AboutViewController: FormViewController {
9091
$0.title = L10n.About.HelpLocalize.title
9192
}.onCellSelection { _, _ in
9293
let urlStr = "https://developers.home-assistant.io/docs/en/internationalization_translation.html"
93-
openURLInBrowser(urlToOpen: URL(string: urlStr)!)
94+
openURLInBrowser(URL(string: urlStr)!, self)
9495
}
9596

9697
+++ ButtonRow {
9798
$0.title = L10n.About.Website.title
9899
}.onCellSelection { _, _ in
99-
openURLInBrowser(urlToOpen: URL(string: "https://www.home-assistant.io/")!)
100+
openURLInBrowser(URL(string: "https://www.home-assistant.io/")!, self)
100101
}
101102

102103
<<< ButtonRow {
103104
$0.title = L10n.About.Forums.title
104105
}.onCellSelection { _, _ in
105-
openURLInBrowser(urlToOpen: URL(string: "https://community.home-assistant.io/")!)
106+
openURLInBrowser(URL(string: "https://community.home-assistant.io/")!, self)
106107
}
107108

108109
<<< ButtonRow {
109110
$0.title = L10n.About.Chat.title
110111
}.onCellSelection { _, _ in
111-
openURLInBrowser(urlToOpen: URL(string: "https://discord.gg/C7fXPmt")!)
112+
openURLInBrowser(URL(string: "https://discord.gg/C7fXPmt")!, self)
112113
}
113114

114115
<<< ButtonRow {
115116
$0.title = L10n.About.Documentation.title
116117
}.onCellSelection { _, _ in
117-
openURLInBrowser(urlToOpen: URL(string: "https://www.home-assistant.io/docs/ecosystem/ios/")!)
118+
openURLInBrowser(URL(string: "https://www.home-assistant.io/docs/ecosystem/ios/")!, self)
118119
}
119120

120121
<<< ButtonRow {
@@ -134,14 +135,13 @@ class AboutViewController: FormViewController {
134135
<<< ButtonRow {
135136
$0.title = L10n.About.Github.title
136137
}.onCellSelection { _, _ in
137-
openURLInBrowser(urlToOpen: URL(string: "https://github.com/home-assistant/home-assistant-iOS")!)
138+
openURLInBrowser(URL(string: "https://github.com/home-assistant/home-assistant-iOS")!, self)
138139
}
139140

140141
<<< ButtonRow {
141142
$0.title = L10n.About.GithubIssueTracker.title
142143
}.onCellSelection { _, _ in
143-
openURLInBrowser(urlToOpen:
144-
URL(string: "https://github.com/home-assistant/home-assistant-iOS/issues")!)
144+
openURLInBrowser(URL(string: "https://github.com/home-assistant/home-assistant-iOS/issues")!, self)
145145
}
146146
}
147147

HomeAssistant/Views/NotificationCategoryConfigurator.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,10 @@ class NotificationCategoryConfigurator: FormViewController, TypedRowControllerTy
277277

278278
@objc
279279
func getInfoAction(_ sender: Any) {
280-
openURLInBrowser(urlToOpen: URL(string: "https://companion.home-assistant.io/docs/notifications/actionable-notifications")!)
280+
openURLInBrowser(
281+
URL(string: "https://companion.home-assistant.io/docs/notifications/actionable-notifications")!,
282+
self
283+
)
281284
}
282285

283286
@objc

HomeAssistant/Views/Onboarding/ConnectionErrorViewController.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,6 @@ class ConnectionErrorViewController: UIViewController {
5656

5757
@IBAction func moreInfoTapped(_ sender: UIButton) {
5858
guard let error = self.error as? ConnectionTestResult else { return }
59-
openURLInBrowser(urlToOpen: error.DocumentationURL)
59+
openURLInBrowser(error.DocumentationURL, self)
6060
}
6161
}

HomeAssistant/Views/SettingsDetailViewController.swift

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -101,11 +101,21 @@ class SettingsDetailViewController: FormViewController, TypedRowControllerType {
101101
UIApplication.shared.setAlternateIconName(newAppIconName.rawValue)
102102
}
103103

104-
+++ SwitchRow("openInChrome") {
105-
$0.title = L10n.SettingsDetails.General.Chrome.title
106-
$0.value = prefs.bool(forKey: "openInChrome")
104+
+++ PushRow<OpenInBrowser>("openInBrowser") {
105+
$0.title = L10n.SettingsDetails.General.OpenInBrowser.title
106+
107+
if let value = prefs.string(forKey: "openInBrowser").flatMap({ OpenInBrowser(rawValue: $0) }),
108+
value.isInstalled {
109+
$0.value = value
110+
} else {
111+
$0.value = .Safari
112+
}
113+
$0.selectorTitle = $0.title
114+
$0.options = OpenInBrowser.allCases.filter { $0.isInstalled }
115+
$0.displayValueFor = { $0?.title }
107116
}.onChange { row in
108-
prefs.setValue(row.value, forKey: "openInChrome")
117+
guard let browserChoice = row.value else { return }
118+
prefs.setValue(browserChoice.rawValue, forKey: "openInBrowser")
109119
prefs.synchronize()
110120
}
111121

@@ -515,15 +525,15 @@ class SettingsDetailViewController: FormViewController, TypedRowControllerType {
515525
}
516526

517527
@objc func firebasePrivacy(_ sender: Any) {
518-
openURLInBrowser(urlToOpen: URL(string: "https://firebase.google.com/support/privacy/")!)
528+
openURLInBrowser(URL(string: "https://firebase.google.com/support/privacy/")!, self)
519529
}
520530

521531
@objc func actionsHelp(_ sender: Any) {
522-
openURLInBrowser(urlToOpen: URL(string: "https://companion.home-assistant.io/docs/core/actions")!)
532+
openURLInBrowser(URL(string: "https://companion.home-assistant.io/docs/core/actions")!, self)
523533
}
524534

525535
@objc func watchHelp(_ sender: Any) {
526-
openURLInBrowser(urlToOpen: URL(string: "https://companion.home-assistant.io/next/integrations/apple-watch")!)
536+
openURLInBrowser(URL(string: "https://companion.home-assistant.io/next/integrations/apple-watch")!, self)
527537
}
528538

529539
override func tableView(_ tableView: UITableView, willBeginReorderingRowAtIndexPath indexPath: IndexPath) {
@@ -856,6 +866,37 @@ enum AppIcon: String, CaseIterable {
856866
}
857867
}
858868

869+
enum OpenInBrowser: String, CaseIterable {
870+
case Chrome
871+
case Firefox
872+
case Safari
873+
case SafariInApp
874+
875+
var title: String {
876+
switch self {
877+
case .Chrome:
878+
return L10n.SettingsDetails.General.OpenInBrowser.chrome
879+
case .Firefox:
880+
return L10n.SettingsDetails.General.OpenInBrowser.firefox
881+
case .Safari:
882+
return L10n.SettingsDetails.General.OpenInBrowser.safari
883+
case .SafariInApp:
884+
return L10n.SettingsDetails.General.OpenInBrowser.safariInApp
885+
}
886+
}
887+
888+
var isInstalled: Bool {
889+
switch self {
890+
case .Chrome:
891+
return OpenInChromeController.sharedInstance.isChromeInstalled()
892+
case .Firefox:
893+
return OpenInFirefoxControllerSwift().isFirefoxInstalled()
894+
default:
895+
return true
896+
}
897+
}
898+
}
899+
859900
@available (iOS 12, *)
860901
extension SettingsDetailViewController: INUIAddVoiceShortcutViewControllerDelegate {
861902

HomeAssistant/Views/WatchComplicationConfigurator.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ class WatchComplicationConfigurator: FormViewController, TypedRowControllerType
304304

305305
@objc
306306
func getInfoAction(_ sender: Any) {
307-
openURLInBrowser(urlToOpen: URL(string: "https://companion.home-assistant.io/next/integrations/apple-watch")!)
307+
openURLInBrowser(URL(string: "https://companion.home-assistant.io/next/integrations/apple-watch")!, self)
308308
}
309309

310310
func renderTemplateForRow(rowTag: String) {

HomeAssistant/Views/WebViewController.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ class WebViewController: UIViewController, WKNavigationDelegate, WKUIDelegate, U
285285
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration,
286286
for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
287287
if navigationAction.targetFrame == nil {
288-
openURLInBrowser(urlToOpen: navigationAction.request.url!)
288+
openURLInBrowser(navigationAction.request.url!, self)
289289
}
290290
return nil
291291
}

Podfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -650,7 +650,7 @@ SPEC CHECKSUMS:
650650
GoogleUtilities: 39530bc0ad980530298e9c4af8549e991fd033b1
651651
"gRPC-C++": 13d8ccef97d5c3c441b7e3c529ef28ebee86fad2
652652
gRPC-Core: 4afa11bfbedf7cdecd04de535a9e046893404ed5
653-
Iconic: a714832bc864d725864a9f78ef72b8897b5b3c48
653+
Iconic: 6253c7913a2fb87c4a1366a9ec0befcf30173c21
654654
KeychainAccess: 3f760109aa99b05d0f231e28b22642da7153e38a
655655
leveldb-library: 55d93ee664b4007aac644a782d11da33fba316f7
656656
Lokalise: bc42066e2dde59b43b46e88ecc0b92b7d593dc72

Shared/Resources/Swiftgen/Strings.swift

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1323,9 +1323,17 @@ internal enum L10n {
13231323
/// Automatically hide toolbar
13241324
internal static let title = L10n.tr("Localizable", "settings_details.general.autohide_toolbar.title")
13251325
}
1326-
internal enum Chrome {
1327-
/// Open links in Chrome
1328-
internal static let title = L10n.tr("Localizable", "settings_details.general.chrome.title")
1326+
internal enum OpenInBrowser {
1327+
/// Google Chrome
1328+
internal static let chrome = L10n.tr("Localizable", "settings_details.general.open_in_browser.chrome")
1329+
/// Mozilla Firefox
1330+
internal static let firefox = L10n.tr("Localizable", "settings_details.general.open_in_browser.firefox")
1331+
/// Apple Safari
1332+
internal static let safari = L10n.tr("Localizable", "settings_details.general.open_in_browser.safari")
1333+
/// Apple Safari (in app)
1334+
internal static let safariInApp = L10n.tr("Localizable", "settings_details.general.open_in_browser.safari_in_app")
1335+
/// Open Links In
1336+
internal static let title = L10n.tr("Localizable", "settings_details.general.open_in_browser.title")
13291337
}
13301338
internal enum DeviceName {
13311339
/// Device Name

0 commit comments

Comments
 (0)