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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: ios/chrome/browser/ui/activity_services/activity_service_controller.mm
diff --git a/ios/chrome/browser/ui/activity_services/activity_service_controller.mm b/ios/chrome/browser/ui/activity_services/activity_service_controller.mm
new file mode 100644
index 0000000000000000000000000000000000000000..e5a359f87cabb2df55c43afaedec94545d386477
--- /dev/null
+++ b/ios/chrome/browser/ui/activity_services/activity_service_controller.mm
@@ -0,0 +1,303 @@
+// Copyright 2014 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/ui/activity_services/activity_service_controller.h"
+
+#import <MobileCoreServices/MobileCoreServices.h>
+
+#include "base/logging.h"
+#include "base/mac/foundation_util.h"
+#include "components/reading_list/core/reading_list_switches.h"
+#import "ios/chrome/browser/ui/activity_services/activity_type_util.h"
+#import "ios/chrome/browser/ui/activity_services/appex_constants.h"
+#import "ios/chrome/browser/ui/activity_services/chrome_activity_item_source.h"
+#import "ios/chrome/browser/ui/activity_services/print_activity.h"
+#import "ios/chrome/browser/ui/activity_services/reading_list_activity.h"
+#import "ios/chrome/browser/ui/activity_services/share_protocol.h"
+#import "ios/chrome/browser/ui/activity_services/share_to_data.h"
+#include "ios/chrome/browser/ui/ui_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface ActivityServiceController () {
+ BOOL active_;
+ id<ShareToDelegate> shareToDelegate_;
+ UIActivityViewController* activityViewController_;
+}
+
+// Resets the controller's user interface and delegate.
+- (void)resetUserInterface;
+// Called when UIActivityViewController user interface is dismissed by user
+// signifying the end of the Share/Action activity.
+- (void)shareFinishedWithActivityType:(NSString*)activityType
+ completed:(BOOL)completed
+ returnedItems:(NSArray*)returnedItems
+ error:(NSError*)activityError;
+// Returns an array of UIActivityItemSource objects to provide the |data| to
+// share to the sharing activities.
+- (NSArray*)activityItemsForData:(ShareToData*)data;
+// Returns an array of UIActivity objects that can handle the given |data|.
+- (NSArray*)applicationActivitiesForData:(ShareToData*)data
+ controller:(UIViewController*)controller;
+// Processes |extensionItems| returned from App Extension invocation returning
+// the |activityType|. Calls shareDelegate_ with the processed returned items
+// and |result| of activity. Returns whether caller should reset UI.
+- (BOOL)processItemsReturnedFromActivity:(NSString*)activityType
+ status:(ShareTo::ShareResult)result
+ items:(NSArray*)extensionItems;
+@end
+
+@implementation ActivityServiceController
+
++ (ActivityServiceController*)sharedInstance {
+ static ActivityServiceController* instance =
+ [[ActivityServiceController alloc] init];
+ return instance;
+}
+
+#pragma mark - ShareProtocol
+
+- (BOOL)isActive {
+ return active_;
+}
+
+- (void)cancelShareAnimated:(BOOL)animated {
+ if (!active_) {
+ return;
+ }
+ DCHECK(activityViewController_);
+ // There is no guarantee that the completion callback will be called because
+ // the |activityViewController_| may have been dismissed already. For example,
+ // if the user selects Facebook Share Extension, the UIActivityViewController
+ // is first dismissed and then the UI for Facebook Share Extension comes up.
+ // At this time, if the user backgrounds Chrome and then relaunch Chrome
+ // through an external app (e.g. with googlechrome://url.com), Chrome restart
+ // dismisses the modal UI coming through this path. But since the
+ // UIActivityViewController has already been dismissed, the following method
+ // does nothing and completion callback is not called. The call
+ // -shareFinishedWithActivityType:completed:returnedItems:error: must be
+ // called explicitly to do the clean up or else future attempts to use
+ // Share will fail.
+ [activityViewController_ dismissViewControllerAnimated:animated
+ completion:nil];
+ [self shareFinishedWithActivityType:nil
+ completed:NO
+ returnedItems:nil
+ error:nil];
+}
+
+- (void)shareWithData:(ShareToData*)data
+ controller:(UIViewController*)controller
+ browserState:(ios::ChromeBrowserState*)browserState
+ shareToDelegate:(id<ShareToDelegate>)delegate
+ fromRect:(CGRect)fromRect
+ inView:(UIView*)inView {
+ DCHECK(controller);
+ DCHECK(data);
+ DCHECK(!active_);
+ DCHECK(!shareToDelegate_);
+ if (IsIPadIdiom()) {
+ DCHECK(fromRect.size.height);
+ DCHECK(fromRect.size.width);
+ DCHECK(inView);
+ }
+
+ DCHECK(!activityViewController_);
+ shareToDelegate_ = delegate;
+ activityViewController_ = [[UIActivityViewController alloc]
+ initWithActivityItems:[self activityItemsForData:data]
+ applicationActivities:[self applicationActivitiesForData:data
+ controller:controller]];
+
+ // Reading List and Print activities refer to iOS' version of these.
+ // Chrome-specific implementations of these two activities are provided below
+ // in applicationActivitiesForData:controller:
+ NSArray* excludedActivityTypes = @[
+ UIActivityTypeAddToReadingList, UIActivityTypePrint,
+ UIActivityTypeSaveToCameraRoll
+ ];
+ [activityViewController_ setExcludedActivityTypes:excludedActivityTypes];
+ // Although |completionWithItemsHandler:...| is not present in the iOS
+ // documentation, it is mentioned in the WWDC presentations (specifically
+ // 217_creating_extensions_for_ios_and_os_x_part_2.pdf) and available in
+ // header file UIKit.framework/UIActivityViewController.h as @property.
+ DCHECK([activityViewController_
+ respondsToSelector:@selector(setCompletionWithItemsHandler:)]);
+ __weak ActivityServiceController* weakSelf = self;
+ [activityViewController_ setCompletionWithItemsHandler:^(
+ NSString* activityType, BOOL completed,
+ NSArray* returnedItems, NSError* activityError) {
+ [weakSelf shareFinishedWithActivityType:activityType
+ completed:completed
+ returnedItems:returnedItems
+ error:activityError];
+ }];
+
+ active_ = YES;
+ activityViewController_.modalPresentationStyle = UIModalPresentationPopover;
+ activityViewController_.popoverPresentationController.sourceView = inView;
+ activityViewController_.popoverPresentationController.sourceRect = fromRect;
+ [controller presentViewController:activityViewController_
+ animated:YES
+ completion:nil];
+}
+
+#pragma mark - Private
+
+- (void)resetUserInterface {
+ shareToDelegate_ = nil;
+ activityViewController_ = nil;
+ active_ = NO;
+}
+
+- (void)shareFinishedWithActivityType:(NSString*)activityType
+ completed:(BOOL)completed
+ returnedItems:(NSArray*)returnedItems
+ error:(NSError*)activityError {
+ DCHECK(active_);
+ DCHECK(shareToDelegate_);
+
+ BOOL shouldResetUI = YES;
+ if (activityType) {
+ ShareTo::ShareResult shareResult = completed
+ ? ShareTo::ShareResult::SHARE_SUCCESS
+ : ShareTo::ShareResult::SHARE_CANCEL;
+ if (activity_type_util::IsPasswordAppExActivity(activityType)) {
+ // A compatible Password Management App Extension was invoked.
+ shouldResetUI = [self processItemsReturnedFromActivity:activityType
+ status:shareResult
+ items:returnedItems];
+ } else {
+ activity_type_util::ActivityType type =
+ activity_type_util::TypeFromString(activityType);
+ activity_type_util::RecordMetricForActivity(type);
+ NSString* successMessage =
+ activity_type_util::SuccessMessageForActivity(type);
+ [shareToDelegate_ shareDidComplete:shareResult
+ successMessage:successMessage];
+ }
+ } else {
+ [shareToDelegate_ shareDidComplete:ShareTo::ShareResult::SHARE_CANCEL
+ successMessage:nil];
+ }
+ if (shouldResetUI)
+ [self resetUserInterface];
+}
+
+- (NSArray*)activityItemsForData:(ShareToData*)data {
+ NSMutableArray* activityItems = [NSMutableArray array];
+ // ShareToData object guarantees that there is a NSURL.
+ DCHECK(data.nsurl);
+
+ // In order to support find-login-action protocol, the provider object
+ // UIActivityFindLoginActionSource supports both Password Management
+ // App Extensions (e.g. 1Password) and also provide a public.url UTType
+ // for Share Extensions (e.g. Facebook, Twitter).
+ UIActivityFindLoginActionSource* loginActionProvider =
+ [[UIActivityFindLoginActionSource alloc] initWithURL:data.nsurl
+ subject:data.title];
+ [activityItems addObject:loginActionProvider];
+
+ UIActivityTextSource* textProvider =
+ [[UIActivityTextSource alloc] initWithText:data.title];
+ [activityItems addObject:textProvider];
+
+ if (data.image) {
+ UIActivityImageSource* imageProvider =
+ [[UIActivityImageSource alloc] initWithImage:data.image];
+ [activityItems addObject:imageProvider];
+ }
+
+ return activityItems;
+}
+
+- (NSArray*)applicationActivitiesForData:(ShareToData*)data
+ controller:(UIViewController*)controller {
+ NSMutableArray* applicationActivities = [NSMutableArray array];
+ if (data.isPagePrintable) {
+ PrintActivity* printActivity = [[PrintActivity alloc] init];
+ [printActivity setResponder:controller];
+ [applicationActivities addObject:printActivity];
+ }
+ if (reading_list::switches::IsReadingListEnabled()) {
+ ReadingListActivity* readingListActivity =
+ [[ReadingListActivity alloc] initWithURL:data.url
+ title:data.title
+ responder:controller];
+ [applicationActivities addObject:readingListActivity];
+ }
+ return applicationActivities;
+}
+
+- (BOOL)processItemsReturnedFromActivity:(NSString*)activityType
+ status:(ShareTo::ShareResult)result
+ items:(NSArray*)extensionItems {
+ NSItemProvider* itemProvider = nil;
+ if ([extensionItems count] > 0) {
+ // Based on calling convention described in
+ // https://github.com/AgileBits/onepassword-app-extension/blob/master/OnePasswordExtension.m
+ // the username/password is always in the first element of the returned
+ // item.
+ NSExtensionItem* extensionItem = extensionItems[0];
+ // Checks that there is at least one attachment and that the attachment
+ // is a property list which can be converted into a NSDictionary object.
+ // If not, early return.
+ if (extensionItem.attachments.count > 0) {
+ itemProvider = [extensionItem.attachments objectAtIndex:0];
+ if (![itemProvider
+ hasItemConformingToTypeIdentifier:(NSString*)kUTTypePropertyList])
+ itemProvider = nil;
+ }
+ }
+ if (!itemProvider) {
+ // ShareToDelegate callback method must still be called on incorrect
+ // |extensionItems|.
+ [shareToDelegate_ passwordAppExDidFinish:ShareTo::ShareResult::SHARE_ERROR
+ username:nil
+ password:nil
+ successMessage:nil];
+ return YES;
+ }
+
+ // |completionHandler| is the block that will be executed once the
+ // property list has been loaded from the attachment.
+ void (^completionHandler)(id, NSError*) = ^(id item, NSError* error) {
+ ShareTo::ShareResult activityResult = result;
+ NSString* username = nil;
+ NSString* password = nil;
+ NSString* message = nil;
+ NSDictionary* loginDictionary = base::mac::ObjCCast<NSDictionary>(item);
+ if (error || !loginDictionary) {
+ activityResult = ShareTo::ShareResult::SHARE_ERROR;
+ } else {
+ username = loginDictionary[activity_services::kPasswordAppExUsernameKey];
+ password = loginDictionary[activity_services::kPasswordAppExPasswordKey];
+ activity_type_util::ActivityType type =
+ activity_type_util::TypeFromString(activityType);
+ activity_type_util::RecordMetricForActivity(type);
+ message = activity_type_util::SuccessMessageForActivity(type);
+ }
+ [shareToDelegate_ passwordAppExDidFinish:activityResult
+ username:username
+ password:password
+ successMessage:message];
+ // Controller state can be reset only after delegate has processed the
+ // item returned from the App Extension.
+ [self resetUserInterface];
+ };
+ [itemProvider loadItemForTypeIdentifier:(NSString*)kUTTypePropertyList
+ options:nil
+ completionHandler:completionHandler];
+ return NO;
+}
+
+#pragma mark - For Testing
+
+- (void)setShareToDelegateForTesting:(id<ShareToDelegate>)delegate {
+ shareToDelegate_ = delegate;
+}
+
+@end

Powered by Google App Engine
This is Rietveld 408576698