Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 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 | 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 <MobileCoreServices/MobileCoreServices.h> | 5 #import <MobileCoreServices/MobileCoreServices.h> |
| 6 | 6 |
| 7 #import "ios/chrome/share_extension/share_view_controller.h" | 7 #import "ios/chrome/share_extension/share_view_controller.h" |
| 8 | 8 |
| 9 #import "base/ios/block_types.h" | 9 #import "base/ios/block_types.h" |
| 10 #import "base/mac/foundation_util.h" | 10 #import "base/mac/foundation_util.h" |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 27 const CGFloat kShareExtensionMaxWidth = 390; | 27 const CGFloat kShareExtensionMaxWidth = 390; |
| 28 // Clip the last separator out of the table view. | 28 // Clip the last separator out of the table view. |
| 29 const CGFloat kScreenShotWidth = 100; | 29 const CGFloat kScreenShotWidth = 100; |
| 30 const CGFloat kScreenShotHeight = 100; | 30 const CGFloat kScreenShotHeight = 100; |
| 31 const CGFloat kAnimationDuration = 0.3; | 31 const CGFloat kAnimationDuration = 0.3; |
| 32 const CGFloat kMediumAlpha = 0.5; | 32 const CGFloat kMediumAlpha = 0.5; |
| 33 | 33 |
| 34 } // namespace | 34 } // namespace |
| 35 | 35 |
| 36 @interface ShareViewController ()<ShareExtensionViewActionTarget> { | 36 @interface ShareViewController ()<ShareExtensionViewActionTarget> { |
| 37 // This constraint the center of the widget to be vertically in the center | 37 // This constrains the center of the widget to be vertically in the center |
|
gambard
2017/01/02 10:55:08
Why adding a "s" to constraint?
Olivier
2017/01/02 12:23:31
Because constraint is a noun and constrain is a ve
| |
| 38 // of the the screen. It has to be modified for the dismissal animation. | 38 // of the the screen. It has to be modified for the appearance and dismissal |
| 39 NSLayoutConstraint* _centerYConstraint; | 39 // animation. |
| 40 NSLayoutConstraint* _widgetVerticalPlacementConstraint; | |
| 40 | 41 |
| 41 NSURL* _shareURL; | 42 NSURL* _shareURL; |
| 42 NSString* _shareTitle; | 43 NSString* _shareTitle; |
| 44 UIImage* _image; | |
| 43 NSExtensionItem* _shareItem; | 45 NSExtensionItem* _shareItem; |
| 44 } | 46 } |
| 45 | 47 |
| 46 @property(nonatomic, weak) UIView* maskView; | 48 @property(nonatomic, weak) UIView* maskView; |
| 47 @property(nonatomic, weak) ShareExtensionView* shareView; | 49 @property(nonatomic, weak) ShareExtensionView* shareView; |
| 48 @property(nonatomic, assign) app_group::ShareExtensionItemType itemType; | 50 @property(nonatomic, assign) app_group::ShareExtensionItemType itemType; |
| 49 | 51 |
| 50 // Creates a files in |app_group::ShareExtensionItemsFolder()| containing a | 52 // Creates a files in |app_group::ShareExtensionItemsFolder()| containing a |
| 51 // serialized NSDictionary. | 53 // serialized NSDictionary. |
| 52 // If |cancel| is true, |actionType| is ignored. | 54 // If |cancel| is true, |actionType| is ignored. |
| 53 - (void)queueActionItemURL:(NSURL*)URL | 55 - (void)queueActionItemURL:(NSURL*)URL |
| 54 title:(NSString*)title | 56 title:(NSString*)title |
| 55 action:(app_group::ShareExtensionItemType)actionType | 57 action:(app_group::ShareExtensionItemType)actionType |
| 56 cancel:(BOOL)cancel | 58 cancel:(BOOL)cancel |
| 57 completion:(ProceduralBlock)completion; | 59 completion:(ProceduralBlock)completion; |
| 58 | 60 |
| 59 // Loads all the shared elements from the extension context and update the UI. | 61 // Loads all the shared elements from the extension context and update the UI. |
| 60 - (void)loadElementsFromContext; | 62 - (void)loadElementsFromContext; |
| 61 | 63 |
| 62 // Sets constaints to the widget so that margin are at least | 64 // Sets constaints to the widget so that margin are at least |
| 63 // kShareExtensionMargin points and widget width is closest up to | 65 // kShareExtensionMargin points and widget width is closest up to |
| 64 // kShareExtensionMaxWidth points. | 66 // kShareExtensionMaxWidth points. |
| 65 - (void)constrainWidgetWidth; | 67 - (void)constrainWidgetWidth; |
| 66 | 68 |
| 69 // Displays the normal share view. | |
| 70 - (void)displayShareView; | |
| 71 | |
| 72 // Displays an alert to report an error to the user. | |
| 73 - (void)displayErrorView; | |
| 74 | |
| 67 @end | 75 @end |
| 68 | 76 |
| 69 @implementation ShareViewController | 77 @implementation ShareViewController |
| 70 | 78 |
| 71 @synthesize maskView = _maskView; | 79 @synthesize maskView = _maskView; |
| 72 @synthesize shareView = _shareView; | 80 @synthesize shareView = _shareView; |
| 73 @synthesize itemType = _itemType; | 81 @synthesize itemType = _itemType; |
| 74 | 82 |
| 75 #pragma mark - UIViewController | 83 #pragma mark - UIViewController |
| 76 | 84 |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 88 // This view is the main view of the share extension. | 96 // This view is the main view of the share extension. |
| 89 ShareExtensionView* shareView = | 97 ShareExtensionView* shareView = |
| 90 [[ShareExtensionView alloc] initWithActionTarget:self]; | 98 [[ShareExtensionView alloc] initWithActionTarget:self]; |
| 91 [self setShareView:shareView]; | 99 [self setShareView:shareView]; |
| 92 [self.view addSubview:self.shareView]; | 100 [self.view addSubview:self.shareView]; |
| 93 | 101 |
| 94 [self constrainWidgetWidth]; | 102 [self constrainWidgetWidth]; |
| 95 | 103 |
| 96 // Position the widget below the screen. It will be slided up with an | 104 // Position the widget below the screen. It will be slided up with an |
| 97 // animation. | 105 // animation. |
| 98 _centerYConstraint = [[shareView centerYAnchor] | 106 _widgetVerticalPlacementConstraint = |
| 99 constraintEqualToAnchor:[self.view centerYAnchor]]; | 107 [[shareView topAnchor] constraintEqualToAnchor:[self.view bottomAnchor]]; |
| 100 [_centerYConstraint setConstant:(self.view.frame.size.height + | 108 [_widgetVerticalPlacementConstraint setActive:YES]; |
| 101 self.shareView.frame.size.height) / | |
| 102 2]; | |
| 103 [_centerYConstraint setActive:YES]; | |
| 104 [[[shareView centerXAnchor] constraintEqualToAnchor:[self.view centerXAnchor]] | 109 [[[shareView centerXAnchor] constraintEqualToAnchor:[self.view centerXAnchor]] |
| 105 setActive:YES]; | 110 setActive:YES]; |
| 106 | 111 |
| 107 [self.maskView setTranslatesAutoresizingMaskIntoConstraints:NO]; | 112 [self.maskView setTranslatesAutoresizingMaskIntoConstraints:NO]; |
| 108 [self.shareView setTranslatesAutoresizingMaskIntoConstraints:NO]; | 113 [self.shareView setTranslatesAutoresizingMaskIntoConstraints:NO]; |
| 109 | 114 |
| 110 [self loadElementsFromContext]; | 115 [self loadElementsFromContext]; |
| 111 } | 116 } |
| 112 | 117 |
| 113 - (void)viewWillAppear:(BOOL)animated { | |
| 114 [super viewWillAppear:animated]; | |
| 115 | |
| 116 // Center the widget. | |
| 117 [_centerYConstraint setConstant:0]; | |
| 118 [self.maskView setAlpha:0]; | |
| 119 [UIView animateWithDuration:kAnimationDuration | |
| 120 animations:^{ | |
| 121 [self.maskView setAlpha:1]; | |
| 122 [self.view layoutIfNeeded]; | |
| 123 }]; | |
| 124 } | |
| 125 | |
| 126 #pragma mark - Private methods | 118 #pragma mark - Private methods |
| 127 | 119 |
| 120 - (void)displayShareView { | |
| 121 [self.shareView setTitle:_shareTitle]; | |
| 122 [self.shareView setURL:_shareURL]; | |
| 123 if (_image) { | |
| 124 [self.shareView setScreenshot:_image]; | |
| 125 } | |
| 126 dispatch_async(dispatch_get_main_queue(), ^{ | |
| 127 // Center the widget. | |
| 128 [_widgetVerticalPlacementConstraint setActive:NO]; | |
|
gambard
2017/01/02 10:55:08
Nit: you could use dot syntax for readability, her
Olivier
2017/01/02 12:23:31
setActive has a lot of consequences, so I prefer t
| |
| 129 _widgetVerticalPlacementConstraint = [[_shareView centerYAnchor] | |
|
gambard
2017/01/02 10:55:08
Same for the centerYAnchor
Olivier
2017/01/02 12:23:31
Done.
| |
| 130 constraintEqualToAnchor:[self.view centerYAnchor]]; | |
| 131 [_widgetVerticalPlacementConstraint setActive:YES]; | |
| 132 [self.maskView setAlpha:0]; | |
| 133 [UIView animateWithDuration:kAnimationDuration | |
| 134 animations:^{ | |
| 135 [self.maskView setAlpha:1]; | |
| 136 [self.view layoutIfNeeded]; | |
| 137 }]; | |
| 138 }); | |
| 139 } | |
| 140 | |
| 141 - (void)displayErrorView { | |
| 142 NSString* errorMessage = | |
| 143 NSLocalizedString(@"IDS_IOS_ERROR_MESSAGE_SHARE_EXTENSION", | |
| 144 @"The error message to display to the user."); | |
| 145 NSString* okButton = | |
| 146 NSLocalizedString(@"IDS_IOS_OK_BUTTON_SHARE_EXTENSION", | |
| 147 @"The label of the OK button in share extension."); | |
| 148 NSString* applicationName = [[[NSBundle mainBundle] infoDictionary] | |
| 149 valueForKey:@"CFBundleDisplayName"]; | |
| 150 errorMessage = | |
| 151 [errorMessage stringByReplacingOccurrencesOfString:@"APPLICATION_NAME" | |
| 152 withString:applicationName]; | |
| 153 UIAlertController* alert = | |
| 154 [UIAlertController alertControllerWithTitle:errorMessage | |
| 155 message:[_shareURL absoluteString] | |
| 156 preferredStyle:UIAlertControllerStyleAlert]; | |
| 157 UIAlertAction* defaultAction = | |
| 158 [UIAlertAction actionWithTitle:okButton | |
| 159 style:UIAlertActionStyleDefault | |
| 160 handler:^(UIAlertAction* action) { | |
| 161 [self dismissAndReturnItem:nil]; | |
| 162 }]; | |
| 163 [alert addAction:defaultAction]; | |
| 164 [self presentViewController:alert animated:YES completion:nil]; | |
| 165 } | |
| 166 | |
| 128 - (void)constrainWidgetWidth { | 167 - (void)constrainWidgetWidth { |
| 129 // Setting the constraints. | 168 // Setting the constraints. |
| 130 NSDictionary* views = @{ @"share" : self.shareView }; | 169 NSDictionary* views = @{ @"share" : self.shareView }; |
| 131 | 170 |
| 132 NSDictionary* metrics = @{ | 171 NSDictionary* metrics = @{ |
| 133 @"margin" : @(kShareExtensionMargin), | 172 @"margin" : @(kShareExtensionMargin), |
| 134 @"maxWidth" : @(kShareExtensionMaxWidth), | 173 @"maxWidth" : @(kShareExtensionMaxWidth), |
| 135 }; | 174 }; |
| 136 | 175 |
| 137 NSArray* constraints = @[ | 176 NSArray* constraints = @[ |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 164 if ([itemProvider hasItemConformingToTypeIdentifier:typeURL]) { | 203 if ([itemProvider hasItemConformingToTypeIdentifier:typeURL]) { |
| 165 ItemBlock URLCompletion = ^(id idURL, NSError* error) { | 204 ItemBlock URLCompletion = ^(id idURL, NSError* error) { |
| 166 NSURL* URL = static_cast<NSURL*>(idURL); | 205 NSURL* URL = static_cast<NSURL*>(idURL); |
| 167 dispatch_async(dispatch_get_main_queue(), ^{ | 206 dispatch_async(dispatch_get_main_queue(), ^{ |
| 168 _shareItem = [item copy]; | 207 _shareItem = [item copy]; |
| 169 _shareURL = [URL copy]; | 208 _shareURL = [URL copy]; |
| 170 _shareTitle = [[[item attributedContentText] string] copy]; | 209 _shareTitle = [[[item attributedContentText] string] copy]; |
| 171 if ([_shareTitle length] == 0) { | 210 if ([_shareTitle length] == 0) { |
| 172 _shareTitle = [URL host]; | 211 _shareTitle = [URL host]; |
| 173 } | 212 } |
| 174 [self.shareView setURL:URL]; | 213 if ([[_shareURL scheme] isEqualToString:@"http"] || |
| 175 [self.shareView setTitle:_shareTitle]; | 214 [[_shareURL scheme] isEqualToString:@"https"]) { |
| 215 [self displayShareView]; | |
| 216 } else { | |
| 217 [self displayErrorView]; | |
| 218 } | |
| 176 }); | 219 }); |
| 177 | 220 |
| 178 }; | 221 }; |
| 179 [itemProvider loadItemForTypeIdentifier:typeURL | 222 [itemProvider loadItemForTypeIdentifier:typeURL |
| 180 options:nil | 223 options:nil |
| 181 completionHandler:URLCompletion]; | 224 completionHandler:URLCompletion]; |
| 182 NSDictionary* imageOptions = @{ | 225 NSDictionary* imageOptions = @{ |
| 183 NSItemProviderPreferredImageSizeKey : [NSValue | 226 NSItemProviderPreferredImageSizeKey : [NSValue |
| 184 valueWithCGSize:CGSizeMake(kScreenShotWidth, kScreenShotHeight)] | 227 valueWithCGSize:CGSizeMake(kScreenShotWidth, kScreenShotHeight)] |
| 185 }; | 228 }; |
| 186 ItemBlock ImageCompletion = ^(id item, NSError* error) { | 229 ItemBlock imageCompletion = ^(id item, NSError* error) { |
| 187 | 230 _image = static_cast<UIImage*>(item); |
| 188 UIImage* image = static_cast<UIImage*>(item); | 231 if (_image && self.shareView) { |
| 189 if (image) { | |
| 190 dispatch_async(dispatch_get_main_queue(), ^{ | 232 dispatch_async(dispatch_get_main_queue(), ^{ |
| 191 [self.shareView setScreenshot:image]; | 233 [self.shareView setScreenshot:_image]; |
| 192 }); | 234 }); |
| 193 } | 235 } |
| 194 | |
| 195 }; | 236 }; |
| 196 [itemProvider loadPreviewImageWithOptions:imageOptions | 237 [itemProvider loadPreviewImageWithOptions:imageOptions |
| 197 completionHandler:ImageCompletion]; | 238 completionHandler:imageCompletion]; |
| 198 } | 239 } |
| 199 } | 240 } |
| 200 } | 241 } |
| 201 } | 242 } |
| 202 | 243 |
| 203 - (void)dismissAndReturnItem:(NSExtensionItem*)item { | 244 - (void)dismissAndReturnItem:(NSExtensionItem*)item { |
| 204 // Set the Y center constraints so the whole extension slides out of the | 245 // Set the Y placement constraints so the whole extension slides out of the |
| 205 // screen. Constant is relative to the center of the screen. | 246 // screen. |
| 206 // The direction (up or down) is relative to the output (cancel or submit). | 247 // The direction (up or down) is relative to the output (cancel or submit). |
| 207 NSInteger direction = item ? -1 : 1; | 248 [_widgetVerticalPlacementConstraint setActive:NO]; |
| 208 [_centerYConstraint setConstant:direction * | 249 if (item) { |
| 209 (self.view.frame.size.height + | 250 _widgetVerticalPlacementConstraint = [[_shareView bottomAnchor] |
| 210 self.shareView.frame.size.height) / | 251 constraintEqualToAnchor:[self.view topAnchor]]; |
| 211 2]; | 252 } else { |
| 253 _widgetVerticalPlacementConstraint = [[_shareView topAnchor] | |
| 254 constraintEqualToAnchor:[self.view bottomAnchor]]; | |
| 255 } | |
| 256 [_widgetVerticalPlacementConstraint setActive:YES]; | |
| 212 [UIView animateWithDuration:kAnimationDuration | 257 [UIView animateWithDuration:kAnimationDuration |
| 213 animations:^{ | 258 animations:^{ |
| 214 [self.maskView setAlpha:0]; | 259 [self.maskView setAlpha:0]; |
| 215 [self.view layoutIfNeeded]; | 260 [self.view layoutIfNeeded]; |
| 216 } | 261 } |
| 217 completion:^(BOOL finished) { | 262 completion:^(BOOL finished) { |
| 218 NSArray* returnItem = item ? @[ item ] : @[]; | 263 NSArray* returnItem = item ? @[ item ] : @[]; |
| 219 [self.extensionContext completeRequestReturningItems:returnItem | 264 [self.extensionContext completeRequestReturningItems:returnItem |
| 220 completionHandler:nil]; | 265 completionHandler:nil]; |
| 221 }]; | 266 }]; |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 298 [self dismissAndReturnItem:_shareItem]; | 343 [self dismissAndReturnItem:_shareItem]; |
| 299 }]; | 344 }]; |
| 300 } | 345 } |
| 301 | 346 |
| 302 - (void)shareExtensionView:(id)sender | 347 - (void)shareExtensionView:(id)sender |
| 303 typeChanged:(app_group::ShareExtensionItemType)type { | 348 typeChanged:(app_group::ShareExtensionItemType)type { |
| 304 [self setItemType:type]; | 349 [self setItemType:type]; |
| 305 } | 350 } |
| 306 | 351 |
| 307 @end | 352 @end |
| OLD | NEW |