| OLD | NEW |
| 1 // Copyright 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #import "ios/chrome/browser/web/external_app_launcher.h" | 5 #import "ios/chrome/browser/web/external_app_launcher.h" |
| 6 | 6 |
| 7 #include "base/ios/ios_util.h" |
| 7 #include "base/ios/weak_nsobject.h" | 8 #include "base/ios/weak_nsobject.h" |
| 8 #include "base/logging.h" | 9 #include "base/logging.h" |
| 9 #include "base/metrics/histogram_macros.h" | 10 #include "base/metrics/histogram_macros.h" |
| 10 #include "base/strings/sys_string_conversions.h" | 11 #include "base/strings/sys_string_conversions.h" |
| 11 #include "components/strings/grit/components_strings.h" | 12 #include "components/strings/grit/components_strings.h" |
| 12 #include "ios/chrome/browser/experimental_flags.h" | 13 #include "ios/chrome/browser/experimental_flags.h" |
| 13 #import "ios/chrome/browser/open_url_util.h" | 14 #import "ios/chrome/browser/open_url_util.h" |
| 14 #include "ios/chrome/grit/ios_strings.h" | 15 #include "ios/chrome/grit/ios_strings.h" |
| 15 #import "net/base/mac/url_conversions.h" | 16 #import "net/base/mac/url_conversions.h" |
| 16 #include "ui/base/l10n/l10n_util.h" | 17 #include "ui/base/l10n/l10n_util.h" |
| (...skipping 23 matching lines...) Expand all Loading... |
| 40 return [ITMSSchemes() containsObject:base::SysUTF8ToNSString(scheme)]; | 41 return [ITMSSchemes() containsObject:base::SysUTF8ToNSString(scheme)]; |
| 41 } | 42 } |
| 42 | 43 |
| 43 // Logs an entry for |Tab.ExternalApplicationOpened|. If the user decided to | 44 // Logs an entry for |Tab.ExternalApplicationOpened|. If the user decided to |
| 44 // open in an external app, pass true. Otherwise, if the user cancelled the | 45 // open in an external app, pass true. Otherwise, if the user cancelled the |
| 45 // opening, pass false. | 46 // opening, pass false. |
| 46 void RecordExternalApplicationOpened(bool opened) { | 47 void RecordExternalApplicationOpened(bool opened) { |
| 47 UMA_HISTOGRAM_BOOLEAN("Tab.ExternalApplicationOpened", opened); | 48 UMA_HISTOGRAM_BOOLEAN("Tab.ExternalApplicationOpened", opened); |
| 48 } | 49 } |
| 49 | 50 |
| 51 // Returns whether gURL has the scheme of a URL that initiates a call. |
| 52 BOOL UrlHasPhoneCallScheme(const GURL& gURL) { |
| 53 return gURL.SchemeIs("tel") || gURL.SchemeIs("facetime") || |
| 54 gURL.SchemeIs("facetime-audio"); |
| 55 } |
| 56 |
| 57 // Returns a string to be used as the label for the prompt's action button. |
| 58 NSString* PromptActionString(NSString* scheme) { |
| 59 if ([scheme isEqualToString:@"facetime"]) |
| 60 return l10n_util::GetNSString(IDS_IOS_FACETIME_BUTTON); |
| 61 else if ([scheme isEqualToString:@"tel"] || |
| 62 [scheme isEqualToString:@"facetime-audio"]) |
| 63 return l10n_util::GetNSString(IDS_IOS_PHONE_CALL_BUTTON); |
| 64 NOTREACHED(); |
| 65 return @""; |
| 66 } |
| 67 |
| 50 } // namespace | 68 } // namespace |
| 51 | 69 |
| 52 @interface ExternalAppLauncher () | 70 @interface ExternalAppLauncher () |
| 53 // Returns the Phone/FaceTime call argument from |URL|. | 71 // Returns the Phone/FaceTime call argument from |URL|. |
| 54 + (NSString*)formatCallArgument:(NSURL*)URL; | 72 + (NSString*)formatCallArgument:(NSURL*)URL; |
| 55 // Ask user for confirmation before dialing FaceTime destination. | 73 // Ask user for confirmation before dialing Phone or FaceTime destinations. |
| 56 - (void)openFaceTimePromptForURL:(NSURL*)telURL; | 74 - (void)openPromptForURL:(NSURL*)URL; |
| 57 // Ask user for confirmation before moving to external app. | 75 // Ask user for confirmation before moving to external app. |
| 58 - (void)openExternalAppWithPromptForURL:(NSURL*)URL; | 76 - (void)openExternalAppWithPromptForURL:(NSURL*)URL; |
| 59 // Presents a configured alert controller on the root view controller. | 77 // Presents a configured alert controller on the root view controller. |
| 60 - (void)presentAlertControllerWithMessage:(NSString*)message | 78 - (void)presentAlertControllerWithMessage:(NSString*)message |
| 61 openTitle:(NSString*)openTitle | 79 openTitle:(NSString*)openTitle |
| 62 openHandler:(AlertHandler)openHandler | 80 openHandler:(AlertHandler)openHandler |
| 63 cancelHandler:(AlertHandler)cancelHandler; | 81 cancelHandler:(AlertHandler)cancelHandler; |
| 64 @end | 82 @end |
| 65 | 83 |
| 66 @implementation ExternalAppLauncher | 84 @implementation ExternalAppLauncher |
| (...skipping 22 matching lines...) Expand all Loading... |
| 89 openTitle:openTitle | 107 openTitle:openTitle |
| 90 openHandler:^(UIAlertAction* action) { | 108 openHandler:^(UIAlertAction* action) { |
| 91 RecordExternalApplicationOpened(true); | 109 RecordExternalApplicationOpened(true); |
| 92 OpenUrlWithCompletionHandler(URL, nil); | 110 OpenUrlWithCompletionHandler(URL, nil); |
| 93 } | 111 } |
| 94 cancelHandler:^(UIAlertAction* action) { | 112 cancelHandler:^(UIAlertAction* action) { |
| 95 RecordExternalApplicationOpened(false); | 113 RecordExternalApplicationOpened(false); |
| 96 }]; | 114 }]; |
| 97 } | 115 } |
| 98 | 116 |
| 99 - (void)openFaceTimePromptForURL:(NSURL*)URL { | 117 - (void)openPromptForURL:(NSURL*)URL { |
| 100 NSString* openTitle = l10n_util::GetNSString(IDS_IOS_FACETIME_BUTTON); | |
| 101 [self presentAlertControllerWithMessage:[[self class] formatCallArgument:URL] | 118 [self presentAlertControllerWithMessage:[[self class] formatCallArgument:URL] |
| 102 openTitle:openTitle | 119 openTitle:PromptActionString(URL.scheme) |
| 103 openHandler:^(UIAlertAction* action) { | 120 openHandler:^(UIAlertAction* action) { |
| 104 OpenUrlWithCompletionHandler(URL, nil); | 121 OpenUrlWithCompletionHandler(URL, nil); |
| 105 } | 122 } |
| 106 cancelHandler:nil]; | 123 cancelHandler:nil]; |
| 107 } | 124 } |
| 108 | 125 |
| 109 - (void)presentAlertControllerWithMessage:(NSString*)message | 126 - (void)presentAlertControllerWithMessage:(NSString*)message |
| 110 openTitle:(NSString*)openTitle | 127 openTitle:(NSString*)openTitle |
| 111 openHandler:(AlertHandler)openHandler | 128 openHandler:(AlertHandler)openHandler |
| 112 cancelHandler:(AlertHandler)cancelHandler { | 129 cancelHandler:(AlertHandler)cancelHandler { |
| (...skipping 16 matching lines...) Expand all Loading... |
| 129 presentViewController:alertController | 146 presentViewController:alertController |
| 130 animated:YES | 147 animated:YES |
| 131 completion:nil]; | 148 completion:nil]; |
| 132 } | 149 } |
| 133 | 150 |
| 134 - (BOOL)openURL:(const GURL&)gURL linkClicked:(BOOL)linkClicked { | 151 - (BOOL)openURL:(const GURL&)gURL linkClicked:(BOOL)linkClicked { |
| 135 if (!gURL.is_valid() || !gURL.has_scheme()) | 152 if (!gURL.is_valid() || !gURL.has_scheme()) |
| 136 return NO; | 153 return NO; |
| 137 NSURL* URL = net::NSURLWithGURL(gURL); | 154 NSURL* URL = net::NSURLWithGURL(gURL); |
| 138 | 155 |
| 139 if (gURL.SchemeIs("facetime") || gURL.SchemeIs("facetime-audio")) { | 156 // iOS 10.3.2 introduced new prompts when facetime: and facetime-audio: |
| 157 // URL schemes are opened. It is no longer necessary for Chrome to present |
| 158 // another prompt before the system-provided prompt. |
| 159 if (!base::ios::IsRunningOnOrLater(10, 3, 2) && UrlHasPhoneCallScheme(gURL)) { |
| 140 // Showing an alert view immediately has a side-effect where focus is | 160 // Showing an alert view immediately has a side-effect where focus is |
| 141 // taken from the UIWebView so quickly that mouseup events are lost and | 161 // taken from the UIWebView so quickly that mouseup events are lost and |
| 142 // buttons get 'stuck' in the on position. The solution is to defer | 162 // buttons get 'stuck' in the on position. The solution is to defer |
| 143 // showing the view. | 163 // showing the view. |
| 144 [self performSelector:@selector(openFaceTimePromptForURL:) | 164 [self performSelector:@selector(openPromptForURL:) |
| 145 withObject:URL | 165 withObject:URL |
| 146 afterDelay:0.0]; | 166 afterDelay:0.0]; |
| 147 return YES; | 167 return YES; |
| 148 } | 168 } |
| 149 | 169 |
| 150 // Use telprompt instead of tel because telprompt returns user back to | |
| 151 // Chrome after phone call is completed/aborted. | |
| 152 if (gURL.SchemeIs("tel")) { | |
| 153 GURL::Replacements replacements; | |
| 154 replacements.SetSchemeStr("telprompt"); | |
| 155 URL = net::NSURLWithGURL(gURL.ReplaceComponents(replacements)); | |
| 156 DCHECK([[URL scheme] isEqualToString:@"telprompt"]); | |
| 157 } | |
| 158 | |
| 159 // Don't open external application if chrome is not active. | 170 // Don't open external application if chrome is not active. |
| 160 if ([[UIApplication sharedApplication] applicationState] != | 171 if ([[UIApplication sharedApplication] applicationState] != |
| 161 UIApplicationStateActive) | 172 UIApplicationStateActive) |
| 162 return NO; | 173 return NO; |
| 163 | 174 |
| 164 if (experimental_flags::IsExternalApplicationPromptEnabled()) { | 175 if (experimental_flags::IsExternalApplicationPromptEnabled()) { |
| 165 // Prompt user to open itunes when opening it is not a result of a link | 176 // Prompt user to open itunes when opening it is not a result of a link |
| 166 // click. | 177 // click. |
| 167 if (!linkClicked && UrlHasAppStoreScheme(gURL)) { | 178 if (!linkClicked && UrlHasAppStoreScheme(gURL)) { |
| 168 [self performSelector:@selector(openExternalAppWithPromptForURL:) | 179 [self performSelector:@selector(openExternalAppWithPromptForURL:) |
| 169 withObject:URL | 180 withObject:URL |
| 170 afterDelay:0.0]; | 181 afterDelay:0.0]; |
| 171 return YES; | 182 return YES; |
| 172 } | 183 } |
| 173 } | 184 } |
| 174 | 185 |
| 175 // If the following call returns YES, an external application is about to be | 186 // If the following call returns YES, an external application is about to be |
| 176 // launched and Chrome will go into the background now. | 187 // launched and Chrome will go into the background now. |
| 177 // TODO(crbug.com/622735): This call still needs to be updated. | 188 // TODO(crbug.com/622735): This call still needs to be updated. |
| 178 // It's heavily nested so some refactoring is needed. | 189 // It's heavily nested so some refactoring is needed. |
| 179 return [[UIApplication sharedApplication] openURL:URL]; | 190 return [[UIApplication sharedApplication] openURL:URL]; |
| 180 } | 191 } |
| 181 | 192 |
| 182 @end | 193 @end |
| OLD | NEW |