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

Side by Side Diff: ios/chrome/app/application_delegate/user_activity_handler.mm

Issue 2580363002: Upstream Chrome on iOS source code [1/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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #import "ios/chrome/app/application_delegate/user_activity_handler.h"
6
7 #import <CoreSpotlight/CoreSpotlight.h>
8 #import <UIKit/UIKit.h>
9
10 #include "base/ios/block_types.h"
11 #include "base/ios/ios_util.h"
12 #include "base/ios/weak_nsobject.h"
13 #include "base/mac/foundation_util.h"
14 #include "base/metrics/histogram_macros.h"
15 #include "base/metrics/user_metrics_action.h"
16 #include "base/strings/sys_string_conversions.h"
17 #include "components/handoff/handoff_utility.h"
18 #import "ios/chrome/app/application_delegate/startup_information.h"
19 #import "ios/chrome/app/application_delegate/tab_opening.h"
20 #include "ios/chrome/app/application_mode.h"
21 #import "ios/chrome/app/spotlight/actions_spotlight_manager.h"
22 #import "ios/chrome/app/spotlight/spotlight_util.h"
23 #include "ios/chrome/browser/app_startup_parameters.h"
24 #include "ios/chrome/browser/chrome_url_constants.h"
25 #include "ios/chrome/browser/experimental_flags.h"
26 #include "ios/chrome/browser/metrics/first_user_action_recorder.h"
27 #import "ios/chrome/browser/tabs/tab.h"
28 #import "ios/chrome/browser/tabs/tab_model.h"
29 #import "ios/chrome/browser/u2f/u2f_controller.h"
30 #import "ios/chrome/browser/ui/main/browser_view_information.h"
31 #import "net/base/mac/url_conversions.h"
32 #include "ui/base/page_transition_types.h"
33 #include "url/gurl.h"
34
35 using base::UserMetricsAction;
36
37 namespace {
38 // Constants for 3D touch application static shortcuts.
39 NSString* const kShortcutNewTab = @"OpenNewTab";
40 NSString* const kShortcutNewIncognitoTab = @"OpenIncognitoTab";
41 NSString* const kShortcutVoiceSearch = @"OpenVoiceSearch";
42 NSString* const kShortcutQRScanner = @"OpenQRScanner";
43 } // namespace
44
45 @interface UserActivityHandler ()
46 // Handles the 3D touch application static items. Does nothing if in first run.
47 + (BOOL)handleShortcutItem:(UIApplicationShortcutItem*)shortcutItem
48 startupInformation:(id<StartupInformation>)startupInformation;
49 // Routes Universal 2nd Factor (U2F) callback to the correct Tab.
50 + (void)routeU2FURL:(const GURL&)URL
51 browserViewInformation:(id<BrowserViewInformation>)browserViewInformation;
52 @end
53
54 @implementation UserActivityHandler
55
56 #pragma mark - Public methods.
57
58 + (BOOL)continueUserActivity:(NSUserActivity*)userActivity
59 applicationIsActive:(BOOL)applicationIsActive
60 tabOpener:(id<TabOpening>)tabOpener
61 startupInformation:(id<StartupInformation>)startupInformation {
62 NSURL* webpageURL = userActivity.webpageURL;
63
64 if ([userActivity.activityType
65 isEqualToString:handoff::kChromeHandoffActivityType]) {
66 // App was launched by iOS as a result of Handoff.
67 NSString* originString = base::mac::ObjCCast<NSString>(
68 userActivity.userInfo[handoff::kOriginKey]);
69 handoff::Origin origin = handoff::OriginFromString(originString);
70 UMA_HISTOGRAM_ENUMERATION("IOS.Handoff.Origin", origin,
71 handoff::ORIGIN_COUNT);
72 } else if ([userActivity.activityType
73 isEqualToString:NSUserActivityTypeBrowsingWeb]) {
74 // App was launched as the result of a Universal Link navigation. The value
75 // of userActivity.webpageURL is not used. The only supported action
76 // at this time is opening a New Tab Page.
77 GURL newTabURL(kChromeUINewTabURL);
78 webpageURL = net::NSURLWithGURL(newTabURL);
79 base::scoped_nsobject<AppStartupParameters> startupParams(
80 [[AppStartupParameters alloc] initWithExternalURL:newTabURL]);
81 [startupInformation setStartupParameters:startupParams];
82 base::RecordAction(base::UserMetricsAction("IOSLaunchedByUniversalLink"));
83 } else if (spotlight::IsSpotlightAvailable() &&
84 [userActivity.activityType
85 isEqualToString:CSSearchableItemActionType]) {
86 // App was launched by iOS as the result of a tap on a Spotlight Search
87 // result.
88 NSString* itemID =
89 [userActivity.userInfo objectForKey:CSSearchableItemActivityIdentifier];
90 spotlight::Domain domain = spotlight::SpotlightDomainFromString(itemID);
91 if (domain == spotlight::DOMAIN_ACTIONS &&
92 !experimental_flags::IsSpotlightActionsEnabled()) {
93 return NO;
94 }
95 UMA_HISTOGRAM_ENUMERATION("IOS.Spotlight.Origin", domain,
96 spotlight::DOMAIN_COUNT);
97
98 if (!itemID) {
99 return NO;
100 }
101 if (domain == spotlight::DOMAIN_ACTIONS) {
102 webpageURL =
103 [NSURL URLWithString:base::SysUTF8ToNSString(kChromeUINewTabURL)];
104 base::scoped_nsobject<AppStartupParameters> startupParams(
105 [[AppStartupParameters alloc]
106 initWithExternalURL:GURL(kChromeUINewTabURL)]);
107 BOOL startupParamsSet = spotlight::SetStartupParametersForSpotlightAction(
108 itemID, startupParams);
109 if (!startupParamsSet) {
110 return NO;
111 }
112 [startupInformation setStartupParameters:startupParams];
113 } else if (!webpageURL && base::ios::IsRunningOnIOS10OrLater()) {
114 // spotlight::GetURLForSpotlightItemID uses CSSearchQuery, which is only
115 // supported from iOS 10.
116 spotlight::GetURLForSpotlightItemID(itemID, ^(NSURL* contentURL) {
117 if (!contentURL) {
118 return;
119 }
120 dispatch_async(dispatch_get_main_queue(), ^{
121 // Update the isActive flag as it may have changed during the async
122 // calls.
123 BOOL isActive = [[UIApplication sharedApplication]
124 applicationState] == UIApplicationStateActive;
125 [self continueUserActivityURL:contentURL
126 applicationIsActive:isActive
127 tabOpener:tabOpener
128 startupInformation:startupInformation];
129 });
130 });
131 return YES;
132 }
133 } else {
134 // Do nothing for unknown activity type.
135 return NO;
136 }
137
138 return [self continueUserActivityURL:webpageURL
139 applicationIsActive:applicationIsActive
140 tabOpener:tabOpener
141 startupInformation:startupInformation];
142 }
143
144 + (BOOL)continueUserActivityURL:(NSURL*)webpageURL
145 applicationIsActive:(BOOL)applicationIsActive
146 tabOpener:(id<TabOpening>)tabOpener
147 startupInformation:(id<StartupInformation>)startupInformation {
148 if (!webpageURL)
149 return NO;
150
151 GURL webpageGURL(net::GURLWithNSURL(webpageURL));
152 if (!webpageGURL.is_valid())
153 return NO;
154
155 if (applicationIsActive && ![startupInformation isPresentingFirstRunUI]) {
156 // The app is already active so the applicationDidBecomeActive: method will
157 // never be called. Open the requested URL immediately.
158 ApplicationMode targetMode =
159 [[startupInformation startupParameters] launchInIncognito]
160 ? ApplicationMode::INCOGNITO
161 : ApplicationMode::NORMAL;
162 [tabOpener dismissModalsAndOpenSelectedTabInMode:targetMode
163 withURL:webpageGURL
164 transition:ui::PAGE_TRANSITION_LINK
165 completion:^{
166 [startupInformation
167 setStartupParameters:nil];
168 }];
169 return YES;
170 }
171
172 // Don't record the first action as a user action, since it will not be
173 // initiated by the user.
174 [startupInformation resetFirstUserActionRecorder];
175
176 if (![startupInformation startupParameters]) {
177 base::scoped_nsobject<AppStartupParameters> startupParams(
178 [[AppStartupParameters alloc] initWithExternalURL:webpageGURL]);
179 [startupInformation setStartupParameters:startupParams];
180 }
181 return YES;
182 }
183
184 + (void)performActionForShortcutItem:(UIApplicationShortcutItem*)shortcutItem
185 completionHandler:(void (^)(BOOL succeeded))completionHandler
186 tabOpener:(id<TabOpening>)tabOpener
187 startupInformation:(id<StartupInformation>)startupInformation
188 browserViewInformation:
189 (id<BrowserViewInformation>)browserViewInformation {
190 BOOL handledShortcutItem =
191 [UserActivityHandler handleShortcutItem:shortcutItem
192 startupInformation:startupInformation];
193 if (handledShortcutItem) {
194 [UserActivityHandler
195 handleStartupParametersWithTabOpener:tabOpener
196 startupInformation:startupInformation
197 browserViewInformation:browserViewInformation];
198 }
199 completionHandler(handledShortcutItem);
200 }
201
202 + (BOOL)willContinueUserActivityWithType:(NSString*)userActivityType {
203 return
204 [userActivityType isEqualToString:handoff::kChromeHandoffActivityType] ||
205 (spotlight::IsSpotlightAvailable() &&
206 [userActivityType isEqualToString:CSSearchableItemActionType]);
207 }
208
209 + (void)handleStartupParametersWithTabOpener:(id<TabOpening>)tabOpener
210 startupInformation:
211 (id<StartupInformation>)startupInformation
212 browserViewInformation:
213 (id<BrowserViewInformation>)browserViewInformation {
214 DCHECK([startupInformation startupParameters]);
215 // Do not load the external URL if the user has not accepted the terms of
216 // service. This corresponds to the case when the user installed Chrome,
217 // has never launched it and attempts to open an external URL in Chrome.
218 if ([startupInformation isPresentingFirstRunUI])
219 return;
220
221 // Check if it's an U2F call. If so, route it to correct tab.
222 // If not, open or reuse tab in main BVC.
223 if ([U2FController
224 isU2FURL:[[startupInformation startupParameters] externalURL]]) {
225 [UserActivityHandler routeU2FURL:[[startupInformation startupParameters]
226 externalURL]
227 browserViewInformation:browserViewInformation];
228 // It's OK to clear startup parameters here because routeU2FURL works
229 // synchronously.
230 [startupInformation setStartupParameters:nil];
231 } else {
232 // The app is already active so the applicationDidBecomeActive: method
233 // will never be called. Open the requested URL after all modal UIs have
234 // been dismissed. |_startupParameters| must be retained until all deferred
235 // modal UIs are dismissed and tab opened with requested URL.
236 ApplicationMode targetMode =
237 [[startupInformation startupParameters] launchInIncognito]
238 ? ApplicationMode::INCOGNITO
239 : ApplicationMode::NORMAL;
240 [tabOpener dismissModalsAndOpenSelectedTabInMode:targetMode
241 withURL:[[startupInformation
242 startupParameters]
243 externalURL]
244 transition:ui::PAGE_TRANSITION_LINK
245 completion:^{
246 [startupInformation
247 setStartupParameters:nil];
248 }];
249 }
250 }
251
252 #pragma mark - Internal methods.
253
254 + (BOOL)handleShortcutItem:(UIApplicationShortcutItem*)shortcutItem
255 startupInformation:(id<StartupInformation>)startupInformation {
256 if ([startupInformation isPresentingFirstRunUI])
257 return NO;
258
259 base::scoped_nsobject<AppStartupParameters> startupParams(
260 [[AppStartupParameters alloc]
261 initWithExternalURL:GURL(kChromeUINewTabURL)]);
262
263 if ([shortcutItem.type isEqualToString:kShortcutNewTab]) {
264 base::RecordAction(UserMetricsAction("ApplicationShortcut.NewTabPressed"));
265 [startupInformation setStartupParameters:startupParams];
266 return YES;
267
268 } else if ([shortcutItem.type isEqualToString:kShortcutNewIncognitoTab]) {
269 base::RecordAction(
270 UserMetricsAction("ApplicationShortcut.NewIncognitoTabPressed"));
271 [startupParams setLaunchInIncognito:YES];
272 [startupInformation setStartupParameters:startupParams];
273 return YES;
274
275 } else if ([shortcutItem.type isEqualToString:kShortcutVoiceSearch]) {
276 base::RecordAction(
277 UserMetricsAction("ApplicationShortcut.VoiceSearchPressed"));
278 [startupParams setLaunchVoiceSearch:YES];
279 [startupInformation setStartupParameters:startupParams];
280 return YES;
281
282 } else if ([shortcutItem.type isEqualToString:kShortcutQRScanner]) {
283 if (experimental_flags::IsQRCodeReaderEnabled()) {
284 base::RecordAction(
285 UserMetricsAction("ApplicationShortcut.ScanQRCodePressed"));
286 [startupParams setLaunchQRScanner:YES];
287 }
288 [startupInformation setStartupParameters:startupParams];
289 return YES;
290 }
291
292 NOTREACHED();
293 return NO;
294 }
295
296 + (void)routeU2FURL:(const GURL&)URL
297 browserViewInformation:(id<BrowserViewInformation>)browserViewInformation {
298 // Retrieve the designated TabID from U2F URL.
299 NSString* tabID = [U2FController tabIDFromResponseURL:URL];
300 if (!tabID) {
301 return;
302 }
303
304 // TODO(crbug.com/619598): move this code to BrowserViewInformation to hide
305 // implementation details of TabModel.
306 // Iterate through mainTabModel and OTRTabModel to find the corresponding tab.
307 NSArray* tabModels = @[
308 [browserViewInformation mainTabModel], [browserViewInformation otrTabModel]
309 ];
310 for (TabModel* tabModel in tabModels) {
311 for (Tab* tab in tabModel) {
312 if ([tab.tabId isEqualToString:tabID]) {
313 [tab evaluateU2FResultFromURL:URL];
314 return;
315 }
316 }
317 }
318 }
319
320 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698