• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "chrome/browser/ui/tabs/tab_utils.h"
6 
7 #include "base/strings/string16.h"
8 #include "chrome/browser/media/audio_stream_indicator.h"
9 #include "chrome/browser/media/media_capture_devices_dispatcher.h"
10 #include "chrome/browser/media/media_stream_capture_indicator.h"
11 #include "grit/generated_resources.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"
16 
17 namespace chrome {
18 
19 namespace {
20 
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
25 
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;
31 
32 // Animation that throbs in (towards 1.0) and out (towards 0.0), and ends in the
33 // "in" state.
34 class TabRecordingIndicatorAnimation : public gfx::MultiAnimation {
35  public:
~TabRecordingIndicatorAnimation()36   virtual ~TabRecordingIndicatorAnimation() {}
37 
38   // Overridden to provide alternating "towards in" and "towards out" behavior.
39   virtual double GetCurrentValue() const OVERRIDE;
40 
41   static scoped_ptr<TabRecordingIndicatorAnimation> Create();
42 
43  private:
TabRecordingIndicatorAnimation(const gfx::MultiAnimation::Parts & parts,const base::TimeDelta interval)44   TabRecordingIndicatorAnimation(const gfx::MultiAnimation::Parts& parts,
45                                  const base::TimeDelta interval)
46       : MultiAnimation(parts, interval) {}
47 
48   // Number of times to "toggle throb" the recording and tab capture indicators
49   // when they first appear.
50   static const int kCaptureIndicatorThrobCycles = 5;
51 };
52 
GetCurrentValue() const53 double TabRecordingIndicatorAnimation::GetCurrentValue() const {
54   return current_part_index() % 2 ?
55       1.0 - MultiAnimation::GetCurrentValue() :
56       MultiAnimation::GetCurrentValue();
57 }
58 
59 scoped_ptr<TabRecordingIndicatorAnimation>
Create()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));
68   }
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();
75 }
76 
77 }  // namespace
78 
ShouldTabShowFavicon(int capacity,bool is_pinned_tab,bool is_active_tab,bool has_favicon,TabMediaState media_state)79 bool ShouldTabShowFavicon(int capacity,
80                           bool is_pinned_tab,
81                           bool is_active_tab,
82                           bool has_favicon,
83                           TabMediaState media_state) {
84   if (!has_favicon)
85     return false;
86   int required_capacity = 1;
87   if (ShouldTabShowCloseButton(capacity, is_pinned_tab, is_active_tab))
88     ++required_capacity;
89   if (ShouldTabShowMediaIndicator(
90           capacity, is_pinned_tab, is_active_tab, has_favicon, media_state)) {
91     ++required_capacity;
92   }
93   return capacity >= required_capacity;
94 }
95 
ShouldTabShowMediaIndicator(int capacity,bool is_pinned_tab,bool is_active_tab,bool has_favicon,TabMediaState media_state)96 bool ShouldTabShowMediaIndicator(int capacity,
97                                  bool is_pinned_tab,
98                                  bool is_active_tab,
99                                  bool has_favicon,
100                                  TabMediaState media_state) {
101   if (media_state == TAB_MEDIA_STATE_NONE)
102     return false;
103   if (ShouldTabShowCloseButton(capacity, is_pinned_tab, is_active_tab))
104     return capacity >= 2;
105   return capacity >= 1;
106 }
107 
ShouldTabShowCloseButton(int capacity,bool is_pinned_tab,bool is_active_tab)108 bool ShouldTabShowCloseButton(int capacity,
109                               bool is_pinned_tab,
110                               bool is_active_tab) {
111   if (is_pinned_tab)
112     return false;
113   else if (is_active_tab)
114     return true;
115   else
116     return capacity >= 3;
117 }
118 
IsPlayingAudio(content::WebContents * contents)119 bool IsPlayingAudio(content::WebContents* contents) {
120   AudioStreamIndicator* audio_indicator =
121       MediaCaptureDevicesDispatcher::GetInstance()->GetAudioStreamIndicator()
122           .get();
123   return audio_indicator && audio_indicator->IsPlayingAudio(contents);
124 }
125 
GetTabMediaStateForContents(content::WebContents * contents)126 TabMediaState GetTabMediaStateForContents(content::WebContents* contents) {
127   if (!contents)
128     return TAB_MEDIA_STATE_NONE;
129 
130   scoped_refptr<MediaStreamCaptureIndicator> indicator =
131       MediaCaptureDevicesDispatcher::GetInstance()->
132           GetMediaStreamCaptureIndicator();
133   if (indicator) {
134     if (indicator->IsBeingMirrored(contents))
135       return TAB_MEDIA_STATE_CAPTURING;
136     if (indicator->IsCapturingUserMedia(contents))
137       return TAB_MEDIA_STATE_RECORDING;
138   }
139 
140   if (IsPlayingAudio(contents))
141     return TAB_MEDIA_STATE_AUDIO_PLAYING;
142 
143   return TAB_MEDIA_STATE_NONE;
144 }
145 
GetTabMediaIndicatorImage(TabMediaState media_state)146 const gfx::Image& GetTabMediaIndicatorImage(TabMediaState media_state) {
147   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
148   switch (media_state) {
149     case TAB_MEDIA_STATE_AUDIO_PLAYING:
150       return rb.GetNativeImageNamed(IDR_TAB_AUDIO_INDICATOR);
151     case TAB_MEDIA_STATE_RECORDING:
152       return rb.GetNativeImageNamed(IDR_TAB_RECORDING_INDICATOR);
153     case TAB_MEDIA_STATE_CAPTURING:
154       return rb.GetNativeImageNamed(IDR_TAB_CAPTURE_INDICATOR);
155     case TAB_MEDIA_STATE_NONE:
156       break;
157   }
158   NOTREACHED();
159   return rb.GetNativeImageNamed(IDR_SAD_FAVICON);
160 }
161 
CreateTabMediaIndicatorFadeAnimation(TabMediaState media_state)162 scoped_ptr<gfx::Animation> CreateTabMediaIndicatorFadeAnimation(
163     TabMediaState media_state) {
164   if (media_state == TAB_MEDIA_STATE_RECORDING ||
165       media_state == TAB_MEDIA_STATE_CAPTURING) {
166     return TabRecordingIndicatorAnimation::Create().PassAs<gfx::Animation>();
167   }
168 
169   // Note: While it seems silly to use a one-part MultiAnimation, it's the only
170   // gfx::Animation implementation that lets us control the frame interval.
171   gfx::MultiAnimation::Parts parts;
172   const bool is_for_fade_in = (media_state != TAB_MEDIA_STATE_NONE);
173   parts.push_back(gfx::MultiAnimation::Part(
174       is_for_fade_in ? kIndicatorFadeInDurationMs : kIndicatorFadeOutDurationMs,
175       gfx::Tween::EASE_IN));
176   const base::TimeDelta interval =
177       base::TimeDelta::FromMilliseconds(kIndicatorFrameIntervalMs);
178   scoped_ptr<gfx::MultiAnimation> animation(
179       new gfx::MultiAnimation(parts, interval));
180   animation->set_continuous(false);
181   return animation.PassAs<gfx::Animation>();
182 }
183 
AssembleTabTooltipText(const base::string16 & title,TabMediaState media_state)184 base::string16 AssembleTabTooltipText(const base::string16& title,
185                                       TabMediaState media_state) {
186   if (media_state == TAB_MEDIA_STATE_NONE)
187     return title;
188 
189   base::string16 result = title;
190   if (!result.empty())
191     result.append(1, '\n');
192   switch (media_state) {
193     case TAB_MEDIA_STATE_AUDIO_PLAYING:
194       result.append(
195           l10n_util::GetStringUTF16(IDS_TOOLTIP_TAB_MEDIA_STATE_AUDIO_PLAYING));
196       break;
197     case TAB_MEDIA_STATE_RECORDING:
198       result.append(
199           l10n_util::GetStringUTF16(IDS_TOOLTIP_TAB_MEDIA_STATE_RECORDING));
200       break;
201     case TAB_MEDIA_STATE_CAPTURING:
202       result.append(
203           l10n_util::GetStringUTF16(IDS_TOOLTIP_TAB_MEDIA_STATE_CAPTURING));
204       break;
205     case TAB_MEDIA_STATE_NONE:
206       NOTREACHED();
207       break;
208   }
209   return result;
210 }
211 
212 }  // namespace chrome
213