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

Side by Side Diff: ios/chrome/browser/web/external_app_launcher.mm

Issue 2893743002: Removed a delay that is no longer necessary. (Closed)
Patch Set: rebase Created 3 years, 7 months 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 unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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/ios_util.h"
8 #include "base/logging.h" 8 #include "base/logging.h"
9 #include "base/mac/foundation_util.h" 9 #include "base/mac/foundation_util.h"
10 #include "base/metrics/histogram_macros.h" 10 #include "base/metrics/histogram_macros.h"
11 #include "base/strings/sys_string_conversions.h" 11 #include "base/strings/sys_string_conversions.h"
12 #include "components/strings/grit/components_strings.h" 12 #include "components/strings/grit/components_strings.h"
13 #include "ios/chrome/browser/experimental_flags.h" 13 #include "ios/chrome/browser/experimental_flags.h"
14 #import "ios/chrome/browser/open_url_util.h" 14 #import "ios/chrome/browser/open_url_util.h"
15 #import "ios/chrome/browser/web/mailto_url_rewriter.h" 15 #import "ios/chrome/browser/web/mailto_url_rewriter.h"
16 #include "ios/chrome/grit/ios_strings.h" 16 #include "ios/chrome/grit/ios_strings.h"
17 #import "net/base/mac/url_conversions.h" 17 #import "net/base/mac/url_conversions.h"
18 #include "ui/base/l10n/l10n_util.h" 18 #include "ui/base/l10n/l10n_util.h"
19 #include "url/gurl.h" 19 #include "url/gurl.h"
20 #include "url/url_constants.h" 20 #include "url/url_constants.h"
21 21
22 #if !defined(__has_feature) || !__has_feature(objc_arc) 22 #if !defined(__has_feature) || !__has_feature(objc_arc)
23 #error "This file requires ARC support." 23 #error "This file requires ARC support."
24 #endif 24 #endif
25 25
26 namespace { 26 namespace {
27 27
28 typedef void (^AlertHandler)(UIAlertAction* action);
29
30 // Returns a set of NSStrings that are URL schemes for iTunes Stores. 28 // Returns a set of NSStrings that are URL schemes for iTunes Stores.
31 NSSet<NSString*>* ITMSSchemes() { 29 NSSet<NSString*>* ITMSSchemes() {
32 static NSSet<NSString*>* schemes; 30 static NSSet<NSString*>* schemes;
33 static dispatch_once_t once; 31 static dispatch_once_t once;
34 dispatch_once(&once, ^{ 32 dispatch_once(&once, ^{
35 schemes = [NSSet<NSString*> 33 schemes = [NSSet<NSString*>
36 setWithObjects:@"itms", @"itmss", @"itms-apps", @"itms-appss", 34 setWithObjects:@"itms", @"itmss", @"itms-apps", @"itms-appss",
37 // There's no evidence that itms-bookss is actually 35 // There's no evidence that itms-bookss is actually
38 // supported, but over-inclusion costs less than 36 // supported, but over-inclusion costs less than
39 // under-inclusion. 37 // under-inclusion.
(...skipping 24 matching lines...) Expand all
64 NSString* PromptActionString(NSString* scheme) { 62 NSString* PromptActionString(NSString* scheme) {
65 if ([scheme isEqualToString:@"facetime"]) 63 if ([scheme isEqualToString:@"facetime"])
66 return l10n_util::GetNSString(IDS_IOS_FACETIME_BUTTON); 64 return l10n_util::GetNSString(IDS_IOS_FACETIME_BUTTON);
67 else if ([scheme isEqualToString:@"tel"] || 65 else if ([scheme isEqualToString:@"tel"] ||
68 [scheme isEqualToString:@"facetime-audio"]) 66 [scheme isEqualToString:@"facetime-audio"])
69 return l10n_util::GetNSString(IDS_IOS_PHONE_CALL_BUTTON); 67 return l10n_util::GetNSString(IDS_IOS_PHONE_CALL_BUTTON);
70 NOTREACHED(); 68 NOTREACHED();
71 return @""; 69 return @"";
72 } 70 }
73 71
74 // Keys for dictionary parameters passed into
75 // -openExternalAppWithPromptWithParams:.
76 NSString* const kAlertPrompt = @"prompt";
77 NSString* const kAlertAction = @"action";
78 NSString* const kAlertURL = @"url";
79
80 } // namespace 72 } // namespace
81 73
82 @interface ExternalAppLauncher () 74 @interface ExternalAppLauncher ()
83 // Returns the Phone/FaceTime call argument from |URL|. 75 // Returns the Phone/FaceTime call argument from |URL|.
84 + (NSString*)formatCallArgument:(NSURL*)URL; 76 + (NSString*)formatCallArgument:(NSURL*)URL;
85 // Asks user for confirmation before moving to external app. |params| is a 77 // Presents an alert controller with |prompt| and |openLabel| as button label
86 // dictionary keyed with values of kAlert* above. 78 // on the root view controller before launching an external app identified by
87 - (void)openExternalAppWithPromptWithParams: 79 // |URL|.
88 (NSDictionary<NSString*, id>*)params; 80 - (void)openExternalAppWithURL:(NSURL*)URL
89 // Presents a configured alert controller on the root view controller. 81 prompt:(NSString*)prompt
90 - (void)presentAlertControllerWithMessage:(NSString*)message 82 action:(NSString*)openLabel;
Eugene But (OOO till 7-30) 2017/05/18 23:40:14 s/action/openLabel ?
pkl (ping after 24h if needed) 2017/05/19 00:59:34 Done.
91 openTitle:(NSString*)openTitle
92 openHandler:(AlertHandler)openHandler
93 cancelHandler:(AlertHandler)cancelHandler;
94 @end 83 @end
95 84
96 @implementation ExternalAppLauncher 85 @implementation ExternalAppLauncher
97 86
98 + (NSString*)formatCallArgument:(NSURL*)URL { 87 + (NSString*)formatCallArgument:(NSURL*)URL {
99 NSCharacterSet* charSet = 88 NSCharacterSet* charSet =
100 [NSCharacterSet characterSetWithCharactersInString:@"/"]; 89 [NSCharacterSet characterSetWithCharactersInString:@"/"];
101 NSString* scheme = [NSString stringWithFormat:@"%@:", [URL scheme]]; 90 NSString* scheme = [NSString stringWithFormat:@"%@:", [URL scheme]];
102 NSString* URLString = [URL absoluteString]; 91 NSString* URLString = [URL absoluteString];
103 if ([URLString length] <= [scheme length]) 92 if ([URLString length] <= [scheme length])
104 return URLString; 93 return URLString;
105 NSString* prompt = [[[[URL absoluteString] substringFromIndex:[scheme length]] 94 NSString* prompt = [[[[URL absoluteString] substringFromIndex:[scheme length]]
106 stringByTrimmingCharactersInSet:charSet] stringByRemovingPercentEncoding]; 95 stringByTrimmingCharactersInSet:charSet] stringByRemovingPercentEncoding];
107 // Returns original URL string if there's nothing interesting to display 96 // Returns original URL string if there's nothing interesting to display
108 // other than the scheme itself. 97 // other than the scheme itself.
109 if (![prompt length]) 98 if (![prompt length])
110 return URLString; 99 return URLString;
111 return prompt; 100 return prompt;
112 } 101 }
113 102
114 - (void)openExternalAppWithPromptWithParams: 103 - (void)openExternalAppWithURL:(NSURL*)URL
115 (NSDictionary<NSString*, id>*)params { 104 prompt:(NSString*)prompt
116 NSURL* URL = base::mac::ObjCCastStrict<NSURL>(params[kAlertURL]); 105 action:(NSString*)openLabel {
117 NSString* prompt = base::mac::ObjCCastStrict<NSString>(params[kAlertPrompt]);
118 NSString* actionTitle =
119 base::mac::ObjCCastStrict<NSString>(params[kAlertAction]);
120 [self presentAlertControllerWithMessage:prompt
121 openTitle:actionTitle
122 openHandler:^(UIAlertAction* action) {
123 RecordExternalApplicationOpened(true);
124 OpenUrlWithCompletionHandler(URL, nil);
125 }
126 cancelHandler:^(UIAlertAction* action) {
127 RecordExternalApplicationOpened(false);
128 }];
129 }
130
131 - (void)presentAlertControllerWithMessage:(NSString*)message
132 openTitle:(NSString*)openTitle
133 openHandler:(AlertHandler)openHandler
134 cancelHandler:(AlertHandler)cancelHandler {
135 UIAlertController* alertController = 106 UIAlertController* alertController =
136 [UIAlertController alertControllerWithTitle:nil 107 [UIAlertController alertControllerWithTitle:nil
137 message:message 108 message:prompt
138 preferredStyle:UIAlertControllerStyleAlert]; 109 preferredStyle:UIAlertControllerStyleAlert];
139 UIAlertAction* openAction = 110 UIAlertAction* openAction =
140 [UIAlertAction actionWithTitle:openTitle 111 [UIAlertAction actionWithTitle:openLabel
141 style:UIAlertActionStyleDefault 112 style:UIAlertActionStyleDefault
142 handler:openHandler]; 113 handler:^(UIAlertAction* action) {
114 RecordExternalApplicationOpened(true);
115 OpenUrlWithCompletionHandler(URL, nil);
116 }];
143 UIAlertAction* cancelAction = 117 UIAlertAction* cancelAction =
144 [UIAlertAction actionWithTitle:l10n_util::GetNSString(IDS_CANCEL) 118 [UIAlertAction actionWithTitle:l10n_util::GetNSString(IDS_CANCEL)
145 style:UIAlertActionStyleCancel 119 style:UIAlertActionStyleCancel
146 handler:cancelHandler]; 120 handler:^(UIAlertAction* action) {
121 RecordExternalApplicationOpened(false);
122 }];
147 [alertController addAction:cancelAction]; 123 [alertController addAction:cancelAction];
148 [alertController addAction:openAction]; 124 [alertController addAction:openAction];
149 125
150 [[[[UIApplication sharedApplication] keyWindow] rootViewController] 126 [[[[UIApplication sharedApplication] keyWindow] rootViewController]
151 presentViewController:alertController 127 presentViewController:alertController
152 animated:YES 128 animated:YES
153 completion:nil]; 129 completion:nil];
154 } 130 }
155 131
156 - (BOOL)openURL:(const GURL&)gURL linkClicked:(BOOL)linkClicked { 132 - (BOOL)openURL:(const GURL&)gURL linkClicked:(BOOL)linkClicked {
157 if (!gURL.is_valid() || !gURL.has_scheme()) 133 if (!gURL.is_valid() || !gURL.has_scheme())
158 return NO; 134 return NO;
159 135
160 // Don't open external application if chrome is not active. 136 // Don't open external application if chrome is not active.
161 if ([[UIApplication sharedApplication] applicationState] != 137 if ([[UIApplication sharedApplication] applicationState] !=
162 UIApplicationStateActive) 138 UIApplicationStateActive)
163 return NO; 139 return NO;
164 140
165 NSURL* URL = net::NSURLWithGURL(gURL); 141 NSURL* URL = net::NSURLWithGURL(gURL);
166 NSMutableDictionary<NSString*, id>* params = [NSMutableDictionary dictionary];
167 if (base::ios::IsRunningOnOrLater(10, 3, 0)) { 142 if (base::ios::IsRunningOnOrLater(10, 3, 0)) {
168 if (UrlHasAppStoreScheme(gURL)) { 143 if (UrlHasAppStoreScheme(gURL)) {
169 params[kAlertPrompt] = 144 NSString* prompt = l10n_util::GetNSString(IDS_IOS_OPEN_IN_ANOTHER_APP);
170 l10n_util::GetNSString(IDS_IOS_OPEN_IN_ANOTHER_APP); 145 NSString* openLabel =
171 params[kAlertAction] =
172 l10n_util::GetNSString(IDS_IOS_APP_LAUNCHER_OPEN_APP_BUTTON_LABEL); 146 l10n_util::GetNSString(IDS_IOS_APP_LAUNCHER_OPEN_APP_BUTTON_LABEL);
147 [self openExternalAppWithURL:URL prompt:prompt action:openLabel];
148 return YES;
173 } 149 }
174 } else { 150 } else {
175 // Prior to iOS 10.3, iOS does not prompt user when facetime: and 151 // Prior to iOS 10.3, iOS does not prompt user when facetime: and
176 // facetime-audio: URL schemes are opened, so Chrome needs to present an 152 // facetime-audio: URL schemes are opened, so Chrome needs to present an
177 // alert before placing a phone call. 153 // alert before placing a phone call.
178 if (UrlHasPhoneCallScheme(gURL)) { 154 if (UrlHasPhoneCallScheme(gURL)) {
179 params[kAlertPrompt] = [[self class] formatCallArgument:URL]; 155 [self openExternalAppWithURL:URL
Eugene But (OOO till 7-30) 2017/05/18 23:40:15 Optional nit: Maybe pass GURL to openExternalAppWi
pkl (ping after 24h if needed) 2017/05/19 00:59:34 I'll save this for the next iteration of refactori
180 params[kAlertAction] = PromptActionString([URL scheme]); 156 prompt:[[self class] formatCallArgument:URL]
157 action:PromptActionString([URL scheme])];
158 return YES;
181 } 159 }
182 // Prior to iOS 10.3, Chrome prompts user with an alert before opening 160 // Prior to iOS 10.3, Chrome prompts user with an alert before opening
183 // App Store when user did not tap on any links and an iTunes app URL is 161 // App Store when user did not tap on any links and an iTunes app URL is
184 // opened. This maintains parity with Safari in pre-10.3 environment. 162 // opened. This maintains parity with Safari in pre-10.3 environment.
185 if (!linkClicked && UrlHasAppStoreScheme(gURL)) { 163 if (!linkClicked && UrlHasAppStoreScheme(gURL)) {
186 params[kAlertPrompt] = 164 NSString* prompt = l10n_util::GetNSString(IDS_IOS_OPEN_IN_ANOTHER_APP);
187 l10n_util::GetNSString(IDS_IOS_OPEN_IN_ANOTHER_APP); 165 NSString* openLabel =
188 params[kAlertAction] =
189 l10n_util::GetNSString(IDS_IOS_APP_LAUNCHER_OPEN_APP_BUTTON_LABEL); 166 l10n_util::GetNSString(IDS_IOS_APP_LAUNCHER_OPEN_APP_BUTTON_LABEL);
167 [self openExternalAppWithURL:URL prompt:prompt action:openLabel];
168 return YES;
190 } 169 }
191 } 170 }
192 if ([params count] > 0) {
193 params[kAlertURL] = URL;
194 // Note: Showing an alert view immediately has a side-effect where focus is
195 // taken from the UIWebView so quickly that mouseup events are lost and
196 // buttons get 'stuck' in the on position. The solution is to defer
197 // showing the view using -performSelector:withObject:afterDelay:.
198 [self performSelector:@selector(openExternalAppWithPromptWithParams:)
199 withObject:params
200 afterDelay:0.0];
201 return YES;
202 }
203 171
204 // Replaces |URL| with a rewritten URL if it is of mailto: scheme. 172 // Replaces |URL| with a rewritten URL if it is of mailto: scheme.
205 if (!experimental_flags::IsNativeAppLauncherEnabled() && 173 if (!experimental_flags::IsNativeAppLauncherEnabled() &&
206 gURL.SchemeIs(url::kMailToScheme)) { 174 gURL.SchemeIs(url::kMailToScheme)) {
207 MailtoURLRewriter* rewriter = 175 MailtoURLRewriter* rewriter =
208 [[MailtoURLRewriter alloc] initWithStandardHandlers]; 176 [[MailtoURLRewriter alloc] initWithStandardHandlers];
209 NSString* launchURL = [rewriter rewriteMailtoURL:gURL]; 177 NSString* launchURL = [rewriter rewriteMailtoURL:gURL];
210 if (launchURL) { 178 if (launchURL)
211 URL = [NSURL URLWithString:launchURL]; 179 URL = [NSURL URLWithString:launchURL];
212 }
213 UMA_HISTOGRAM_BOOLEAN("IOS.MailtoURLRewritten", launchURL != nil); 180 UMA_HISTOGRAM_BOOLEAN("IOS.MailtoURLRewritten", launchURL != nil);
214 } 181 }
215 182
216 // If the following call returns YES, an external application is about to be 183 // If the following call returns YES, an external application is about to be
217 // launched and Chrome will go into the background now. 184 // launched and Chrome will go into the background now.
218 // TODO(crbug.com/622735): This call still needs to be updated. 185 // TODO(crbug.com/622735): This call still needs to be updated.
219 // It's heavily nested so some refactoring is needed. 186 // It's heavily nested so some refactoring is needed.
220 return [[UIApplication sharedApplication] openURL:URL]; 187 return [[UIApplication sharedApplication] openURL:URL];
221 } 188 }
222 189
223 @end 190 @end
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698