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::TimeDelta
& progress_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
>(progress_time
.InMilliseconds() * 0.08);
101 } else if (percent_done
> 0) {
102 sweep_angle
= static_cast<SkScalar
>(360 * percent_done
/ 100.0);
107 progress
.addArc(SkRect::MakeLTRB(kCenterPoint
- kRadius
,
108 kCenterPoint
- kRadius
,
109 kCenterPoint
+ kRadius
,
110 kCenterPoint
+ kRadius
),
111 start_pos
, sweep_angle
);
112 SkPaint progress_paint
;
113 progress_paint
.setColor(indicator_color
);
114 progress_paint
.setStyle(SkPaint::kStroke_Style
);
115 progress_paint
.setStrokeWidth(1.7f
);
116 progress_paint
.setAntiAlias(true);
117 canvas
->DrawPath(progress
, progress_paint
);
121 void DownloadShelf::PaintDownloadComplete(
123 const ui::ThemeProvider
& theme_provider
,
124 double animation_progress
) {
125 // Start at full opacity, then loop back and forth five times before ending
127 canvas
->sk_canvas()->saveLayerAlpha(nullptr, GetOpacity(animation_progress
));
128 PaintDownloadProgress(canvas
, theme_provider
, base::TimeDelta(), 100);
129 canvas
->sk_canvas()->restore();
133 void DownloadShelf::PaintDownloadInterrupted(
135 const ui::ThemeProvider
& theme_provider
,
136 double animation_progress
) {
137 // Start at zero opacity, then loop back and forth five times before ending
139 PaintDownloadComplete(canvas
, theme_provider
, 1.0 - animation_progress
);
142 void DownloadShelf::AddDownload(DownloadItem
* download
) {
144 if (DownloadItemModel(download
).ShouldRemoveFromShelfWhenComplete()) {
145 // If we are going to remove the download from the shelf upon completion,
146 // wait a few seconds to see if it completes quickly. If it's a small
147 // download, then the user won't have time to interact with it.
148 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
150 base::Bind(&DownloadShelf::ShowDownloadById
,
151 weak_ptr_factory_
.GetWeakPtr(), download
->GetId()),
152 GetTransientDownloadShowDelay());
154 ShowDownload(download
);
158 void DownloadShelf::Show() {
160 should_show_on_unhide_
= true;
166 void DownloadShelf::Close(CloseReason reason
) {
168 should_show_on_unhide_
= false;
174 void DownloadShelf::Hide() {
179 should_show_on_unhide_
= true;
184 void DownloadShelf::Unhide() {
188 if (should_show_on_unhide_
) {
189 should_show_on_unhide_
= false;
194 base::TimeDelta
DownloadShelf::GetTransientDownloadShowDelay() {
195 return base::TimeDelta::FromSeconds(kDownloadShowDelayInSeconds
);
198 content::DownloadManager
* DownloadShelf::GetDownloadManager() {
199 return content::BrowserContext::GetDownloadManager(browser()->profile());
202 void DownloadShelf::ShowDownload(DownloadItem
* download
) {
203 if (download
->GetState() == DownloadItem::COMPLETE
&&
204 DownloadItemModel(download
).ShouldRemoveFromShelfWhenComplete())
206 if (!DownloadServiceFactory::GetForBrowserContext(
207 download
->GetBrowserContext())->IsShelfEnabled())
213 DoAddDownload(download
);
215 // browser() can be NULL for tests.
219 // Show the download started animation if:
220 // - Download started animation is enabled for this download. It is disabled
221 // for "Save As" downloads and extension installs, for example.
222 // - The browser has an active visible WebContents. (browser isn't minimized,
223 // or running under a test etc.)
224 // - Rich animations are enabled.
225 content::WebContents
* shelf_tab
=
226 browser()->tab_strip_model()->GetActiveWebContents();
227 if (DownloadItemModel(download
).ShouldShowDownloadStartedAnimation() &&
229 platform_util::IsVisible(shelf_tab
->GetNativeView()) &&
230 gfx::Animation::ShouldRenderRichAnimation()) {
231 DownloadStartedAnimation::Show(shelf_tab
);
235 void DownloadShelf::ShowDownloadById(int32 download_id
) {
236 content::DownloadManager
* download_manager
= GetDownloadManager();
237 if (!download_manager
)
240 DownloadItem
* download
= download_manager
->GetDownload(download_id
);
244 ShowDownload(download
);