Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(85)

Unified Diff: ios/chrome/browser/web/external_app_launcher.mm

Issue 2580333003: Upstream Chrome on iOS source code [10/11]. (Closed)
Patch Set: Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: ios/chrome/browser/web/external_app_launcher.mm
diff --git a/ios/chrome/browser/web/external_app_launcher.mm b/ios/chrome/browser/web/external_app_launcher.mm
new file mode 100644
index 0000000000000000000000000000000000000000..0af7de42663d0e9e5b2bf8977f752db1ad5420ce
--- /dev/null
+++ b/ios/chrome/browser/web/external_app_launcher.mm
@@ -0,0 +1,182 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/web/external_app_launcher.h"
+
+#include "base/ios/weak_nsobject.h"
+#include "base/logging.h"
+#include "base/metrics/histogram.h"
+#include "base/strings/sys_string_conversions.h"
+#include "components/strings/grit/components_strings.h"
+#include "ios/chrome/browser/experimental_flags.h"
+#import "ios/chrome/browser/open_url_util.h"
+#include "ios/chrome/grit/ios_strings.h"
+#import "net/base/mac/url_conversions.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "url/gurl.h"
+
+namespace {
+
+typedef void (^AlertHandler)(UIAlertAction* action);
+
+// Returns a set of NSStrings that are URL schemes for iTunes Stores.
+NSSet<NSString*>* ITMSSchemes() {
+ static NSSet<NSString*>* schemes;
+ static dispatch_once_t once;
+ dispatch_once(&once, ^{
+ schemes =
+ [[NSSet setWithObjects:@"itms", @"itmss", @"itms-apps", @"itms-appss",
+ // There's no evidence that itms-bookss is
+ // actually supported, but over-inclusion
+ // costs less than under-inclusion.
+ @"itms-books", @"itms-bookss", nil] retain];
+ });
+ return schemes;
+}
+
+bool UrlHasAppStoreScheme(const GURL& gURL) {
+ std::string scheme = gURL.scheme();
+ return [ITMSSchemes() containsObject:base::SysUTF8ToNSString(scheme)];
+}
+
+// Logs an entry for |Tab.ExternalApplicationOpened|. If the user decided to
+// open in an external app, pass true. Otherwise, if the user cancelled the
+// opening, pass false.
+void RecordExternalApplicationOpened(bool opened) {
+ UMA_HISTOGRAM_BOOLEAN("Tab.ExternalApplicationOpened", opened);
+}
+
+} // namespace
+
+@interface ExternalAppLauncher ()
+// Returns the Phone/FaceTime call argument from |URL|.
++ (NSString*)formatCallArgument:(NSURL*)URL;
+// Ask user for confirmation before dialing FaceTime destination.
+- (void)openFaceTimePromptForURL:(NSURL*)telURL;
+// Ask user for confirmation before moving to external app.
+- (void)openExternalAppWithPromptForURL:(NSURL*)URL;
+// Presents a configured alert controller on the root view controller.
+- (void)presentAlertControllerWithMessage:(NSString*)message
+ openTitle:(NSString*)openTitle
+ openHandler:(AlertHandler)openHandler
+ cancelHandler:(AlertHandler)cancelHandler;
+@end
+
+@implementation ExternalAppLauncher
+
++ (NSString*)formatCallArgument:(NSURL*)URL {
+ NSCharacterSet* charSet =
+ [NSCharacterSet characterSetWithCharactersInString:@"/"];
+ NSString* scheme = [NSString stringWithFormat:@"%@:", [URL scheme]];
+ NSString* URLString = [URL absoluteString];
+ if ([URLString length] <= [scheme length])
+ return URLString;
+ NSString* prompt = [[[[URL absoluteString] substringFromIndex:[scheme length]]
+ stringByTrimmingCharactersInSet:charSet] stringByRemovingPercentEncoding];
+ // Returns original URL string if there's nothing interesting to display
+ // other than the scheme itself.
+ if (![prompt length])
+ return URLString;
+ return prompt;
+}
+
+- (void)openExternalAppWithPromptForURL:(NSURL*)URL {
+ NSString* message = l10n_util::GetNSString(IDS_IOS_OPEN_IN_ANOTHER_APP);
+ NSString* openTitle =
+ l10n_util::GetNSString(IDS_IOS_APP_LAUNCHER_OPEN_APP_BUTTON_LABEL);
+ [self presentAlertControllerWithMessage:message
+ openTitle:openTitle
+ openHandler:^(UIAlertAction* action) {
+ RecordExternalApplicationOpened(true);
+ OpenUrlWithCompletionHandler(URL, nil);
+ }
+ cancelHandler:^(UIAlertAction* action) {
+ RecordExternalApplicationOpened(false);
+ }];
+}
+
+- (void)openFaceTimePromptForURL:(NSURL*)URL {
+ NSString* openTitle = l10n_util::GetNSString(IDS_IOS_FACETIME_BUTTON);
+ [self presentAlertControllerWithMessage:[[self class] formatCallArgument:URL]
+ openTitle:openTitle
+ openHandler:^(UIAlertAction* action) {
+ OpenUrlWithCompletionHandler(URL, nil);
+ }
+ cancelHandler:nil];
+}
+
+- (void)presentAlertControllerWithMessage:(NSString*)message
+ openTitle:(NSString*)openTitle
+ openHandler:(AlertHandler)openHandler
+ cancelHandler:(AlertHandler)cancelHandler {
+ UIAlertController* alertController =
+ [UIAlertController alertControllerWithTitle:nil
+ message:message
+ preferredStyle:UIAlertControllerStyleAlert];
+ UIAlertAction* openAction =
+ [UIAlertAction actionWithTitle:openTitle
+ style:UIAlertActionStyleDefault
+ handler:openHandler];
+ UIAlertAction* cancelAction =
+ [UIAlertAction actionWithTitle:l10n_util::GetNSString(IDS_CANCEL)
+ style:UIAlertActionStyleCancel
+ handler:cancelHandler];
+ [alertController addAction:cancelAction];
+ [alertController addAction:openAction];
+
+ [[[[UIApplication sharedApplication] keyWindow] rootViewController]
+ presentViewController:alertController
+ animated:YES
+ completion:nil];
+}
+
+- (BOOL)openURL:(const GURL&)gURL linkClicked:(BOOL)linkClicked {
+ if (!gURL.is_valid() || !gURL.has_scheme())
+ return NO;
+ NSURL* URL = net::NSURLWithGURL(gURL);
+
+ if (gURL.SchemeIs("facetime") || gURL.SchemeIs("facetime-audio")) {
+ // Showing an alert view immediately has a side-effect where focus is
+ // taken from the UIWebView so quickly that mouseup events are lost and
+ // buttons get 'stuck' in the on position. The solution is to defer
+ // showing the view.
+ [self performSelector:@selector(openFaceTimePromptForURL:)
+ withObject:URL
+ afterDelay:0.0];
+ return YES;
+ }
+
+ // Use telprompt instead of tel because telprompt returns user back to
+ // Chrome after phone call is completed/aborted.
+ if (gURL.SchemeIs("tel")) {
+ GURL::Replacements replacements;
+ replacements.SetSchemeStr("telprompt");
+ URL = net::NSURLWithGURL(gURL.ReplaceComponents(replacements));
+ DCHECK([[URL scheme] isEqualToString:@"telprompt"]);
+ }
+
+ // Don't open external application if chrome is not active.
+ if ([[UIApplication sharedApplication] applicationState] !=
+ UIApplicationStateActive)
+ return NO;
+
+ if (experimental_flags::IsExternalApplicationPromptEnabled()) {
+ // Prompt user to open itunes when opening it is not a result of a link
+ // click.
+ if (!linkClicked && UrlHasAppStoreScheme(gURL)) {
+ [self performSelector:@selector(openExternalAppWithPromptForURL:)
+ withObject:URL
+ afterDelay:0.0];
+ return YES;
+ }
+ }
+
+ // If the following call returns YES, an external application is about to be
+ // launched and Chrome will go into the background now.
+ // TODO(crbug.com/622735): This call still needs to be updated.
+ // It's heavily nested so some refactoring is needed.
+ return [[UIApplication sharedApplication] openURL:URL];
+}
+
+@end
« no previous file with comments | « ios/chrome/browser/web/external_app_launcher.h ('k') | ios/chrome/browser/web/external_app_launcher_disabler.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698