| OLD | NEW |
| 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/ui/open_in_controller.h" | 5 #import "ios/chrome/browser/ui/open_in_controller.h" |
| 6 | 6 |
| 7 #include "base/files/file_path.h" | 7 #include "base/files/file_path.h" |
| 8 #include "base/ios/weak_nsobject.h" | |
| 9 #include "base/location.h" | 8 #include "base/location.h" |
| 10 #include "base/logging.h" | 9 #include "base/logging.h" |
| 11 #import "base/mac/bind_objc_block.h" | 10 #import "base/mac/bind_objc_block.h" |
| 12 #include "base/mac/objc_property_releaser.h" | 11 |
| 13 #include "base/sequenced_task_runner.h" | 12 #include "base/sequenced_task_runner.h" |
| 14 #include "base/strings/sys_string_conversions.h" | 13 #include "base/strings/sys_string_conversions.h" |
| 15 #include "base/threading/sequenced_worker_pool.h" | 14 #include "base/threading/sequenced_worker_pool.h" |
| 16 #include "base/threading/thread_restrictions.h" | 15 #include "base/threading/thread_restrictions.h" |
| 17 #include "components/strings/grit/components_strings.h" | 16 #include "components/strings/grit/components_strings.h" |
| 18 #import "ios/chrome/browser/ui/alert_coordinator/alert_coordinator.h" | 17 #import "ios/chrome/browser/ui/alert_coordinator/alert_coordinator.h" |
| 19 #import "ios/chrome/browser/ui/open_in_controller_testing.h" | 18 #import "ios/chrome/browser/ui/open_in_controller_testing.h" |
| 20 #include "ios/chrome/browser/ui/ui_util.h" | 19 #include "ios/chrome/browser/ui/ui_util.h" |
| 21 #import "ios/chrome/browser/ui/uikit_ui_util.h" | 20 #import "ios/chrome/browser/ui/uikit_ui_util.h" |
| 22 #include "ios/chrome/grit/ios_strings.h" | 21 #include "ios/chrome/grit/ios_strings.h" |
| 23 #include "ios/web/public/web_thread.h" | 22 #include "ios/web/public/web_thread.h" |
| 24 #import "ios/web/web_state/ui/crw_web_controller.h" | 23 #import "ios/web/web_state/ui/crw_web_controller.h" |
| 25 #include "net/base/load_flags.h" | 24 #include "net/base/load_flags.h" |
| 26 #include "net/url_request/url_fetcher.h" | 25 #include "net/url_request/url_fetcher.h" |
| 27 #include "net/url_request/url_fetcher_delegate.h" | 26 #include "net/url_request/url_fetcher_delegate.h" |
| 28 #include "net/url_request/url_request_context_getter.h" | 27 #include "net/url_request/url_request_context_getter.h" |
| 29 #include "ui/base/l10n/l10n_util_mac.h" | 28 #include "ui/base/l10n/l10n_util_mac.h" |
| 30 #import "ui/gfx/ios/NSString+CrStringDrawing.h" | 29 #import "ui/gfx/ios/NSString+CrStringDrawing.h" |
| 31 #include "url/gurl.h" | 30 #include "url/gurl.h" |
| 32 | 31 |
| 32 #if !defined(__has_feature) || !__has_feature(objc_arc) |
| 33 #error "This file requires ARC support." |
| 34 #endif |
| 35 |
| 33 namespace { | 36 namespace { |
| 34 // The path in the temp directory containing documents that are to be opened in | 37 // The path in the temp directory containing documents that are to be opened in |
| 35 // other applications. | 38 // other applications. |
| 36 static NSString* const kDocumentsTempPath = @"OpenIn"; | 39 static NSString* const kDocumentsTempPath = @"OpenIn"; |
| 37 | 40 |
| 38 static const int kHTTPResponseCodeSucceeded = 200; | 41 static const int kHTTPResponseCodeSucceeded = 200; |
| 39 | 42 |
| 40 // Duration of the show/hide animation for the |openInToolbar_|. | 43 // Duration of the show/hide animation for the |openInToolbar_|. |
| 41 const NSTimeInterval kOpenInToolbarAnimationDuration = 0.2; | 44 const NSTimeInterval kOpenInToolbarAnimationDuration = 0.2; |
| 42 | 45 |
| (...skipping 14 matching lines...) Expand all Loading... |
| 57 const CGFloat kOverlayedViewLabelWidthPercentage = 0.7; | 60 const CGFloat kOverlayedViewLabelWidthPercentage = 0.7; |
| 58 | 61 |
| 59 // Bottom margin for the label displayed on the |overlayedView_|. | 62 // Bottom margin for the label displayed on the |overlayedView_|. |
| 60 const CGFloat kOverlayedViewLabelBottomMargin = 60; | 63 const CGFloat kOverlayedViewLabelBottomMargin = 60; |
| 61 | 64 |
| 62 } // anonymous namespace | 65 } // anonymous namespace |
| 63 | 66 |
| 64 @interface OpenInController () { | 67 @interface OpenInController () { |
| 65 // AlertCoordinator for showing an alert if no applications were found to open | 68 // AlertCoordinator for showing an alert if no applications were found to open |
| 66 // the current document. | 69 // the current document. |
| 67 base::scoped_nsobject<AlertCoordinator> _alertCoordinator; | 70 AlertCoordinator* _alertCoordinator; |
| 68 } | 71 } |
| 69 | 72 |
| 70 // URLFetcher delegate method called when |fetcher_| completes a request. | 73 // URLFetcher delegate method called when |fetcher_| completes a request. |
| 71 - (void)urlFetchDidComplete:(const net::URLFetcher*)source; | 74 - (void)urlFetchDidComplete:(const net::URLFetcher*)source; |
| 72 // Ensures the destination directory is created and any contained obsolete files | 75 // Ensures the destination directory is created and any contained obsolete files |
| 73 // are deleted. Returns YES if the directory is created successfully. | 76 // are deleted. Returns YES if the directory is created successfully. |
| 74 + (BOOL)createDestinationDirectoryAndRemoveObsoleteFiles; | 77 + (BOOL)createDestinationDirectoryAndRemoveObsoleteFiles; |
| 75 // Starts downloading the file at path |kDocumentsTempPath| with the name | 78 // Starts downloading the file at path |kDocumentsTempPath| with the name |
| 76 // |suggestedFilename_|. | 79 // |suggestedFilename_|. |
| 77 - (void)startDownload; | 80 - (void)startDownload; |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 142 // |OpenInController| destroys the fetcher and cancels the callback. This is | 145 // |OpenInController| destroys the fetcher and cancels the callback. This is |
| 143 // why |OnURLFetchComplete| will neved be called after |owner_| is disabled. | 146 // why |OnURLFetchComplete| will neved be called after |owner_| is disabled. |
| 144 owner_ = nil; | 147 owner_ = nil; |
| 145 } | 148 } |
| 146 | 149 |
| 147 protected: | 150 protected: |
| 148 friend base::RefCountedThreadSafe<OpenInControllerBridge>; | 151 friend base::RefCountedThreadSafe<OpenInControllerBridge>; |
| 149 ~OpenInControllerBridge() override {} | 152 ~OpenInControllerBridge() override {} |
| 150 | 153 |
| 151 private: | 154 private: |
| 152 OpenInController* owner_; // weak | 155 __weak OpenInController* owner_; |
| 153 }; | 156 }; |
| 154 | 157 |
| 155 @implementation OpenInController { | 158 @implementation OpenInController { |
| 156 // Bridge from C++ to Obj-C class. | 159 // Bridge from C++ to Obj-C class. |
| 157 scoped_refptr<OpenInControllerBridge> bridge_; | 160 scoped_refptr<OpenInControllerBridge> bridge_; |
| 158 | 161 |
| 159 // URL of the document. | 162 // URL of the document. |
| 160 GURL documentURL_; | 163 GURL documentURL_; |
| 161 | 164 |
| 162 // Controller for opening documents in other applications. | 165 // Controller for opening documents in other applications. |
| 163 base::scoped_nsobject<UIDocumentInteractionController> documentController_; | 166 UIDocumentInteractionController* documentController_; |
| 164 | 167 |
| 165 // Toolbar overlay to be displayed on tap. | 168 // Toolbar overlay to be displayed on tap. |
| 166 base::scoped_nsobject<OpenInToolbar> openInToolbar_; | 169 OpenInToolbar* openInToolbar_; |
| 167 | 170 |
| 168 // Timer used to automatically hide the |openInToolbar_| after a period. | 171 // Timer used to automatically hide the |openInToolbar_| after a period. |
| 169 base::scoped_nsobject<NSTimer> openInTimer_; | 172 NSTimer* openInTimer_; |
| 170 | 173 |
| 171 // Gesture recognizer to catch taps on the document. | 174 // Gesture recognizer to catch taps on the document. |
| 172 base::scoped_nsobject<UITapGestureRecognizer> tapRecognizer_; | 175 UITapGestureRecognizer* tapRecognizer_; |
| 173 | 176 |
| 174 // Suggested filename for the document. | 177 // Suggested filename for the document. |
| 175 base::scoped_nsobject<NSString> suggestedFilename_; | 178 NSString* suggestedFilename_; |
| 176 | 179 |
| 177 // Fetcher used to redownload the document and save it in the sandbox. | 180 // Fetcher used to redownload the document and save it in the sandbox. |
| 178 std::unique_ptr<net::URLFetcher> fetcher_; | 181 std::unique_ptr<net::URLFetcher> fetcher_; |
| 179 | 182 |
| 180 // CRWWebController used to check if the tap is not on a link and the | 183 // CRWWebController used to check if the tap is not on a link and the |
| 181 // |openInToolbar_| should be displayed. | 184 // |openInToolbar_| should be displayed. |
| 182 base::scoped_nsobject<CRWWebController> webController_; | 185 CRWWebController* webController_; |
| 183 | 186 |
| 184 // URLRequestContextGetter needed for the URLFetcher. | 187 // URLRequestContextGetter needed for the URLFetcher. |
| 185 scoped_refptr<net::URLRequestContextGetter> requestContext_; | 188 scoped_refptr<net::URLRequestContextGetter> requestContext_; |
| 186 | 189 |
| 187 // Spinner view displayed while the file is downloading. | 190 // Spinner view displayed while the file is downloading. |
| 188 base::scoped_nsobject<UIView> overlayedView_; | 191 UIView* overlayedView_; |
| 189 | 192 |
| 190 // The location where the "Open in..." menu is anchored. | 193 // The location where the "Open in..." menu is anchored. |
| 191 CGRect anchorLocation_; | 194 CGRect anchorLocation_; |
| 192 | 195 |
| 193 // YES if the file download was canceled. | 196 // YES if the file download was canceled. |
| 194 BOOL downloadCanceled_; | 197 BOOL downloadCanceled_; |
| 195 | 198 |
| 196 // YES if the OpenIn menu is displayed. | 199 // YES if the OpenIn menu is displayed. |
| 197 BOOL isOpenInMenuDisplayed_; | 200 BOOL isOpenInMenuDisplayed_; |
| 198 | 201 |
| 199 // YES if the toolbar is displayed. | 202 // YES if the toolbar is displayed. |
| 200 BOOL isOpenInToolbarDisplayed_; | 203 BOOL isOpenInToolbarDisplayed_; |
| 201 | 204 |
| 202 // Task runner on which file operations should happen. | 205 // Task runner on which file operations should happen. |
| 203 scoped_refptr<base::SequencedTaskRunner> sequencedTaskRunner_; | 206 scoped_refptr<base::SequencedTaskRunner> sequencedTaskRunner_; |
| 204 | |
| 205 base::mac::ObjCPropertyReleaser propertyReleaser_OpenInController_; | |
| 206 } | 207 } |
| 207 | 208 |
| 208 - (id)initWithRequestContext:(net::URLRequestContextGetter*)requestContext | 209 - (id)initWithRequestContext:(net::URLRequestContextGetter*)requestContext |
| 209 webController:(CRWWebController*)webController { | 210 webController:(CRWWebController*)webController { |
| 210 self = [super init]; | 211 self = [super init]; |
| 211 if (self) { | 212 if (self) { |
| 212 requestContext_ = requestContext; | 213 requestContext_ = requestContext; |
| 213 webController_.reset([webController retain]); | 214 webController_ = webController; |
| 214 tapRecognizer_.reset([[UITapGestureRecognizer alloc] | 215 tapRecognizer_ = [[UITapGestureRecognizer alloc] |
| 215 initWithTarget:self | 216 initWithTarget:self |
| 216 action:@selector(handleTapFrom:)]); | 217 action:@selector(handleTapFrom:)]; |
| 217 [tapRecognizer_ setDelegate:self]; | 218 [tapRecognizer_ setDelegate:self]; |
| 218 base::SequencedWorkerPool* pool = web::WebThread::GetBlockingPool(); | 219 base::SequencedWorkerPool* pool = web::WebThread::GetBlockingPool(); |
| 219 sequencedTaskRunner_ = | 220 sequencedTaskRunner_ = |
| 220 pool->GetSequencedTaskRunner(pool->GetSequenceToken()); | 221 pool->GetSequencedTaskRunner(pool->GetSequenceToken()); |
| 221 isOpenInMenuDisplayed_ = NO; | 222 isOpenInMenuDisplayed_ = NO; |
| 222 propertyReleaser_OpenInController_.Init(self, [OpenInController class]); | |
| 223 } | 223 } |
| 224 return self; | 224 return self; |
| 225 } | 225 } |
| 226 | 226 |
| 227 - (void)enableWithDocumentURL:(const GURL&)documentURL | 227 - (void)enableWithDocumentURL:(const GURL&)documentURL |
| 228 suggestedFilename:(NSString*)suggestedFilename { | 228 suggestedFilename:(NSString*)suggestedFilename { |
| 229 documentURL_ = GURL(documentURL); | 229 documentURL_ = GURL(documentURL); |
| 230 suggestedFilename_.reset([suggestedFilename retain]); | 230 suggestedFilename_ = suggestedFilename; |
| 231 [webController_ addGestureRecognizerToWebView:tapRecognizer_]; | 231 [webController_ addGestureRecognizerToWebView:tapRecognizer_]; |
| 232 [self openInToolbar].alpha = 0.0f; | 232 [self openInToolbar].alpha = 0.0f; |
| 233 [webController_ addToolbarViewToWebView:[self openInToolbar]]; | 233 [webController_ addToolbarViewToWebView:[self openInToolbar]]; |
| 234 [self showOpenInToolbar]; | 234 [self showOpenInToolbar]; |
| 235 } | 235 } |
| 236 | 236 |
| 237 - (void)disable { | 237 - (void)disable { |
| 238 [self openInToolbar].alpha = 0.0f; | 238 [self openInToolbar].alpha = 0.0f; |
| 239 [openInTimer_ invalidate]; | 239 [openInTimer_ invalidate]; |
| 240 if (bridge_.get()) | 240 if (bridge_.get()) |
| 241 bridge_->OnOwnerDisabled(); | 241 bridge_->OnOwnerDisabled(); |
| 242 bridge_ = nil; | 242 bridge_ = nil; |
| 243 [webController_ removeGestureRecognizerFromWebView:tapRecognizer_]; | 243 [webController_ removeGestureRecognizerFromWebView:tapRecognizer_]; |
| 244 [webController_ removeToolbarViewFromWebView:[self openInToolbar]]; | 244 [webController_ removeToolbarViewFromWebView:[self openInToolbar]]; |
| 245 [documentController_ dismissMenuAnimated:NO]; | 245 [documentController_ dismissMenuAnimated:NO]; |
| 246 [documentController_ setDelegate:nil]; | 246 [documentController_ setDelegate:nil]; |
| 247 documentURL_ = GURL(); | 247 documentURL_ = GURL(); |
| 248 suggestedFilename_.reset(); | 248 suggestedFilename_ = nil; |
| 249 fetcher_.reset(); | 249 fetcher_.reset(); |
| 250 } | 250 } |
| 251 | 251 |
| 252 - (void)detachFromWebController { | 252 - (void)detachFromWebController { |
| 253 [self disable]; | 253 [self disable]; |
| 254 // Animation blocks may be keeping this object alive; don't extend the | 254 // Animation blocks may be keeping this object alive; don't extend the |
| 255 // lifetime of CRWWebController. | 255 // lifetime of CRWWebController. |
| 256 webController_.reset(); | 256 webController_ = nil; |
| 257 } | 257 } |
| 258 | 258 |
| 259 - (void)dealloc { | 259 - (void)dealloc { |
| 260 [self disable]; | 260 [self disable]; |
| 261 [super dealloc]; | |
| 262 } | 261 } |
| 263 | 262 |
| 264 - (void)handleTapFrom:(UIGestureRecognizer*)gestureRecognizer { | 263 - (void)handleTapFrom:(UIGestureRecognizer*)gestureRecognizer { |
| 265 if ([gestureRecognizer state] == UIGestureRecognizerStateEnded) { | 264 if ([gestureRecognizer state] == UIGestureRecognizerStateEnded) { |
| 266 if (isOpenInToolbarDisplayed_) { | 265 if (isOpenInToolbarDisplayed_) { |
| 267 [self hideOpenInToolbar]; | 266 [self hideOpenInToolbar]; |
| 268 } else { | 267 } else { |
| 269 [self showOpenInToolbar]; | 268 [self showOpenInToolbar]; |
| 270 } | 269 } |
| 271 } | 270 } |
| 272 } | 271 } |
| 273 | 272 |
| 274 - (void)showOpenInToolbar { | 273 - (void)showOpenInToolbar { |
| 275 if ([openInTimer_ isValid]) { | 274 if ([openInTimer_ isValid]) { |
| 276 [openInTimer_ setFireDate:([NSDate dateWithTimeIntervalSinceNow: | 275 [openInTimer_ setFireDate:([NSDate dateWithTimeIntervalSinceNow: |
| 277 kOpenInToolbarDisplayDuration])]; | 276 kOpenInToolbarDisplayDuration])]; |
| 278 } else { | 277 } else { |
| 279 openInTimer_.reset( | 278 openInTimer_ = |
| 280 [[NSTimer scheduledTimerWithTimeInterval:kOpenInToolbarDisplayDuration | 279 [NSTimer scheduledTimerWithTimeInterval:kOpenInToolbarDisplayDuration |
| 281 target:self | 280 target:self |
| 282 selector:@selector(hideOpenInToolbar) | 281 selector:@selector(hideOpenInToolbar) |
| 283 userInfo:nil | 282 userInfo:nil |
| 284 repeats:NO] retain]); | 283 repeats:NO]; |
| 285 UIView* openInToolbar = [self openInToolbar]; | 284 UIView* openInToolbar = [self openInToolbar]; |
| 286 [UIView animateWithDuration:kOpenInToolbarAnimationDuration | 285 [UIView animateWithDuration:kOpenInToolbarAnimationDuration |
| 287 animations:^{ | 286 animations:^{ |
| 288 [openInToolbar setAlpha:1.0]; | 287 [openInToolbar setAlpha:1.0]; |
| 289 }]; | 288 }]; |
| 290 } | 289 } |
| 291 isOpenInToolbarDisplayed_ = YES; | 290 isOpenInToolbarDisplayed_ = YES; |
| 292 } | 291 } |
| 293 | 292 |
| 294 - (void)hideOpenInToolbar { | 293 - (void)hideOpenInToolbar { |
| (...skipping 30 matching lines...) Expand all Loading... |
| 325 base::Callback<void(BOOL)> reply = base::Bind( | 324 base::Callback<void(BOOL)> reply = base::Bind( |
| 326 &OpenInControllerBridge::OnDestinationDirectoryCreated, bridge_); | 325 &OpenInControllerBridge::OnDestinationDirectoryCreated, bridge_); |
| 327 base::PostTaskAndReplyWithResult(sequencedTaskRunner_.get(), FROM_HERE, task, | 326 base::PostTaskAndReplyWithResult(sequencedTaskRunner_.get(), FROM_HERE, task, |
| 328 reply); | 327 reply); |
| 329 } | 328 } |
| 330 | 329 |
| 331 - (void)startDownload { | 330 - (void)startDownload { |
| 332 NSString* tempDirPath = [NSTemporaryDirectory() | 331 NSString* tempDirPath = [NSTemporaryDirectory() |
| 333 stringByAppendingPathComponent:kDocumentsTempPath]; | 332 stringByAppendingPathComponent:kDocumentsTempPath]; |
| 334 NSString* filePath = | 333 NSString* filePath = |
| 335 [tempDirPath stringByAppendingPathComponent:suggestedFilename_.get()]; | 334 [tempDirPath stringByAppendingPathComponent:suggestedFilename_]; |
| 336 | 335 |
| 337 // In iPad the toolbar has to be displayed to anchor the "Open in" menu. | 336 // In iPad the toolbar has to be displayed to anchor the "Open in" menu. |
| 338 if (!IsIPadIdiom()) | 337 if (!IsIPadIdiom()) |
| 339 [self hideOpenInToolbar]; | 338 [self hideOpenInToolbar]; |
| 340 | 339 |
| 341 // Show an overlayed view to indicate a download is in progress. On tap this | 340 // Show an overlayed view to indicate a download is in progress. On tap this |
| 342 // view can be dismissed and the download canceled. | 341 // view can be dismissed and the download canceled. |
| 343 [self showDownloadOverlayView]; | 342 [self showDownloadOverlayView]; |
| 344 downloadCanceled_ = NO; | 343 downloadCanceled_ = NO; |
| 345 | 344 |
| (...skipping 18 matching lines...) Expand all Loading... |
| 364 [self removeOverlayedView]; | 363 [self removeOverlayedView]; |
| 365 if (IsIPadIdiom()) | 364 if (IsIPadIdiom()) |
| 366 [self hideOpenInToolbar]; | 365 [self hideOpenInToolbar]; |
| 367 downloadCanceled_ = YES; | 366 downloadCanceled_ = YES; |
| 368 } | 367 } |
| 369 | 368 |
| 370 - (void)removeOverlayedView { | 369 - (void)removeOverlayedView { |
| 371 if (!overlayedView_) | 370 if (!overlayedView_) |
| 372 return; | 371 return; |
| 373 | 372 |
| 374 UIView* overlayedView = overlayedView_.get(); | 373 UIView* overlayedView = overlayedView_; |
| 375 [UIView animateWithDuration:kOverlayViewAnimationDuration | 374 [UIView animateWithDuration:kOverlayViewAnimationDuration |
| 376 animations:^{ | 375 animations:^{ |
| 377 [overlayedView setAlpha:0.0]; | 376 [overlayedView setAlpha:0.0]; |
| 378 } | 377 } |
| 379 completion:^(BOOL finished) { | 378 completion:^(BOOL finished) { |
| 380 [overlayedView removeFromSuperview]; | 379 [overlayedView removeFromSuperview]; |
| 381 }]; | 380 }]; |
| 382 overlayedView_.reset(); | 381 overlayedView_ = nil; |
| 383 } | 382 } |
| 384 | 383 |
| 385 - (void)showErrorWithMessage:(NSString*)message { | 384 - (void)showErrorWithMessage:(NSString*)message { |
| 386 UIViewController* topViewController = | 385 UIViewController* topViewController = |
| 387 [[[UIApplication sharedApplication] keyWindow] rootViewController]; | 386 [[[UIApplication sharedApplication] keyWindow] rootViewController]; |
| 388 | 387 |
| 389 _alertCoordinator.reset([[AlertCoordinator alloc] | 388 _alertCoordinator = |
| 390 initWithBaseViewController:topViewController | 389 [[AlertCoordinator alloc] initWithBaseViewController:topViewController |
| 391 title:nil | 390 title:nil |
| 392 message:message]); | 391 message:message]; |
| 393 | 392 |
| 394 [_alertCoordinator addItemWithTitle:l10n_util::GetNSString(IDS_OK) | 393 [_alertCoordinator addItemWithTitle:l10n_util::GetNSString(IDS_OK) |
| 395 action:nil | 394 action:nil |
| 396 style:UIAlertActionStyleDefault]; | 395 style:UIAlertActionStyleDefault]; |
| 397 | 396 |
| 398 [_alertCoordinator start]; | 397 [_alertCoordinator start]; |
| 399 } | 398 } |
| 400 | 399 |
| 401 - (void)presentOpenInMenuForFileAtURL:(NSURL*)fileURL { | 400 - (void)presentOpenInMenuForFileAtURL:(NSURL*)fileURL { |
| 402 if (!webController_) | 401 if (!webController_) |
| 403 return; | 402 return; |
| 404 | 403 |
| 405 if (requestContext_.get()) { | 404 if (requestContext_.get()) { |
| 406 // |requestContext_| is nil only if this is called from a unit test, in | 405 // |requestContext_| is nil only if this is called from a unit test, in |
| 407 // which case the |documentController_| was set already. | 406 // which case the |documentController_| was set already. |
| 408 documentController_.reset([[UIDocumentInteractionController | 407 documentController_ = |
| 409 interactionControllerWithURL:fileURL] retain]); | 408 [UIDocumentInteractionController interactionControllerWithURL:fileURL]; |
| 410 } | 409 } |
| 411 | 410 |
| 412 // TODO(cgrigoruta): The UTI is hardcoded for now, change this when we add | 411 // TODO(cgrigoruta): The UTI is hardcoded for now, change this when we add |
| 413 // support for other file types as well. | 412 // support for other file types as well. |
| 414 [documentController_ setUTI:@"com.adobe.pdf"]; | 413 [documentController_ setUTI:@"com.adobe.pdf"]; |
| 415 [documentController_ setDelegate:self]; | 414 [documentController_ setDelegate:self]; |
| 416 BOOL success = | 415 BOOL success = |
| 417 [documentController_ presentOpenInMenuFromRect:anchorLocation_ | 416 [documentController_ presentOpenInMenuFromRect:anchorLocation_ |
| 418 inView:[webController_ view] | 417 inView:[webController_ view] |
| 419 animated:YES]; | 418 animated:YES]; |
| 420 if (requestContext_.get()) { | 419 if (requestContext_.get()) { |
| 421 [self removeOverlayedView]; | 420 [self removeOverlayedView]; |
| 422 if (!success) { | 421 if (!success) { |
| 423 if (IsIPadIdiom()) | 422 if (IsIPadIdiom()) |
| 424 [self hideOpenInToolbar]; | 423 [self hideOpenInToolbar]; |
| 425 NSString* errorMessage = | 424 NSString* errorMessage = |
| 426 l10n_util::GetNSStringWithFixup(IDS_IOS_OPEN_IN_NO_APPS_REGISTERED); | 425 l10n_util::GetNSStringWithFixup(IDS_IOS_OPEN_IN_NO_APPS_REGISTERED); |
| 427 [self showErrorWithMessage:errorMessage]; | 426 [self showErrorWithMessage:errorMessage]; |
| 428 } else { | 427 } else { |
| 429 isOpenInMenuDisplayed_ = YES; | 428 isOpenInMenuDisplayed_ = YES; |
| 430 } | 429 } |
| 431 } | 430 } |
| 432 } | 431 } |
| 433 | 432 |
| 434 - (void)showDownloadOverlayView { | 433 - (void)showDownloadOverlayView { |
| 435 UIViewController* topViewController = | 434 UIViewController* topViewController = |
| 436 [[[UIApplication sharedApplication] keyWindow] rootViewController]; | 435 [[[UIApplication sharedApplication] keyWindow] rootViewController]; |
| 437 UIView* topView = topViewController.view; | 436 UIView* topView = topViewController.view; |
| 438 overlayedView_.reset([[UIView alloc] initWithFrame:[topView bounds]]); | 437 overlayedView_ = [[UIView alloc] initWithFrame:[topView bounds]]; |
| 439 [overlayedView_ setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | | 438 [overlayedView_ setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | |
| 440 UIViewAutoresizingFlexibleHeight)]; | 439 UIViewAutoresizingFlexibleHeight)]; |
| 441 base::scoped_nsobject<UIView> grayBackgroundView( | 440 UIView* grayBackgroundView = |
| 442 [[UIView alloc] initWithFrame:[overlayedView_ frame]]); | 441 [[UIView alloc] initWithFrame:[overlayedView_ frame]]; |
| 443 [grayBackgroundView setBackgroundColor:[UIColor darkGrayColor]]; | 442 [grayBackgroundView setBackgroundColor:[UIColor darkGrayColor]]; |
| 444 [grayBackgroundView setAlpha:kOverlayedViewBackgroundAlpha]; | 443 [grayBackgroundView setAlpha:kOverlayedViewBackgroundAlpha]; |
| 445 [grayBackgroundView setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | | 444 [grayBackgroundView setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | |
| 446 UIViewAutoresizingFlexibleHeight)]; | 445 UIViewAutoresizingFlexibleHeight)]; |
| 447 [overlayedView_ addSubview:grayBackgroundView]; | 446 [overlayedView_ addSubview:grayBackgroundView]; |
| 448 | 447 |
| 449 base::scoped_nsobject<UIActivityIndicatorView> spinner([ | 448 UIActivityIndicatorView* spinner = [[UIActivityIndicatorView alloc] |
| 450 [UIActivityIndicatorView alloc] | 449 initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]; |
| 451 initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]); | |
| 452 [spinner setFrame:[overlayedView_ frame]]; | 450 [spinner setFrame:[overlayedView_ frame]]; |
| 453 [spinner setHidesWhenStopped:YES]; | 451 [spinner setHidesWhenStopped:YES]; |
| 454 [spinner setUserInteractionEnabled:NO]; | 452 [spinner setUserInteractionEnabled:NO]; |
| 455 [spinner startAnimating]; | 453 [spinner startAnimating]; |
| 456 [spinner setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | | 454 [spinner setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | |
| 457 UIViewAutoresizingFlexibleHeight)]; | 455 UIViewAutoresizingFlexibleHeight)]; |
| 458 [overlayedView_ addSubview:spinner]; | 456 [overlayedView_ addSubview:spinner]; |
| 459 | 457 |
| 460 base::scoped_nsobject<UILabel> label([[UILabel alloc] init]); | 458 UILabel* label = [[UILabel alloc] init]; |
| 461 [label setTextColor:[UIColor whiteColor]]; | 459 [label setTextColor:[UIColor whiteColor]]; |
| 462 [label setFont:GetUIFont(FONT_HELVETICA, true, kLabelTextSize)]; | 460 [label setFont:GetUIFont(FONT_HELVETICA, true, kLabelTextSize)]; |
| 463 [label setNumberOfLines:0]; | 461 [label setNumberOfLines:0]; |
| 464 [label setShadowColor:[UIColor blackColor]]; | 462 [label setShadowColor:[UIColor blackColor]]; |
| 465 [label setShadowOffset:CGSizeMake(0.0, 1.0)]; | 463 [label setShadowOffset:CGSizeMake(0.0, 1.0)]; |
| 466 [label setBackgroundColor:[UIColor clearColor]]; | 464 [label setBackgroundColor:[UIColor clearColor]]; |
| 467 [label setText:l10n_util::GetNSString(IDS_IOS_OPEN_IN_FILE_DOWNLOAD_CANCEL)]; | 465 [label setText:l10n_util::GetNSString(IDS_IOS_OPEN_IN_FILE_DOWNLOAD_CANCEL)]; |
| 468 [label setLineBreakMode:NSLineBreakByWordWrapping]; | 466 [label setLineBreakMode:NSLineBreakByWordWrapping]; |
| 469 [label setTextAlignment:NSTextAlignmentCenter]; | 467 [label setTextAlignment:NSTextAlignmentCenter]; |
| 470 CGFloat labelWidth = | 468 CGFloat labelWidth = |
| 471 [overlayedView_ frame].size.width * kOverlayedViewLabelWidthPercentage; | 469 [overlayedView_ frame].size.width * kOverlayedViewLabelWidthPercentage; |
| 472 CGFloat originX = ([overlayedView_ frame].size.width - labelWidth) / 2; | 470 CGFloat originX = ([overlayedView_ frame].size.width - labelWidth) / 2; |
| 473 | 471 |
| 474 CGFloat labelHeight = | 472 CGFloat labelHeight = |
| 475 [[label text] cr_boundingSizeWithSize:CGSizeMake(labelWidth, CGFLOAT_MAX) | 473 [[label text] cr_boundingSizeWithSize:CGSizeMake(labelWidth, CGFLOAT_MAX) |
| 476 font:[label font]] | 474 font:[label font]] |
| 477 .height; | 475 .height; |
| 478 CGFloat originY = | 476 CGFloat originY = |
| 479 [overlayedView_ center].y - labelHeight - kOverlayedViewLabelBottomMargin; | 477 [overlayedView_ center].y - labelHeight - kOverlayedViewLabelBottomMargin; |
| 480 [label setFrame:CGRectMake(originX, originY, labelWidth, labelHeight)]; | 478 [label setFrame:CGRectMake(originX, originY, labelWidth, labelHeight)]; |
| 481 [overlayedView_ addSubview:label]; | 479 [overlayedView_ addSubview:label]; |
| 482 | 480 |
| 483 base::scoped_nsobject<UITapGestureRecognizer> tapRecognizer( | 481 UITapGestureRecognizer* tapRecognizer = [[UITapGestureRecognizer alloc] |
| 484 [[UITapGestureRecognizer alloc] | 482 initWithTarget:self |
| 485 initWithTarget:self | 483 action:@selector(handleTapOnOverlayedView:)]; |
| 486 action:@selector(handleTapOnOverlayedView:)]); | |
| 487 [tapRecognizer setDelegate:self]; | 484 [tapRecognizer setDelegate:self]; |
| 488 [overlayedView_ addGestureRecognizer:tapRecognizer]; | 485 [overlayedView_ addGestureRecognizer:tapRecognizer]; |
| 489 | 486 |
| 490 [overlayedView_ setAlpha:0.0]; | 487 [overlayedView_ setAlpha:0.0]; |
| 491 [topView addSubview:overlayedView_]; | 488 [topView addSubview:overlayedView_]; |
| 492 UIView* overlayedView = overlayedView_.get(); | 489 UIView* overlayedView = overlayedView_; |
| 493 [UIView animateWithDuration:kOverlayViewAnimationDuration | 490 [UIView animateWithDuration:kOverlayViewAnimationDuration |
| 494 animations:^{ | 491 animations:^{ |
| 495 [overlayedView setAlpha:1.0]; | 492 [overlayedView setAlpha:1.0]; |
| 496 }]; | 493 }]; |
| 497 } | 494 } |
| 498 | 495 |
| 499 - (UIView*)openInToolbar { | 496 - (UIView*)openInToolbar { |
| 500 if (!openInToolbar_) { | 497 if (!openInToolbar_) { |
| 501 openInToolbar_.reset([[OpenInToolbar alloc] | 498 openInToolbar_ = [[OpenInToolbar alloc] |
| 502 initWithTarget:self | 499 initWithTarget:self |
| 503 action:@selector(exportFileWithOpenInMenuAnchoredAt:)]); | 500 action:@selector(exportFileWithOpenInMenuAnchoredAt:)]; |
| 504 } | 501 } |
| 505 return openInToolbar_.get(); | 502 return openInToolbar_; |
| 506 } | 503 } |
| 507 | 504 |
| 508 #pragma mark - | 505 #pragma mark - |
| 509 #pragma mark File management | 506 #pragma mark File management |
| 510 | 507 |
| 511 - (void)removeDocumentAtPath:(NSString*)path { | 508 - (void)removeDocumentAtPath:(NSString*)path { |
| 512 base::ThreadRestrictions::AssertIOAllowed(); | 509 base::ThreadRestrictions::AssertIOAllowed(); |
| 513 NSFileManager* fileManager = [NSFileManager defaultManager]; | 510 NSFileManager* fileManager = [NSFileManager defaultManager]; |
| 514 NSError* error = nil; | 511 NSError* error = nil; |
| 515 if (![fileManager removeItemAtPath:path error:&error]) { | 512 if (![fileManager removeItemAtPath:path error:&error]) { |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 575 - (void)urlFetchDidComplete:(const net::URLFetcher*)fetcher { | 572 - (void)urlFetchDidComplete:(const net::URLFetcher*)fetcher { |
| 576 DCHECK(fetcher); | 573 DCHECK(fetcher); |
| 577 if (requestContext_.get()) | 574 if (requestContext_.get()) |
| 578 DCHECK_CURRENTLY_ON(web::WebThread::UI); | 575 DCHECK_CURRENTLY_ON(web::WebThread::UI); |
| 579 base::FilePath filePath; | 576 base::FilePath filePath; |
| 580 if (fetcher->GetResponseCode() == kHTTPResponseCodeSucceeded && | 577 if (fetcher->GetResponseCode() == kHTTPResponseCodeSucceeded && |
| 581 fetcher->GetResponseAsFilePath(true, &filePath)) { | 578 fetcher->GetResponseAsFilePath(true, &filePath)) { |
| 582 NSURL* fileURL = | 579 NSURL* fileURL = |
| 583 [NSURL fileURLWithPath:base::SysUTF8ToNSString(filePath.value())]; | 580 [NSURL fileURLWithPath:base::SysUTF8ToNSString(filePath.value())]; |
| 584 if (downloadCanceled_) { | 581 if (downloadCanceled_) { |
| 585 sequencedTaskRunner_->PostTask(FROM_HERE, base::BindBlock(^{ | 582 sequencedTaskRunner_->PostTask(FROM_HERE, base::BindBlockArc(^{ |
| 586 [self | 583 [self |
| 587 removeDocumentAtPath:[fileURL path]]; | 584 removeDocumentAtPath:[fileURL path]]; |
| 588 })); | 585 })); |
| 589 } else { | 586 } else { |
| 590 [self presentOpenInMenuForFileAtURL:fileURL]; | 587 [self presentOpenInMenuForFileAtURL:fileURL]; |
| 591 } | 588 } |
| 592 } else if (!downloadCanceled_) { | 589 } else if (!downloadCanceled_) { |
| 593 if (IsIPadIdiom()) | 590 if (IsIPadIdiom()) |
| 594 [self hideOpenInToolbar]; | 591 [self hideOpenInToolbar]; |
| 595 [self removeOverlayedView]; | 592 [self removeOverlayedView]; |
| 596 [self showErrorWithMessage:l10n_util::GetNSStringWithFixup( | 593 [self showErrorWithMessage:l10n_util::GetNSStringWithFixup( |
| 597 IDS_IOS_OPEN_IN_FILE_DOWNLOAD_FAILED)]; | 594 IDS_IOS_OPEN_IN_FILE_DOWNLOAD_FAILED)]; |
| 598 } | 595 } |
| 599 } | 596 } |
| 600 | 597 |
| 601 #pragma mark - | 598 #pragma mark - |
| 602 #pragma mark UIDocumentInteractionControllerDelegate Methods | 599 #pragma mark UIDocumentInteractionControllerDelegate Methods |
| 603 | 600 |
| 604 - (void)documentInteractionController:(UIDocumentInteractionController*)contr | 601 - (void)documentInteractionController:(UIDocumentInteractionController*)contr |
| 605 didEndSendingToApplication:(NSString*)application { | 602 didEndSendingToApplication:(NSString*)application { |
| 606 sequencedTaskRunner_->PostTask(FROM_HERE, base::BindBlock(^{ | 603 sequencedTaskRunner_->PostTask(FROM_HERE, base::BindBlockArc(^{ |
| 607 [self | 604 [self |
| 608 removeDocumentAtPath:[[contr URL] path]]; | 605 removeDocumentAtPath:[[contr URL] path]]; |
| 609 })); | 606 })); |
| 610 if (IsIPadIdiom()) { | 607 if (IsIPadIdiom()) { |
| 611 // Call the |documentInteractionControllerDidDismissOpenInMenu:| method | 608 // Call the |documentInteractionControllerDidDismissOpenInMenu:| method |
| 612 // as this is not called on the iPad after the document has been opened | 609 // as this is not called on the iPad after the document has been opened |
| 613 // in another application. | 610 // in another application. |
| 614 [self documentInteractionControllerDidDismissOpenInMenu:contr]; | 611 [self documentInteractionControllerDidDismissOpenInMenu:contr]; |
| 615 } | 612 } |
| 616 } | 613 } |
| 617 | 614 |
| 618 - (void)documentInteractionControllerDidDismissOpenInMenu: | 615 - (void)documentInteractionControllerDidDismissOpenInMenu: |
| 619 (UIDocumentInteractionController*)controller { | 616 (UIDocumentInteractionController*)controller { |
| 620 if (!IsIPadIdiom()) { | 617 if (!IsIPadIdiom()) { |
| 621 isOpenInMenuDisplayed_ = NO; | 618 isOpenInMenuDisplayed_ = NO; |
| 622 // On the iPhone the |openInToolber_| is hidden already. | 619 // On the iPhone the |openInToolber_| is hidden already. |
| 623 return; | 620 return; |
| 624 } | 621 } |
| 625 | 622 |
| 626 // On iPad this method is called whenever the device changes orientation, | 623 // On iPad this method is called whenever the device changes orientation, |
| 627 // even thought the OpenIn menu is not displayed. To distinguish the cases | 624 // even thought the OpenIn menu is not displayed. To distinguish the cases |
| 628 // when this method is called after the OpenIn menu is dismissed, we | 625 // when this method is called after the OpenIn menu is dismissed, we |
| 629 // check the BOOL |isOpenInMenuDisplayed|. | 626 // check the BOOL |isOpenInMenuDisplayed|. |
| 630 if (isOpenInMenuDisplayed_) { | 627 if (isOpenInMenuDisplayed_) { |
| 631 openInTimer_.reset( | 628 openInTimer_ = |
| 632 [[NSTimer scheduledTimerWithTimeInterval:kOpenInToolbarDisplayDuration | 629 [NSTimer scheduledTimerWithTimeInterval:kOpenInToolbarDisplayDuration |
| 633 target:self | 630 target:self |
| 634 selector:@selector(hideOpenInToolbar) | 631 selector:@selector(hideOpenInToolbar) |
| 635 userInfo:nil | 632 userInfo:nil |
| 636 repeats:NO] retain]); | 633 repeats:NO]; |
| 637 } | 634 } |
| 638 isOpenInMenuDisplayed_ = NO; | 635 isOpenInMenuDisplayed_ = NO; |
| 639 } | 636 } |
| 640 | 637 |
| 641 #pragma mark - | 638 #pragma mark - |
| 642 #pragma mark UIGestureRecognizerDelegate Methods | 639 #pragma mark UIGestureRecognizerDelegate Methods |
| 643 | 640 |
| 644 - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer*)gestureRecognizer { | 641 - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer*)gestureRecognizer { |
| 645 if ([gestureRecognizer.view isEqual:overlayedView_]) | 642 if ([gestureRecognizer.view isEqual:overlayedView_]) |
| 646 return YES; | 643 return YES; |
| 647 | 644 |
| 648 CGPoint location = [gestureRecognizer locationInView:[self openInToolbar]]; | 645 CGPoint location = [gestureRecognizer locationInView:[self openInToolbar]]; |
| 649 return ![[self openInToolbar] pointInside:location withEvent:nil]; | 646 return ![[self openInToolbar] pointInside:location withEvent:nil]; |
| 650 } | 647 } |
| 651 | 648 |
| 652 #pragma mark - TestingAditions | 649 #pragma mark - TestingAditions |
| 653 | 650 |
| 654 - (void)setDocumentInteractionController: | 651 - (void)setDocumentInteractionController: |
| 655 (UIDocumentInteractionController*)controller { | 652 (UIDocumentInteractionController*)controller { |
| 656 documentController_.reset([controller retain]); | 653 documentController_ = controller; |
| 657 } | 654 } |
| 658 | 655 |
| 659 - (NSString*)suggestedFilename { | 656 - (NSString*)suggestedFilename { |
| 660 return suggestedFilename_.get(); | 657 return suggestedFilename_; |
| 661 } | 658 } |
| 662 | 659 |
| 663 @end | 660 @end |
| OLD | NEW |