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

Side by Side Diff: ios/chrome/browser/ui/open_in_controller.mm

Issue 2589803002: Upstream Chrome on iOS source code [6/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 2012 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/open_in_controller.h"
6
7 #include "base/files/file_path.h"
8 #include "base/ios/weak_nsobject.h"
9 #include "base/location.h"
10 #include "base/logging.h"
11 #import "base/mac/bind_objc_block.h"
12 #include "base/mac/objc_property_releaser.h"
13 #include "base/sequenced_task_runner.h"
14 #include "base/strings/sys_string_conversions.h"
15 #include "base/threading/sequenced_worker_pool.h"
16 #include "base/threading/thread_restrictions.h"
17 #include "components/strings/grit/components_strings.h"
18 #import "ios/chrome/browser/ui/alert_coordinator/alert_coordinator.h"
19 #import "ios/chrome/browser/ui/open_in_controller_testing.h"
20 #include "ios/chrome/browser/ui/ui_util.h"
21 #import "ios/chrome/browser/ui/uikit_ui_util.h"
22 #include "ios/chrome/grit/ios_strings.h"
23 #include "ios/web/public/web_thread.h"
24 #import "ios/web/web_state/ui/crw_web_controller.h"
25 #include "net/base/load_flags.h"
26 #include "net/url_request/url_fetcher.h"
27 #include "net/url_request/url_fetcher_delegate.h"
28 #include "net/url_request/url_request_context_getter.h"
29 #include "ui/base/l10n/l10n_util_mac.h"
30 #import "ui/gfx/ios/NSString+CrStringDrawing.h"
31 #include "url/gurl.h"
32
33 namespace {
34 // The path in the temp directory containing documents that are to be opened in
35 // other applications.
36 static NSString* const kDocumentsTempPath = @"OpenIn";
37
38 static const int kHTTPResponseCodeSucceeded = 200;
39
40 // Duration of the show/hide animation for the |openInToolbar_|.
41 const NSTimeInterval kOpenInToolbarAnimationDuration = 0.2;
42
43 // Duration to show or hide the |overlayedView_|.
44 const NSTimeInterval kOverlayViewAnimationDuration = 0.3;
45
46 // Time interval after which the |openInToolbar_| is automatically hidden.
47 const NSTimeInterval kOpenInToolbarDisplayDuration = 2.0;
48
49 // Text size used for the label indicating a download in progress.
50 const CGFloat kLabelTextSize = 22.0;
51
52 // Alpha value for the background view of |overlayedView_|.
53 const CGFloat kOverlayedViewBackgroundAlpha = 0.6;
54
55 // Width of the label displayed on the |overlayedView_| as a percentage of the
56 // |overlayedView_|'s width.
57 const CGFloat kOverlayedViewLabelWidthPercentage = 0.7;
58
59 // Bottom margin for the label displayed on the |overlayedView_|.
60 const CGFloat kOverlayedViewLabelBottomMargin = 60;
61
62 } // anonymous namespace
63
64 @interface OpenInController () {
65 // AlertCoordinator for showing an alert if no applications were found to open
66 // the current document.
67 base::scoped_nsobject<AlertCoordinator> _alertCoordinator;
68 }
69
70 // URLFetcher delegate method called when |fetcher_| completes a request.
71 - (void)urlFetchDidComplete:(const net::URLFetcher*)source;
72 // Ensures the destination directory is created and any contained obsolete files
73 // are deleted. Returns YES if the directory is created successfully.
74 + (BOOL)createDestinationDirectoryAndRemoveObsoleteFiles;
75 // Starts downloading the file at path |kDocumentsTempPath| with the name
76 // |suggestedFilename_|.
77 - (void)startDownload;
78 // Shows the overlayed toolbar |openInToolbar_|.
79 - (void)showOpenInToolbar;
80 // Hides the overlayed toolbar |openInToolbar_|.
81 - (void)hideOpenInToolbar;
82 // Called when there is a tap on the |webController_|'s view to display the
83 // overlayed toolbar |openInToolbar_| if necessary and (re)schedule the
84 // |openInTimer_|.
85 - (void)handleTapFrom:(UIGestureRecognizer*)gestureRecognizer;
86 // Downloads the file at |documentURL_| and presents the OpenIn menu for opening
87 // it in other applications.
88 - (void)exportFileWithOpenInMenuAnchoredAt:(id)sender;
89 // Called when there is a tap on the |overlayedView_| to cancel the file
90 // download.
91 - (void)handleTapOnOverlayedView:(UIGestureRecognizer*)gestureRecognizer;
92 // Removes |overlayedView_| from the top view of the application.
93 - (void)removeOverlayedView;
94 // Shows an alert with the given error message.
95 - (void)showErrorWithMessage:(NSString*)message;
96 // Presents the OpenIn menu for the file at |fileURL|.
97 - (void)presentOpenInMenuForFileAtURL:(NSURL*)fileURL;
98 // Removes the file at path |path|.
99 - (void)removeDocumentAtPath:(NSString*)path;
100 // Removes all the stored files at path |path|.
101 + (void)removeAllStoredDocumentsAtPath:(NSString*)path;
102 // Shows an overlayed spinner on the top view to indicate that a file download
103 // is in progress.
104 - (void)showDownloadOverlayView;
105 // Returns a toolbar with an "Open in..." button to be overlayed on a document
106 // on tap.
107 - (UIToolbar*)openInToolbar;
108 @end
109
110 // Bridge to deliver method calls from C++ to the |OpenInController| class.
111 class OpenInControllerBridge
112 : public net::URLFetcherDelegate,
113 public base::RefCountedThreadSafe<OpenInControllerBridge> {
114 public:
115 explicit OpenInControllerBridge(OpenInController* owner) : owner_(owner) {}
116
117 void OnURLFetchComplete(const net::URLFetcher* source) override {
118 DCHECK(owner_);
119 [owner_ urlFetchDidComplete:source];
120 }
121
122 BOOL CreateDestinationDirectoryAndRemoveObsoleteFiles(void) {
123 return [OpenInController createDestinationDirectoryAndRemoveObsoleteFiles];
124 }
125
126 void OnDestinationDirectoryCreated(BOOL success) {
127 DCHECK_CURRENTLY_ON(web::WebThread::UI);
128 if (!success)
129 [owner_ hideOpenInToolbar];
130 else
131 [owner_ startDownload];
132 }
133
134 void OnOwnerDisabled() {
135 // When the owner is disabled:
136 // - if there is a task in flight posted via |PostTaskAndReplyWithResult|
137 // then dereferencing |bridge_| will not release it as |bridge_| is also
138 // referenced by the task posting; setting |owner_| to nil makes sure that
139 // no methods are called on it, and it works since |owner_| is only used on
140 // the main thread.
141 // - if there is a task in flight posted by the URLFetcher then
142 // |OpenInController| destroys the fetcher and cancels the callback. This is
143 // why |OnURLFetchComplete| will neved be called after |owner_| is disabled.
144 owner_ = nil;
145 }
146
147 protected:
148 friend base::RefCountedThreadSafe<OpenInControllerBridge>;
149 ~OpenInControllerBridge() override {}
150
151 private:
152 OpenInController* owner_; // weak
153 };
154
155 @implementation OpenInController {
156 // Bridge from C++ to Obj-C class.
157 scoped_refptr<OpenInControllerBridge> bridge_;
158
159 // URL of the document.
160 GURL documentURL_;
161
162 // Controller for opening documents in other applications.
163 base::scoped_nsobject<UIDocumentInteractionController> documentController_;
164
165 // Toolbar overlay to be displayed on tap.
166 base::scoped_nsobject<OpenInToolbar> openInToolbar_;
167
168 // Timer used to automatically hide the |openInToolbar_| after a period.
169 base::scoped_nsobject<NSTimer> openInTimer_;
170
171 // Gesture recognizer to catch taps on the document.
172 base::scoped_nsobject<UITapGestureRecognizer> tapRecognizer_;
173
174 // Suggested filename for the document.
175 base::scoped_nsobject<NSString> suggestedFilename_;
176
177 // Fetcher used to redownload the document and save it in the sandbox.
178 std::unique_ptr<net::URLFetcher> fetcher_;
179
180 // CRWWebController used to check if the tap is not on a link and the
181 // |openInToolbar_| should be displayed.
182 base::scoped_nsobject<CRWWebController> webController_;
183
184 // URLRequestContextGetter needed for the URLFetcher.
185 scoped_refptr<net::URLRequestContextGetter> requestContext_;
186
187 // Spinner view displayed while the file is downloading.
188 base::scoped_nsobject<UIView> overlayedView_;
189
190 // The location where the "Open in..." menu is anchored.
191 CGRect anchorLocation_;
192
193 // YES if the file download was canceled.
194 BOOL downloadCanceled_;
195
196 // YES if the OpenIn menu is displayed.
197 BOOL isOpenInMenuDisplayed_;
198
199 // Task runner on which file operations should happen.
200 scoped_refptr<base::SequencedTaskRunner> sequencedTaskRunner_;
201
202 base::mac::ObjCPropertyReleaser propertyReleaser_OpenInController_;
203 }
204
205 - (id)initWithRequestContext:(net::URLRequestContextGetter*)requestContext
206 webController:(CRWWebController*)webController {
207 self = [super init];
208 if (self) {
209 requestContext_ = requestContext;
210 webController_.reset([webController retain]);
211 tapRecognizer_.reset([[UITapGestureRecognizer alloc]
212 initWithTarget:self
213 action:@selector(handleTapFrom:)]);
214 [tapRecognizer_ setDelegate:self];
215 base::SequencedWorkerPool* pool = web::WebThread::GetBlockingPool();
216 sequencedTaskRunner_ =
217 pool->GetSequencedTaskRunner(pool->GetSequenceToken());
218 isOpenInMenuDisplayed_ = NO;
219 propertyReleaser_OpenInController_.Init(self, [OpenInController class]);
220 }
221 return self;
222 }
223
224 - (void)enableWithDocumentURL:(const GURL&)documentURL
225 suggestedFilename:(NSString*)suggestedFilename {
226 documentURL_ = GURL(documentURL);
227 suggestedFilename_.reset([suggestedFilename retain]);
228 [webController_ addGestureRecognizerToWebView:tapRecognizer_];
229 [self openInToolbar].alpha = 0.0f;
230 [webController_ addToolbarViewToWebView:[self openInToolbar]];
231 }
232
233 - (void)disable {
234 [self openInToolbar].alpha = 0.0f;
235 [openInTimer_ invalidate];
236 if (bridge_.get())
237 bridge_->OnOwnerDisabled();
238 bridge_ = nil;
239 [webController_ removeGestureRecognizerFromWebView:tapRecognizer_];
240 [webController_ removeToolbarViewFromWebView:[self openInToolbar]];
241 [documentController_ dismissMenuAnimated:NO];
242 [documentController_ setDelegate:nil];
243 documentURL_ = GURL();
244 suggestedFilename_.reset();
245 fetcher_.reset();
246 }
247
248 - (void)detachFromWebController {
249 [self disable];
250 // Animation blocks may be keeping this object alive; don't extend the
251 // lifetime of CRWWebController.
252 webController_.reset();
253 }
254
255 - (void)dealloc {
256 [self disable];
257 [super dealloc];
258 }
259
260 - (void)handleTapFrom:(UIGestureRecognizer*)gestureRecognizer {
261 if ([gestureRecognizer state] == UIGestureRecognizerStateEnded) {
262 [self showOpenInToolbar];
263 }
264 }
265
266 - (void)showOpenInToolbar {
267 if ([openInTimer_ isValid]) {
268 [openInTimer_ setFireDate:([NSDate dateWithTimeIntervalSinceNow:
269 kOpenInToolbarDisplayDuration])];
270 } else {
271 openInTimer_.reset(
272 [[NSTimer scheduledTimerWithTimeInterval:kOpenInToolbarDisplayDuration
273 target:self
274 selector:@selector(hideOpenInToolbar)
275 userInfo:nil
276 repeats:NO] retain]);
277 UIView* openInToolbar = [self openInToolbar];
278 [UIView animateWithDuration:kOpenInToolbarAnimationDuration
279 animations:^{
280 [openInToolbar setAlpha:1.0];
281 }];
282 }
283 }
284
285 - (void)hideOpenInToolbar {
286 if (!openInToolbar_)
287 return;
288 UIView* openInToolbar = [self openInToolbar];
289 [UIView animateWithDuration:kOpenInToolbarAnimationDuration
290 animations:^{
291 [openInToolbar setAlpha:0.0];
292 }];
293 }
294
295 - (void)exportFileWithOpenInMenuAnchoredAt:(UIView*)view {
296 DCHECK([view isKindOfClass:[UIView class]]);
297 DCHECK_CURRENTLY_ON(web::WebThread::UI);
298 if (!webController_)
299 return;
300
301 anchorLocation_ = [[self openInToolbar] convertRect:view.frame
302 toView:[webController_ view]];
303 [openInTimer_ invalidate];
304 if (!bridge_.get())
305 bridge_ = new OpenInControllerBridge(self);
306
307 // This needs to be done in two steps, on two separate threads. The
308 // first task needs to be done on the worker pool and returns a BOOL which is
309 // then used in the second function, |OnDestinationDirectoryCreated|, which
310 // runs on the UI thread.
311 base::Callback<BOOL(void)> task = base::Bind(
312 &OpenInControllerBridge::CreateDestinationDirectoryAndRemoveObsoleteFiles,
313 bridge_);
314 base::Callback<void(BOOL)> reply = base::Bind(
315 &OpenInControllerBridge::OnDestinationDirectoryCreated, bridge_);
316 base::PostTaskAndReplyWithResult(sequencedTaskRunner_.get(), FROM_HERE, task,
317 reply);
318 }
319
320 - (void)startDownload {
321 NSString* tempDirPath = [NSTemporaryDirectory()
322 stringByAppendingPathComponent:kDocumentsTempPath];
323 NSString* filePath =
324 [tempDirPath stringByAppendingPathComponent:suggestedFilename_.get()];
325
326 // In iPad the toolbar has to be displayed to anchor the "Open in" menu.
327 if (!IsIPadIdiom())
328 [self hideOpenInToolbar];
329
330 // Show an overlayed view to indicate a download is in progress. On tap this
331 // view can be dismissed and the download canceled.
332 [self showDownloadOverlayView];
333 downloadCanceled_ = NO;
334
335 // Ensure |bridge_| is set in case this function is called from a unittest.
336 if (!bridge_.get())
337 bridge_ = new OpenInControllerBridge(self);
338
339 // Download the document and save it at |filePath|.
340 fetcher_ = net::URLFetcher::Create(0, documentURL_, net::URLFetcher::GET,
341 bridge_.get());
342 fetcher_->SetRequestContext(requestContext_.get());
343 fetcher_->SetLoadFlags(net::LOAD_SKIP_CACHE_VALIDATION);
344 fetcher_->SaveResponseToFileAtPath(
345 base::FilePath(base::SysNSStringToUTF8(filePath)), sequencedTaskRunner_);
346 fetcher_->Start();
347 }
348
349 - (void)handleTapOnOverlayedView:(UIGestureRecognizer*)gestureRecognizer {
350 if ([gestureRecognizer state] != UIGestureRecognizerStateEnded)
351 return;
352
353 [self removeOverlayedView];
354 if (IsIPadIdiom())
355 [self hideOpenInToolbar];
356 downloadCanceled_ = YES;
357 }
358
359 - (void)removeOverlayedView {
360 if (!overlayedView_)
361 return;
362
363 UIView* overlayedView = overlayedView_.get();
364 [UIView animateWithDuration:kOverlayViewAnimationDuration
365 animations:^{
366 [overlayedView setAlpha:0.0];
367 }
368 completion:^(BOOL finished) {
369 [overlayedView removeFromSuperview];
370 }];
371 overlayedView_.reset();
372 }
373
374 - (void)showErrorWithMessage:(NSString*)message {
375 UIViewController* topViewController =
376 [[[UIApplication sharedApplication] keyWindow] rootViewController];
377
378 _alertCoordinator.reset([[AlertCoordinator alloc]
379 initWithBaseViewController:topViewController
380 title:nil
381 message:message]);
382
383 [_alertCoordinator addItemWithTitle:l10n_util::GetNSString(IDS_OK)
384 action:nil
385 style:UIAlertActionStyleDefault];
386
387 [_alertCoordinator start];
388 }
389
390 - (void)presentOpenInMenuForFileAtURL:(NSURL*)fileURL {
391 if (!webController_)
392 return;
393
394 if (requestContext_.get()) {
395 // |requestContext_| is nil only if this is called from a unit test, in
396 // which case the |documentController_| was set already.
397 documentController_.reset([[UIDocumentInteractionController
398 interactionControllerWithURL:fileURL] retain]);
399 }
400
401 // TODO(cgrigoruta): The UTI is hardcoded for now, change this when we add
402 // support for other file types as well.
403 [documentController_ setUTI:@"com.adobe.pdf"];
404 [documentController_ setDelegate:self];
405 BOOL success =
406 [documentController_ presentOpenInMenuFromRect:anchorLocation_
407 inView:[webController_ view]
408 animated:YES];
409 if (requestContext_.get()) {
410 [self removeOverlayedView];
411 if (!success) {
412 if (IsIPadIdiom())
413 [self hideOpenInToolbar];
414 NSString* errorMessage =
415 l10n_util::GetNSStringWithFixup(IDS_IOS_OPEN_IN_NO_APPS_REGISTERED);
416 [self showErrorWithMessage:errorMessage];
417 } else {
418 isOpenInMenuDisplayed_ = YES;
419 }
420 }
421 }
422
423 - (void)showDownloadOverlayView {
424 UIViewController* topViewController =
425 [[[UIApplication sharedApplication] keyWindow] rootViewController];
426 UIView* topView = topViewController.view;
427 overlayedView_.reset([[UIView alloc] initWithFrame:[topView bounds]]);
428 [overlayedView_ setAutoresizingMask:(UIViewAutoresizingFlexibleWidth |
429 UIViewAutoresizingFlexibleHeight)];
430 base::scoped_nsobject<UIView> grayBackgroundView(
431 [[UIView alloc] initWithFrame:[overlayedView_ frame]]);
432 [grayBackgroundView setBackgroundColor:[UIColor darkGrayColor]];
433 [grayBackgroundView setAlpha:kOverlayedViewBackgroundAlpha];
434 [grayBackgroundView setAutoresizingMask:(UIViewAutoresizingFlexibleWidth |
435 UIViewAutoresizingFlexibleHeight)];
436 [overlayedView_ addSubview:grayBackgroundView];
437
438 base::scoped_nsobject<UIActivityIndicatorView> spinner([
439 [UIActivityIndicatorView alloc]
440 initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]);
441 [spinner setFrame:[overlayedView_ frame]];
442 [spinner setHidesWhenStopped:YES];
443 [spinner setUserInteractionEnabled:NO];
444 [spinner startAnimating];
445 [spinner setAutoresizingMask:(UIViewAutoresizingFlexibleWidth |
446 UIViewAutoresizingFlexibleHeight)];
447 [overlayedView_ addSubview:spinner];
448
449 base::scoped_nsobject<UILabel> label([[UILabel alloc] init]);
450 [label setTextColor:[UIColor whiteColor]];
451 [label setFont:GetUIFont(FONT_HELVETICA, true, kLabelTextSize)];
452 [label setNumberOfLines:0];
453 [label setShadowColor:[UIColor blackColor]];
454 [label setShadowOffset:CGSizeMake(0.0, 1.0)];
455 [label setBackgroundColor:[UIColor clearColor]];
456 [label setText:l10n_util::GetNSString(IDS_IOS_OPEN_IN_FILE_DOWNLOAD_CANCEL)];
457 [label setLineBreakMode:NSLineBreakByWordWrapping];
458 [label setTextAlignment:NSTextAlignmentCenter];
459 CGFloat labelWidth =
460 [overlayedView_ frame].size.width * kOverlayedViewLabelWidthPercentage;
461 CGFloat originX = ([overlayedView_ frame].size.width - labelWidth) / 2;
462
463 CGFloat labelHeight =
464 [[label text] cr_boundingSizeWithSize:CGSizeMake(labelWidth, CGFLOAT_MAX)
465 font:[label font]]
466 .height;
467 CGFloat originY =
468 [overlayedView_ center].y - labelHeight - kOverlayedViewLabelBottomMargin;
469 [label setFrame:CGRectMake(originX, originY, labelWidth, labelHeight)];
470 [overlayedView_ addSubview:label];
471
472 base::scoped_nsobject<UITapGestureRecognizer> tapRecognizer(
473 [[UITapGestureRecognizer alloc]
474 initWithTarget:self
475 action:@selector(handleTapOnOverlayedView:)]);
476 [tapRecognizer setDelegate:self];
477 [overlayedView_ addGestureRecognizer:tapRecognizer];
478
479 [overlayedView_ setAlpha:0.0];
480 [topView addSubview:overlayedView_];
481 UIView* overlayedView = overlayedView_.get();
482 [UIView animateWithDuration:kOverlayViewAnimationDuration
483 animations:^{
484 [overlayedView setAlpha:1.0];
485 }];
486 }
487
488 - (UIView*)openInToolbar {
489 if (!openInToolbar_) {
490 openInToolbar_.reset([[OpenInToolbar alloc]
491 initWithTarget:self
492 action:@selector(exportFileWithOpenInMenuAnchoredAt:)]);
493 }
494 return openInToolbar_.get();
495 }
496
497 #pragma mark -
498 #pragma mark File management
499
500 - (void)removeDocumentAtPath:(NSString*)path {
501 base::ThreadRestrictions::AssertIOAllowed();
502 NSFileManager* fileManager = [NSFileManager defaultManager];
503 NSError* error = nil;
504 if (![fileManager removeItemAtPath:path error:&error]) {
505 DLOG(ERROR) << "Failed to remove file: "
506 << base::SysNSStringToUTF8([error description]);
507 }
508 }
509
510 + (void)removeAllStoredDocumentsAtPath:(NSString*)tempDirPath {
511 base::ThreadRestrictions::AssertIOAllowed();
512 NSFileManager* fileManager = [NSFileManager defaultManager];
513 NSError* error = nil;
514 NSArray* documentFiles =
515 [fileManager contentsOfDirectoryAtPath:tempDirPath error:&error];
516 if (!documentFiles) {
517 DLOG(ERROR) << "Failed to get content of directory at path: "
518 << base::SysNSStringToUTF8([error description]);
519 return;
520 }
521
522 for (NSString* filename in documentFiles) {
523 NSString* filePath = [tempDirPath stringByAppendingPathComponent:filename];
524 if (![fileManager removeItemAtPath:filePath error:&error]) {
525 DLOG(ERROR) << "Failed to remove file: "
526 << base::SysNSStringToUTF8([error description]);
527 }
528 }
529 }
530
531 + (BOOL)createDestinationDirectoryAndRemoveObsoleteFiles {
532 base::ThreadRestrictions::AssertIOAllowed();
533 NSString* tempDirPath = [NSTemporaryDirectory()
534 stringByAppendingPathComponent:kDocumentsTempPath];
535 NSFileManager* fileManager = [NSFileManager defaultManager];
536 BOOL isDirectory;
537 NSError* error = nil;
538 if (![fileManager fileExistsAtPath:tempDirPath isDirectory:&isDirectory]) {
539 BOOL created = [fileManager createDirectoryAtPath:tempDirPath
540 withIntermediateDirectories:YES
541 attributes:nil
542 error:&error];
543 DCHECK(created);
544 if (!created) {
545 DLOG(ERROR) << "Error creating destination dir: "
546 << base::SysNSStringToUTF8([error description]);
547 return NO;
548 }
549 } else {
550 DCHECK(isDirectory);
551 if (!isDirectory) {
552 DLOG(ERROR) << "Destination Directory already exists and is a file.";
553 return NO;
554 }
555 // Remove all documents that might be still on temporary storage.
556 [self removeAllStoredDocumentsAtPath:(NSString*)tempDirPath];
557 }
558 return YES;
559 }
560
561 #pragma mark -
562 #pragma mark URLFetcher delegate method
563
564 - (void)urlFetchDidComplete:(const net::URLFetcher*)fetcher {
565 DCHECK(fetcher);
566 if (requestContext_.get())
567 DCHECK_CURRENTLY_ON(web::WebThread::UI);
568 base::FilePath filePath;
569 if (fetcher->GetResponseCode() == kHTTPResponseCodeSucceeded &&
570 fetcher->GetResponseAsFilePath(true, &filePath)) {
571 NSURL* fileURL =
572 [NSURL fileURLWithPath:base::SysUTF8ToNSString(filePath.value())];
573 if (downloadCanceled_) {
574 sequencedTaskRunner_->PostTask(FROM_HERE, base::BindBlock(^{
575 [self
576 removeDocumentAtPath:[fileURL path]];
577 }));
578 } else {
579 [self presentOpenInMenuForFileAtURL:fileURL];
580 }
581 } else if (!downloadCanceled_) {
582 if (IsIPadIdiom())
583 [self hideOpenInToolbar];
584 [self removeOverlayedView];
585 [self showErrorWithMessage:l10n_util::GetNSStringWithFixup(
586 IDS_IOS_OPEN_IN_FILE_DOWNLOAD_FAILED)];
587 }
588 }
589
590 #pragma mark -
591 #pragma mark UIDocumentInteractionControllerDelegate Methods
592
593 - (void)documentInteractionController:(UIDocumentInteractionController*)contr
594 didEndSendingToApplication:(NSString*)application {
595 sequencedTaskRunner_->PostTask(FROM_HERE, base::BindBlock(^{
596 [self
597 removeDocumentAtPath:[[contr URL] path]];
598 }));
599 if (IsIPadIdiom()) {
600 // Call the |documentInteractionControllerDidDismissOpenInMenu:| method
601 // as this is not called on the iPad after the document has been opened
602 // in another application.
603 [self documentInteractionControllerDidDismissOpenInMenu:contr];
604 }
605 }
606
607 - (void)documentInteractionControllerDidDismissOpenInMenu:
608 (UIDocumentInteractionController*)controller {
609 if (!IsIPadIdiom()) {
610 isOpenInMenuDisplayed_ = NO;
611 // On the iPhone the |openInToolber_| is hidden already.
612 return;
613 }
614
615 // On iPad this method is called whenever the device changes orientation,
616 // even thought the OpenIn menu is not displayed. To distinguish the cases
617 // when this method is called after the OpenIn menu is dismissed, we
618 // check the BOOL |isOpenInMenuDisplayed|.
619 if (isOpenInMenuDisplayed_) {
620 openInTimer_.reset(
621 [[NSTimer scheduledTimerWithTimeInterval:kOpenInToolbarDisplayDuration
622 target:self
623 selector:@selector(hideOpenInToolbar)
624 userInfo:nil
625 repeats:NO] retain]);
626 }
627 isOpenInMenuDisplayed_ = NO;
628 }
629
630 #pragma mark -
631 #pragma mark UIGestureRecognizerDelegate Methods
632
633 - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer*)gestureRecognizer {
634 if ([gestureRecognizer.view isEqual:overlayedView_])
635 return YES;
636
637 CGPoint location = [gestureRecognizer locationInView:[self openInToolbar]];
638 return ![[self openInToolbar] pointInside:location withEvent:nil];
639 }
640
641 #pragma mark - TestingAditions
642
643 - (void)setDocumentInteractionController:
644 (UIDocumentInteractionController*)controller {
645 documentController_.reset([controller retain]);
646 }
647
648 - (NSString*)suggestedFilename {
649 return suggestedFilename_.get();
650 }
651
652 @end
OLDNEW
« no previous file with comments | « ios/chrome/browser/ui/open_in_controller.h ('k') | ios/chrome/browser/ui/open_in_controller_testing.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698