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

Side by Side Diff: ios/chrome/browser/ui/activity_services/activity_service_controller.mm

Issue 2585233003: Upstream Chrome on iOS source code [2/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 2014 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/browser/ui/activity_services/activity_service_controller.h"
6
7 #import <MobileCoreServices/MobileCoreServices.h>
8
9 #include "base/logging.h"
10 #include "base/mac/foundation_util.h"
11 #include "components/reading_list/core/reading_list_switches.h"
12 #import "ios/chrome/browser/ui/activity_services/activity_type_util.h"
13 #import "ios/chrome/browser/ui/activity_services/appex_constants.h"
14 #import "ios/chrome/browser/ui/activity_services/chrome_activity_item_source.h"
15 #import "ios/chrome/browser/ui/activity_services/print_activity.h"
16 #import "ios/chrome/browser/ui/activity_services/reading_list_activity.h"
17 #import "ios/chrome/browser/ui/activity_services/share_protocol.h"
18 #import "ios/chrome/browser/ui/activity_services/share_to_data.h"
19 #include "ios/chrome/browser/ui/ui_util.h"
20
21 #if !defined(__has_feature) || !__has_feature(objc_arc)
22 #error "This file requires ARC support."
23 #endif
24
25 @interface ActivityServiceController () {
26 BOOL active_;
27 id<ShareToDelegate> shareToDelegate_;
28 UIActivityViewController* activityViewController_;
29 }
30
31 // Resets the controller's user interface and delegate.
32 - (void)resetUserInterface;
33 // Called when UIActivityViewController user interface is dismissed by user
34 // signifying the end of the Share/Action activity.
35 - (void)shareFinishedWithActivityType:(NSString*)activityType
36 completed:(BOOL)completed
37 returnedItems:(NSArray*)returnedItems
38 error:(NSError*)activityError;
39 // Returns an array of UIActivityItemSource objects to provide the |data| to
40 // share to the sharing activities.
41 - (NSArray*)activityItemsForData:(ShareToData*)data;
42 // Returns an array of UIActivity objects that can handle the given |data|.
43 - (NSArray*)applicationActivitiesForData:(ShareToData*)data
44 controller:(UIViewController*)controller;
45 // Processes |extensionItems| returned from App Extension invocation returning
46 // the |activityType|. Calls shareDelegate_ with the processed returned items
47 // and |result| of activity. Returns whether caller should reset UI.
48 - (BOOL)processItemsReturnedFromActivity:(NSString*)activityType
49 status:(ShareTo::ShareResult)result
50 items:(NSArray*)extensionItems;
51 @end
52
53 @implementation ActivityServiceController
54
55 + (ActivityServiceController*)sharedInstance {
56 static ActivityServiceController* instance =
57 [[ActivityServiceController alloc] init];
58 return instance;
59 }
60
61 #pragma mark - ShareProtocol
62
63 - (BOOL)isActive {
64 return active_;
65 }
66
67 - (void)cancelShareAnimated:(BOOL)animated {
68 if (!active_) {
69 return;
70 }
71 DCHECK(activityViewController_);
72 // There is no guarantee that the completion callback will be called because
73 // the |activityViewController_| may have been dismissed already. For example,
74 // if the user selects Facebook Share Extension, the UIActivityViewController
75 // is first dismissed and then the UI for Facebook Share Extension comes up.
76 // At this time, if the user backgrounds Chrome and then relaunch Chrome
77 // through an external app (e.g. with googlechrome://url.com), Chrome restart
78 // dismisses the modal UI coming through this path. But since the
79 // UIActivityViewController has already been dismissed, the following method
80 // does nothing and completion callback is not called. The call
81 // -shareFinishedWithActivityType:completed:returnedItems:error: must be
82 // called explicitly to do the clean up or else future attempts to use
83 // Share will fail.
84 [activityViewController_ dismissViewControllerAnimated:animated
85 completion:nil];
86 [self shareFinishedWithActivityType:nil
87 completed:NO
88 returnedItems:nil
89 error:nil];
90 }
91
92 - (void)shareWithData:(ShareToData*)data
93 controller:(UIViewController*)controller
94 browserState:(ios::ChromeBrowserState*)browserState
95 shareToDelegate:(id<ShareToDelegate>)delegate
96 fromRect:(CGRect)fromRect
97 inView:(UIView*)inView {
98 DCHECK(controller);
99 DCHECK(data);
100 DCHECK(!active_);
101 DCHECK(!shareToDelegate_);
102 if (IsIPadIdiom()) {
103 DCHECK(fromRect.size.height);
104 DCHECK(fromRect.size.width);
105 DCHECK(inView);
106 }
107
108 DCHECK(!activityViewController_);
109 shareToDelegate_ = delegate;
110 activityViewController_ = [[UIActivityViewController alloc]
111 initWithActivityItems:[self activityItemsForData:data]
112 applicationActivities:[self applicationActivitiesForData:data
113 controller:controller]];
114
115 // Reading List and Print activities refer to iOS' version of these.
116 // Chrome-specific implementations of these two activities are provided below
117 // in applicationActivitiesForData:controller:
118 NSArray* excludedActivityTypes = @[
119 UIActivityTypeAddToReadingList, UIActivityTypePrint,
120 UIActivityTypeSaveToCameraRoll
121 ];
122 [activityViewController_ setExcludedActivityTypes:excludedActivityTypes];
123 // Although |completionWithItemsHandler:...| is not present in the iOS
124 // documentation, it is mentioned in the WWDC presentations (specifically
125 // 217_creating_extensions_for_ios_and_os_x_part_2.pdf) and available in
126 // header file UIKit.framework/UIActivityViewController.h as @property.
127 DCHECK([activityViewController_
128 respondsToSelector:@selector(setCompletionWithItemsHandler:)]);
129 __weak ActivityServiceController* weakSelf = self;
130 [activityViewController_ setCompletionWithItemsHandler:^(
131 NSString* activityType, BOOL completed,
132 NSArray* returnedItems, NSError* activityError) {
133 [weakSelf shareFinishedWithActivityType:activityType
134 completed:completed
135 returnedItems:returnedItems
136 error:activityError];
137 }];
138
139 active_ = YES;
140 activityViewController_.modalPresentationStyle = UIModalPresentationPopover;
141 activityViewController_.popoverPresentationController.sourceView = inView;
142 activityViewController_.popoverPresentationController.sourceRect = fromRect;
143 [controller presentViewController:activityViewController_
144 animated:YES
145 completion:nil];
146 }
147
148 #pragma mark - Private
149
150 - (void)resetUserInterface {
151 shareToDelegate_ = nil;
152 activityViewController_ = nil;
153 active_ = NO;
154 }
155
156 - (void)shareFinishedWithActivityType:(NSString*)activityType
157 completed:(BOOL)completed
158 returnedItems:(NSArray*)returnedItems
159 error:(NSError*)activityError {
160 DCHECK(active_);
161 DCHECK(shareToDelegate_);
162
163 BOOL shouldResetUI = YES;
164 if (activityType) {
165 ShareTo::ShareResult shareResult = completed
166 ? ShareTo::ShareResult::SHARE_SUCCESS
167 : ShareTo::ShareResult::SHARE_CANCEL;
168 if (activity_type_util::IsPasswordAppExActivity(activityType)) {
169 // A compatible Password Management App Extension was invoked.
170 shouldResetUI = [self processItemsReturnedFromActivity:activityType
171 status:shareResult
172 items:returnedItems];
173 } else {
174 activity_type_util::ActivityType type =
175 activity_type_util::TypeFromString(activityType);
176 activity_type_util::RecordMetricForActivity(type);
177 NSString* successMessage =
178 activity_type_util::SuccessMessageForActivity(type);
179 [shareToDelegate_ shareDidComplete:shareResult
180 successMessage:successMessage];
181 }
182 } else {
183 [shareToDelegate_ shareDidComplete:ShareTo::ShareResult::SHARE_CANCEL
184 successMessage:nil];
185 }
186 if (shouldResetUI)
187 [self resetUserInterface];
188 }
189
190 - (NSArray*)activityItemsForData:(ShareToData*)data {
191 NSMutableArray* activityItems = [NSMutableArray array];
192 // ShareToData object guarantees that there is a NSURL.
193 DCHECK(data.nsurl);
194
195 // In order to support find-login-action protocol, the provider object
196 // UIActivityFindLoginActionSource supports both Password Management
197 // App Extensions (e.g. 1Password) and also provide a public.url UTType
198 // for Share Extensions (e.g. Facebook, Twitter).
199 UIActivityFindLoginActionSource* loginActionProvider =
200 [[UIActivityFindLoginActionSource alloc] initWithURL:data.nsurl
201 subject:data.title];
202 [activityItems addObject:loginActionProvider];
203
204 UIActivityTextSource* textProvider =
205 [[UIActivityTextSource alloc] initWithText:data.title];
206 [activityItems addObject:textProvider];
207
208 if (data.image) {
209 UIActivityImageSource* imageProvider =
210 [[UIActivityImageSource alloc] initWithImage:data.image];
211 [activityItems addObject:imageProvider];
212 }
213
214 return activityItems;
215 }
216
217 - (NSArray*)applicationActivitiesForData:(ShareToData*)data
218 controller:(UIViewController*)controller {
219 NSMutableArray* applicationActivities = [NSMutableArray array];
220 if (data.isPagePrintable) {
221 PrintActivity* printActivity = [[PrintActivity alloc] init];
222 [printActivity setResponder:controller];
223 [applicationActivities addObject:printActivity];
224 }
225 if (reading_list::switches::IsReadingListEnabled()) {
226 ReadingListActivity* readingListActivity =
227 [[ReadingListActivity alloc] initWithURL:data.url
228 title:data.title
229 responder:controller];
230 [applicationActivities addObject:readingListActivity];
231 }
232 return applicationActivities;
233 }
234
235 - (BOOL)processItemsReturnedFromActivity:(NSString*)activityType
236 status:(ShareTo::ShareResult)result
237 items:(NSArray*)extensionItems {
238 NSItemProvider* itemProvider = nil;
239 if ([extensionItems count] > 0) {
240 // Based on calling convention described in
241 // https://github.com/AgileBits/onepassword-app-extension/blob/master/OnePas swordExtension.m
242 // the username/password is always in the first element of the returned
243 // item.
244 NSExtensionItem* extensionItem = extensionItems[0];
245 // Checks that there is at least one attachment and that the attachment
246 // is a property list which can be converted into a NSDictionary object.
247 // If not, early return.
248 if (extensionItem.attachments.count > 0) {
249 itemProvider = [extensionItem.attachments objectAtIndex:0];
250 if (![itemProvider
251 hasItemConformingToTypeIdentifier:(NSString*)kUTTypePropertyList])
252 itemProvider = nil;
253 }
254 }
255 if (!itemProvider) {
256 // ShareToDelegate callback method must still be called on incorrect
257 // |extensionItems|.
258 [shareToDelegate_ passwordAppExDidFinish:ShareTo::ShareResult::SHARE_ERROR
259 username:nil
260 password:nil
261 successMessage:nil];
262 return YES;
263 }
264
265 // |completionHandler| is the block that will be executed once the
266 // property list has been loaded from the attachment.
267 void (^completionHandler)(id, NSError*) = ^(id item, NSError* error) {
268 ShareTo::ShareResult activityResult = result;
269 NSString* username = nil;
270 NSString* password = nil;
271 NSString* message = nil;
272 NSDictionary* loginDictionary = base::mac::ObjCCast<NSDictionary>(item);
273 if (error || !loginDictionary) {
274 activityResult = ShareTo::ShareResult::SHARE_ERROR;
275 } else {
276 username = loginDictionary[activity_services::kPasswordAppExUsernameKey];
277 password = loginDictionary[activity_services::kPasswordAppExPasswordKey];
278 activity_type_util::ActivityType type =
279 activity_type_util::TypeFromString(activityType);
280 activity_type_util::RecordMetricForActivity(type);
281 message = activity_type_util::SuccessMessageForActivity(type);
282 }
283 [shareToDelegate_ passwordAppExDidFinish:activityResult
284 username:username
285 password:password
286 successMessage:message];
287 // Controller state can be reset only after delegate has processed the
288 // item returned from the App Extension.
289 [self resetUserInterface];
290 };
291 [itemProvider loadItemForTypeIdentifier:(NSString*)kUTTypePropertyList
292 options:nil
293 completionHandler:completionHandler];
294 return NO;
295 }
296
297 #pragma mark - For Testing
298
299 - (void)setShareToDelegateForTesting:(id<ShareToDelegate>)delegate {
300 shareToDelegate_ = delegate;
301 }
302
303 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698