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 #include "chrome/browser/ui/tabs/tab_utils.h"
7 #include "base/strings/string16.h"
8 #include "chrome/browser/media/media_capture_devices_dispatcher.h"
9 #include "chrome/browser/media/media_stream_capture_indicator.h"
10 #include "chrome/grit/generated_resources.h"
11 #include "content/public/browser/web_contents.h"
12 #include "grit/theme_resources.h"
13 #include "ui/base/l10n/l10n_util.h"
14 #include "ui/base/resource/resource_bundle.h"
15 #include "ui/gfx/animation/multi_animation.h"
21 // Interval between frame updates of the tab indicator animations. This is not
22 // the usual 60 FPS because a trade-off must be made between tab UI animation
23 // smoothness and media recording/playback performance on low-end hardware.
24 const int kIndicatorFrameIntervalMs
= 50; // 20 FPS
26 // Fade-in/out duration for the tab indicator animations. Fade-in is quick to
27 // immediately notify the user. Fade-out is more gradual, so that the user has
28 // a chance of finding a tab that has quickly "blipped" on and off.
29 const int kIndicatorFadeInDurationMs
= 200;
30 const int kIndicatorFadeOutDurationMs
= 1000;
32 // Animation that throbs in (towards 1.0) and out (towards 0.0), and ends in the
34 class TabRecordingIndicatorAnimation
: public gfx::MultiAnimation
{
36 virtual ~TabRecordingIndicatorAnimation() {}
38 // Overridden to provide alternating "towards in" and "towards out" behavior.
39 virtual double GetCurrentValue() const OVERRIDE
;
41 static scoped_ptr
<TabRecordingIndicatorAnimation
> Create();
44 TabRecordingIndicatorAnimation(const gfx::MultiAnimation::Parts
& parts
,
45 const base::TimeDelta interval
)
46 : MultiAnimation(parts
, interval
) {}
48 // Number of times to "toggle throb" the recording and tab capture indicators
49 // when they first appear.
50 static const int kCaptureIndicatorThrobCycles
= 5;
53 double TabRecordingIndicatorAnimation::GetCurrentValue() const {
54 return current_part_index() % 2 ?
55 1.0 - MultiAnimation::GetCurrentValue() :
56 MultiAnimation::GetCurrentValue();
59 scoped_ptr
<TabRecordingIndicatorAnimation
>
60 TabRecordingIndicatorAnimation::Create() {
61 MultiAnimation::Parts parts
;
62 COMPILE_ASSERT(kCaptureIndicatorThrobCycles
% 2 != 0,
63 must_be_odd_so_animation_finishes_in_showing_state
);
64 for (int i
= 0; i
< kCaptureIndicatorThrobCycles
; ++i
) {
65 parts
.push_back(MultiAnimation::Part(
66 i
% 2 ? kIndicatorFadeOutDurationMs
: kIndicatorFadeInDurationMs
,
67 gfx::Tween::EASE_IN
));
69 const base::TimeDelta interval
=
70 base::TimeDelta::FromMilliseconds(kIndicatorFrameIntervalMs
);
71 scoped_ptr
<TabRecordingIndicatorAnimation
> animation(
72 new TabRecordingIndicatorAnimation(parts
, interval
));
73 animation
->set_continuous(false);
74 return animation
.Pass();
79 bool ShouldTabShowFavicon(int capacity
,
83 TabMediaState media_state
) {
86 int required_capacity
= 1;
87 if (ShouldTabShowCloseButton(capacity
, is_pinned_tab
, is_active_tab
))
89 if (ShouldTabShowMediaIndicator(
90 capacity
, is_pinned_tab
, is_active_tab
, has_favicon
, media_state
)) {
93 return capacity
>= required_capacity
;
96 bool ShouldTabShowMediaIndicator(int capacity
,
100 TabMediaState media_state
) {
101 if (media_state
== TAB_MEDIA_STATE_NONE
)
103 if (ShouldTabShowCloseButton(capacity
, is_pinned_tab
, is_active_tab
))
104 return capacity
>= 2;
105 return capacity
>= 1;
108 bool ShouldTabShowCloseButton(int capacity
,
110 bool is_active_tab
) {
113 else if (is_active_tab
)
116 return capacity
>= 3;
119 bool IsPlayingAudio(content::WebContents
* contents
) {
120 return contents
->WasRecentlyAudible();
123 TabMediaState
GetTabMediaStateForContents(content::WebContents
* contents
) {
125 return TAB_MEDIA_STATE_NONE
;
127 scoped_refptr
<MediaStreamCaptureIndicator
> indicator
=
128 MediaCaptureDevicesDispatcher::GetInstance()->
129 GetMediaStreamCaptureIndicator();
130 if (indicator
.get()) {
131 if (indicator
->IsBeingMirrored(contents
))
132 return TAB_MEDIA_STATE_CAPTURING
;
133 if (indicator
->IsCapturingUserMedia(contents
))
134 return TAB_MEDIA_STATE_RECORDING
;
137 if (IsPlayingAudio(contents
))
138 return TAB_MEDIA_STATE_AUDIO_PLAYING
;
140 return TAB_MEDIA_STATE_NONE
;
143 const gfx::Image
& GetTabMediaIndicatorImage(TabMediaState media_state
) {
144 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
145 switch (media_state
) {
146 case TAB_MEDIA_STATE_AUDIO_PLAYING
:
147 return rb
.GetNativeImageNamed(IDR_TAB_AUDIO_INDICATOR
);
148 case TAB_MEDIA_STATE_RECORDING
:
149 return rb
.GetNativeImageNamed(IDR_TAB_RECORDING_INDICATOR
);
150 case TAB_MEDIA_STATE_CAPTURING
:
151 return rb
.GetNativeImageNamed(IDR_TAB_CAPTURE_INDICATOR
);
152 case TAB_MEDIA_STATE_NONE
:
156 return rb
.GetNativeImageNamed(IDR_SAD_FAVICON
);
159 scoped_ptr
<gfx::Animation
> CreateTabMediaIndicatorFadeAnimation(
160 TabMediaState media_state
) {
161 if (media_state
== TAB_MEDIA_STATE_RECORDING
||
162 media_state
== TAB_MEDIA_STATE_CAPTURING
) {
163 return TabRecordingIndicatorAnimation::Create().PassAs
<gfx::Animation
>();
166 // Note: While it seems silly to use a one-part MultiAnimation, it's the only
167 // gfx::Animation implementation that lets us control the frame interval.
168 gfx::MultiAnimation::Parts parts
;
169 const bool is_for_fade_in
= (media_state
!= TAB_MEDIA_STATE_NONE
);
170 parts
.push_back(gfx::MultiAnimation::Part(
171 is_for_fade_in
? kIndicatorFadeInDurationMs
: kIndicatorFadeOutDurationMs
,
172 gfx::Tween::EASE_IN
));
173 const base::TimeDelta interval
=
174 base::TimeDelta::FromMilliseconds(kIndicatorFrameIntervalMs
);
175 scoped_ptr
<gfx::MultiAnimation
> animation(
176 new gfx::MultiAnimation(parts
, interval
));
177 animation
->set_continuous(false);
178 return animation
.PassAs
<gfx::Animation
>();
181 base::string16
AssembleTabTooltipText(const base::string16
& title
,
182 TabMediaState media_state
) {
183 if (media_state
== TAB_MEDIA_STATE_NONE
)
186 base::string16 result
= title
;
188 result
.append(1, '\n');
189 switch (media_state
) {
190 case TAB_MEDIA_STATE_AUDIO_PLAYING
:
192 l10n_util::GetStringUTF16(IDS_TOOLTIP_TAB_MEDIA_STATE_AUDIO_PLAYING
));
194 case TAB_MEDIA_STATE_RECORDING
:
196 l10n_util::GetStringUTF16(IDS_TOOLTIP_TAB_MEDIA_STATE_RECORDING
));
198 case TAB_MEDIA_STATE_CAPTURING
:
200 l10n_util::GetStringUTF16(IDS_TOOLTIP_TAB_MEDIA_STATE_CAPTURING
));
202 case TAB_MEDIA_STATE_NONE
:
209 } // namespace chrome