1 // Copyright (c) 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.
5 #define _USE_MATH_DEFINES // For VC++ to get M_PI. This has to be first.
7 #include "chrome/browser/download/download_shelf.h"
11 #include "base/bind.h"
12 #include "base/callback.h"
13 #include "base/location.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/thread_task_runner_handle.h"
17 #include "base/time/time.h"
18 #include "chrome/browser/download/download_item_model.h"
19 #include "chrome/browser/download/download_service.h"
20 #include "chrome/browser/download/download_service_factory.h"
21 #include "chrome/browser/download/download_started_animation.h"
22 #include "chrome/browser/platform_util.h"
23 #include "chrome/browser/profiles/profile.h"
24 #include "chrome/browser/themes/theme_properties.h"
25 #include "chrome/browser/ui/browser.h"
26 #include "chrome/browser/ui/tabs/tab_strip_model.h"
27 #include "chrome/grit/locale_settings.h"
28 #include "content/public/browser/browser_context.h"
29 #include "content/public/browser/download_item.h"
30 #include "content/public/browser/download_manager.h"
31 #include "content/public/browser/web_contents.h"
32 #include "grit/theme_resources.h"
33 #include "third_party/skia/include/core/SkPaint.h"
34 #include "third_party/skia/include/core/SkPath.h"
35 #include "ui/base/l10n/l10n_util.h"
36 #include "ui/base/resource/resource_bundle.h"
37 #include "ui/base/theme_provider.h"
38 #include "ui/gfx/animation/animation.h"
39 #include "ui/gfx/canvas.h"
41 using content::DownloadItem
;
45 // Delay before we show a transient download.
46 const int64 kDownloadShowDelayInSeconds
= 2;
48 // Get the opacity based on |animation_progress|, with values in [0.0, 1.0].
49 // Range of return value is [0, 255].
50 int GetOpacity(double animation_progress
) {
51 DCHECK(animation_progress
>= 0 && animation_progress
<= 1);
53 // How many times to cycle the complete animation. This should be an odd
54 // number so that the animation ends faded out.
55 static const int kCompleteAnimationCycles
= 5;
56 double temp
= animation_progress
* kCompleteAnimationCycles
* M_PI
+ M_PI_2
;
57 temp
= sin(temp
) / 2 + 0.5;
58 return static_cast<int>(255.0 * temp
);
63 DownloadShelf::DownloadShelf()
64 : should_show_on_unhide_(false),
66 weak_ptr_factory_(this) {
69 DownloadShelf::~DownloadShelf() {}
71 // Download progress painting --------------------------------------------------
74 void DownloadShelf::PaintDownloadProgress(
76 const ui::ThemeProvider
& theme_provider
,
77 const base::TimeTicks
& progress_start_time
,
79 // Draw background (light blue circle).
81 bg_paint
.setStyle(SkPaint::kFill_Style
);
82 SkColor indicator_color
=
83 theme_provider
.GetColor(ThemeProperties::COLOR_THROBBER_SPINNING
);
84 bg_paint
.setColor(SkColorSetA(indicator_color
, 0x33));
85 bg_paint
.setAntiAlias(true);
86 const SkScalar kCenterPoint
= kProgressIndicatorSize
/ 2.f
;
87 const SkScalar kRadius
= 12.5f
;
89 bg
.addCircle(kCenterPoint
, kCenterPoint
, kRadius
);
90 canvas
->DrawPath(bg
, bg_paint
);
92 // Calculate progress.
93 SkScalar sweep_angle
= 0.f
;
94 // Start at 12 o'clock.
95 SkScalar start_pos
= SkIntToScalar(270);
96 if (percent_done
< 0) {
97 // For unknown size downloads, draw a 50 degree sweep that moves at
98 // 0.08 degrees per millisecond.
100 start_pos
+= static_cast<SkScalar
>(
101 (base::TimeTicks::Now() - progress_start_time
).InMilliseconds() * 0.08);
102 } else if (percent_done
> 0) {
103 sweep_angle
= static_cast<SkScalar
>(360 * percent_done
/ 100.0);
108 progress
.addArc(SkRect::MakeLTRB(kCenterPoint
- kRadius
,
109 kCenterPoint
- kRadius
,
110 kCenterPoint
+ kRadius
,
111 kCenterPoint
+ kRadius
),
112 start_pos
, sweep_angle
);
113 SkPaint progress_paint
;
114 progress_paint
.setColor(indicator_color
);
115 progress_paint
.setStyle(SkPaint::kStroke_Style
);
116 progress_paint
.setStrokeWidth(1.7f
);
117 progress_paint
.setAntiAlias(true);
118 canvas
->DrawPath(progress
, progress_paint
);
122 void DownloadShelf::PaintDownloadComplete(
124 const ui::ThemeProvider
& theme_provider
,
125 double animation_progress
) {
126 // Start at full opacity, then loop back and forth five times before ending
128 canvas
->sk_canvas()->saveLayerAlpha(nullptr, GetOpacity(animation_progress
));
129 PaintDownloadProgress(canvas
, theme_provider
, base::TimeTicks(), 100);
130 canvas
->sk_canvas()->restore();
134 void DownloadShelf::PaintDownloadInterrupted(
136 const ui::ThemeProvider
& theme_provider
,
137 double animation_progress
) {
138 // Start at zero opacity, then loop back and forth five times before ending
140 PaintDownloadComplete(canvas
, theme_provider
, 1.0 - animation_progress
);
143 void DownloadShelf::AddDownload(DownloadItem
* download
) {
145 if (DownloadItemModel(download
).ShouldRemoveFromShelfWhenComplete()) {
146 // If we are going to remove the download from the shelf upon completion,
147 // wait a few seconds to see if it completes quickly. If it's a small
148 // download, then the user won't have time to interact with it.
149 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
151 base::Bind(&DownloadShelf::ShowDownloadById
,
152 weak_ptr_factory_
.GetWeakPtr(), download
->GetId()),
153 GetTransientDownloadShowDelay());
155 ShowDownload(download
);
159 void DownloadShelf::Show() {
161 should_show_on_unhide_
= true;
167 void DownloadShelf::Close(CloseReason reason
) {
169 should_show_on_unhide_
= false;
175 void DownloadShelf::Hide() {
180 should_show_on_unhide_
= true;
185 void DownloadShelf::Unhide() {
189 if (should_show_on_unhide_
) {
190 should_show_on_unhide_
= false;
195 base::TimeDelta
DownloadShelf::GetTransientDownloadShowDelay() {
196 return base::TimeDelta::FromSeconds(kDownloadShowDelayInSeconds
);
199 content::DownloadManager
* DownloadShelf::GetDownloadManager() {
200 return content::BrowserContext::GetDownloadManager(browser()->profile());
203 void DownloadShelf::ShowDownload(DownloadItem
* download
) {
204 if (download
->GetState() == DownloadItem::COMPLETE
&&
205 DownloadItemModel(download
).ShouldRemoveFromShelfWhenComplete())
207 if (!DownloadServiceFactory::GetForBrowserContext(
208 download
->GetBrowserContext())->IsShelfEnabled())
214 DoAddDownload(download
);
216 // browser() can be NULL for tests.
220 // Show the download started animation if:
221 // - Download started animation is enabled for this download. It is disabled
222 // for "Save As" downloads and extension installs, for example.
223 // - The browser has an active visible WebContents. (browser isn't minimized,
224 // or running under a test etc.)
225 // - Rich animations are enabled.
226 content::WebContents
* shelf_tab
=
227 browser()->tab_strip_model()->GetActiveWebContents();
228 if (DownloadItemModel(download
).ShouldShowDownloadStartedAnimation() &&
230 platform_util::IsVisible(shelf_tab
->GetNativeView()) &&
231 gfx::Animation::ShouldRenderRichAnimation()) {
232 DownloadStartedAnimation::Show(shelf_tab
);
236 void DownloadShelf::ShowDownloadById(int32 download_id
) {
237 content::DownloadManager
* download_manager
= GetDownloadManager();
238 if (!download_manager
)
241 DownloadItem
* download
= download_manager
->GetDownload(download_id
);
245 ShowDownload(download
);