• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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/chromeos/status/network_menu_button.h"
6 
7 #include <algorithm>
8 #include <limits>
9 
10 #include "base/logging.h"
11 #include "base/message_loop.h"
12 #include "base/string_util.h"
13 #include "base/stringprintf.h"
14 #include "base/utf_string_conversions.h"
15 #include "chrome/browser/browser_process.h"
16 #include "chrome/browser/chromeos/cros/cros_library.h"
17 #include "chrome/browser/chromeos/login/helper.h"
18 #include "chrome/browser/chromeos/login/user_manager.h"
19 #include "chrome/browser/chromeos/options/network_config_view.h"
20 #include "chrome/browser/chromeos/sim_dialog_delegate.h"
21 #include "chrome/browser/chromeos/status/status_area_host.h"
22 #include "chrome/browser/prefs/pref_service.h"
23 #include "chrome/browser/profiles/profile.h"
24 #include "chrome/browser/ui/browser.h"
25 #include "chrome/browser/ui/browser_list.h"
26 #include "chrome/common/pref_names.h"
27 #include "grit/generated_resources.h"
28 #include "grit/theme_resources.h"
29 #include "ui/base/l10n/l10n_util.h"
30 #include "ui/base/resource/resource_bundle.h"
31 #include "ui/gfx/canvas_skia.h"
32 #include "views/window/window.h"
33 
34 namespace {
35 
36 // Time in milliseconds to delay showing of promo
37 // notification when Chrome window is not on screen.
38 const int kPromoShowDelayMs = 10000;
39 
40 const int kNotificationCountPrefDefault = -1;
41 
GetBooleanPref(const char * pref_name)42 bool GetBooleanPref(const char* pref_name) {
43   Browser* browser = BrowserList::GetLastActive();
44   // Default to safe value which is false (not to show bubble).
45   if (!browser || !browser->profile())
46     return false;
47 
48   PrefService* prefs = browser->profile()->GetPrefs();
49   return prefs->GetBoolean(pref_name);
50 }
51 
GetIntegerPref(const char * pref_name)52 int GetIntegerPref(const char* pref_name) {
53   Browser* browser = BrowserList::GetLastActive();
54   // Default to "safe" value.
55   if (!browser || !browser->profile())
56     return kNotificationCountPrefDefault;
57 
58   PrefService* prefs = browser->profile()->GetPrefs();
59   return prefs->GetInteger(pref_name);
60 }
61 
SetBooleanPref(const char * pref_name,bool value)62 void SetBooleanPref(const char* pref_name, bool value) {
63   Browser* browser = BrowserList::GetLastActive();
64   if (!browser || !browser->profile())
65     return;
66 
67   PrefService* prefs = browser->profile()->GetPrefs();
68   prefs->SetBoolean(pref_name, value);
69 }
70 
SetIntegerPref(const char * pref_name,int value)71 void SetIntegerPref(const char* pref_name, int value) {
72   Browser* browser = BrowserList::GetLastActive();
73   if (!browser || !browser->profile())
74     return;
75 
76   PrefService* prefs = browser->profile()->GetPrefs();
77   prefs->SetInteger(pref_name, value);
78 }
79 
80 // Returns prefs::kShow3gPromoNotification or false
81 // if there's no active browser.
ShouldShow3gPromoNotification()82 bool ShouldShow3gPromoNotification() {
83   return GetBooleanPref(prefs::kShow3gPromoNotification);
84 }
85 
SetShow3gPromoNotification(bool value)86 void SetShow3gPromoNotification(bool value) {
87   SetBooleanPref(prefs::kShow3gPromoNotification, value);
88 }
89 // Returns prefs::kCarrierDealPromoShown which is number of times
90 // carrier deal notification has been shown to user or -1
91 // if there's no active browser.
GetCarrierDealPromoShown()92 int GetCarrierDealPromoShown() {
93   return GetIntegerPref(prefs::kCarrierDealPromoShown);
94 }
95 
SetCarrierDealPromoShown(int value)96 void SetCarrierDealPromoShown(int value) {
97   SetIntegerPref(prefs::kCarrierDealPromoShown, value);
98 }
99 
100 }  // namespace
101 
102 namespace chromeos {
103 
104 ////////////////////////////////////////////////////////////////////////////////
105 // NetworkMenuButton
106 
107 // static
108 const int NetworkMenuButton::kThrobDuration = 750;
109 
NetworkMenuButton(StatusAreaHost * host)110 NetworkMenuButton::NetworkMenuButton(StatusAreaHost* host)
111     : StatusAreaButton(host, this),
112       NetworkMenu(),
113       icon_(NULL),
114       right_badge_(NULL),
115       left_badge_(NULL),
116       mobile_data_bubble_(NULL),
117       check_for_promo_(true),
118       was_sim_locked_(false),
119       ALLOW_THIS_IN_INITIALIZER_LIST(animation_connecting_(this)),
120       ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
121   animation_connecting_.SetThrobDuration(kThrobDuration);
122   animation_connecting_.SetTweenType(ui::Tween::LINEAR);
123   NetworkLibrary* network_library = CrosLibrary::Get()->GetNetworkLibrary();
124   OnNetworkManagerChanged(network_library);
125   network_library->AddNetworkManagerObserver(this);
126   network_library->AddCellularDataPlanObserver(this);
127   const NetworkDevice* cellular = network_library->FindCellularDevice();
128   if (cellular) {
129     cellular_device_path_ = cellular->device_path();
130     was_sim_locked_ = cellular->is_sim_locked();
131     network_library->AddNetworkDeviceObserver(cellular_device_path_, this);
132   }
133 }
134 
~NetworkMenuButton()135 NetworkMenuButton::~NetworkMenuButton() {
136   NetworkLibrary* netlib = CrosLibrary::Get()->GetNetworkLibrary();
137   netlib->RemoveNetworkManagerObserver(this);
138   netlib->RemoveObserverForAllNetworks(this);
139   netlib->RemoveCellularDataPlanObserver(this);
140   if (!cellular_device_path_.empty())
141     netlib->RemoveNetworkDeviceObserver(cellular_device_path_, this);
142   if (mobile_data_bubble_)
143     mobile_data_bubble_->Close();
144 }
145 
146 ////////////////////////////////////////////////////////////////////////////////
147 // NetworkMenuButton, ui::AnimationDelegate implementation:
148 
AnimationProgressed(const ui::Animation * animation)149 void NetworkMenuButton::AnimationProgressed(const ui::Animation* animation) {
150   if (animation == &animation_connecting_) {
151     SetIconOnly(IconForNetworkConnecting(
152         animation_connecting_.GetCurrentValue(), false));
153     // No need to set the badge here, because it should already be set.
154     SchedulePaint();
155   } else {
156     MenuButton::AnimationProgressed(animation);
157   }
158 }
159 
160 ////////////////////////////////////////////////////////////////////////////////
161 // NetworkLibrary::NetworkDeviceObserver implementation:
162 
OnNetworkDeviceChanged(NetworkLibrary * cros,const NetworkDevice * device)163 void NetworkMenuButton::OnNetworkDeviceChanged(NetworkLibrary* cros,
164                                                const NetworkDevice* device) {
165   // Device status, such as SIMLock may have changed.
166   OnNetworkChanged(cros, cros->active_network());
167   const NetworkDevice* cellular = cros->FindCellularDevice();
168   if (cellular) {
169     // We make an assumption (which is valid for now) that the SIM
170     // unlock dialog is put up only when the user is trying to enable
171     // mobile data. So if the SIM is now unlocked, initiate the
172     // enable operation that the user originally requested.
173     if (was_sim_locked_ && !cellular->is_sim_locked() &&
174         !cros->cellular_enabled()) {
175       cros->EnableCellularNetworkDevice(true);
176     }
177     was_sim_locked_ = cellular->is_sim_locked();
178   }
179 }
180 
181 ////////////////////////////////////////////////////////////////////////////////
182 // NetworkMenuButton, NetworkLibrary::NetworkManagerObserver implementation:
183 
OnNetworkManagerChanged(NetworkLibrary * cros)184 void NetworkMenuButton::OnNetworkManagerChanged(NetworkLibrary* cros) {
185   OnNetworkChanged(cros, cros->active_network());
186   ShowOptionalMobileDataPromoNotification(cros);
187 }
188 
189 ////////////////////////////////////////////////////////////////////////////////
190 // NetworkMenuButton, NetworkLibrary::NetworkObserver implementation:
OnNetworkChanged(NetworkLibrary * cros,const Network * network)191 void NetworkMenuButton::OnNetworkChanged(NetworkLibrary* cros,
192                                          const Network* network) {
193   // This gets called on initialization, so any changes should be reflected
194   // in CrosMock::SetNetworkLibraryStatusAreaExpectations().
195   SetNetworkIcon(cros, network);
196   RefreshNetworkObserver(cros);
197   RefreshNetworkDeviceObserver(cros);
198   SchedulePaint();
199   UpdateMenu();
200 }
201 
OnCellularDataPlanChanged(NetworkLibrary * cros)202 void NetworkMenuButton::OnCellularDataPlanChanged(NetworkLibrary* cros) {
203   // Call OnNetworkManagerChanged which will update the icon.
204   OnNetworkManagerChanged(cros);
205 }
206 
207 ////////////////////////////////////////////////////////////////////////////////
208 // NetworkMenuButton, NetworkMenu implementation:
209 
IsBrowserMode() const210 bool NetworkMenuButton::IsBrowserMode() const {
211   return host_->GetScreenMode() == StatusAreaHost::kBrowserMode;
212 }
213 
GetNativeWindow() const214 gfx::NativeWindow NetworkMenuButton::GetNativeWindow() const {
215   return host_->GetNativeWindow();
216 }
217 
OpenButtonOptions()218 void NetworkMenuButton::OpenButtonOptions() {
219   host_->OpenButtonOptions(this);
220 }
221 
ShouldOpenButtonOptions() const222 bool NetworkMenuButton::ShouldOpenButtonOptions() const {
223   return host_->ShouldOpenButtonOptions(this);
224 }
225 
226 ////////////////////////////////////////////////////////////////////////////////
227 // NetworkMenuButton, views::View implementation:
228 
OnLocaleChanged()229 void NetworkMenuButton::OnLocaleChanged() {
230   NetworkLibrary* lib = CrosLibrary::Get()->GetNetworkLibrary();
231   SetNetworkIcon(lib, lib->active_network());
232 }
233 
234 ////////////////////////////////////////////////////////////////////////////////
235 // MessageBubbleDelegate implementation:
236 
OnHelpLinkActivated()237 void NetworkMenuButton::OnHelpLinkActivated() {
238   // mobile_data_bubble_ will be set to NULL in callback.
239   if (mobile_data_bubble_)
240     mobile_data_bubble_->Close();
241   if (!deal_url_.empty()) {
242     Browser* browser = BrowserList::GetLastActive();
243     if (!browser)
244       return;
245     browser->ShowSingletonTab(GURL(deal_url_));
246     deal_url_.clear();
247   } else {
248     const Network* cellular =
249         CrosLibrary::Get()->GetNetworkLibrary()->cellular_network();
250     if (!cellular)
251       return;
252     ShowTabbedNetworkSettings(cellular);
253   }
254 }
255 
256 ////////////////////////////////////////////////////////////////////////////////
257 // NetworkMenuButton, private methods
258 
259 const ServicesCustomizationDocument::CarrierDeal*
GetCarrierDeal(NetworkLibrary * cros)260 NetworkMenuButton::GetCarrierDeal(
261     NetworkLibrary* cros) {
262   std::string carrier_id = cros->GetCellularHomeCarrierId();
263   if (carrier_id.empty()) {
264     LOG(ERROR) << "Empty carrier ID with a cellular connected.";
265     return NULL;
266   }
267 
268   ServicesCustomizationDocument* customization =
269       ServicesCustomizationDocument::GetInstance();
270   if (!customization->IsReady())
271     return NULL;
272 
273   const ServicesCustomizationDocument::CarrierDeal* deal =
274       customization->GetCarrierDeal(carrier_id, true);
275   if (deal) {
276     // Check deal for validity.
277     int carrier_deal_promo_pref = GetCarrierDealPromoShown();
278     if (carrier_deal_promo_pref >= deal->notification_count)
279       return NULL;
280     const std::string locale = g_browser_process->GetApplicationLocale();
281     std::string deal_text = deal->GetLocalizedString(locale,
282                                                      "notification_text");
283     if (deal_text.empty())
284       return NULL;
285   }
286   return deal;
287 }
288 
SetIconAndBadges(const SkBitmap * icon,const SkBitmap * right_badge,const SkBitmap * left_badge)289 void NetworkMenuButton::SetIconAndBadges(const SkBitmap* icon,
290                                          const SkBitmap* right_badge,
291                                          const SkBitmap* left_badge) {
292   icon_ = icon;
293   right_badge_ = right_badge;
294   left_badge_ = left_badge;
295   SetIcon(IconForDisplay(icon_, right_badge_, NULL /*no top_left_icon*/,
296                          left_badge_));
297 }
298 
SetIconOnly(const SkBitmap * icon)299 void NetworkMenuButton::SetIconOnly(const SkBitmap* icon) {
300   icon_ = icon;
301   SetIcon(IconForDisplay(icon_, right_badge_, NULL /*no top_left_icon*/,
302                          left_badge_));
303 }
304 
SetBadgesOnly(const SkBitmap * right_badge,const SkBitmap * left_badge)305 void NetworkMenuButton::SetBadgesOnly(const SkBitmap* right_badge,
306                                       const SkBitmap* left_badge) {
307   right_badge_ = right_badge;
308   left_badge_ = left_badge;
309   SetIcon(IconForDisplay(icon_, right_badge_, NULL /*no top_left_icon*/,
310                          left_badge_));
311 }
312 
SetNetworkIcon(NetworkLibrary * cros,const Network * network)313 void NetworkMenuButton::SetNetworkIcon(NetworkLibrary* cros,
314                                        const Network* network) {
315   ResourceBundle& rb = ResourceBundle::GetSharedInstance();
316 
317   if (!cros || !CrosLibrary::Get()->EnsureLoaded()) {
318     SetIconAndBadges(rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_BARS0),
319                      rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_WARNING),
320                      NULL);
321     SetTooltipText(UTF16ToWide(l10n_util::GetStringUTF16(
322         IDS_STATUSBAR_NETWORK_NO_NETWORK_TOOLTIP)));
323     return;
324   }
325 
326   if (!cros->Connected() && !cros->Connecting()) {
327     animation_connecting_.Stop();
328     SetIconAndBadges(rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_BARS0),
329                      rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_DISCONNECTED),
330                      NULL);
331     SetTooltipText(UTF16ToWide(l10n_util::GetStringUTF16(
332         IDS_STATUSBAR_NETWORK_NO_NETWORK_TOOLTIP)));
333     return;
334   }
335 
336   if (cros->wifi_connecting() || cros->cellular_connecting()) {
337     // Start the connecting animation if not running.
338     if (!animation_connecting_.is_animating()) {
339       animation_connecting_.Reset();
340       animation_connecting_.StartThrobbing(-1);
341       SetIconOnly(IconForNetworkConnecting(0, false));
342     }
343     const WirelessNetwork* wireless = NULL;
344     if (cros->wifi_connecting()) {
345       wireless = cros->wifi_network();
346       SetBadgesOnly(NULL, NULL);
347     } else {  // cellular_connecting
348       wireless = cros->cellular_network();
349       SetBadgesOnly(BadgeForNetworkTechnology(cros->cellular_network()), NULL);
350     }
351     SetTooltipText(UTF16ToWide(l10n_util::GetStringFUTF16(
352         wireless->configuring() ? IDS_STATUSBAR_NETWORK_CONFIGURING_TOOLTIP
353                                 : IDS_STATUSBAR_NETWORK_CONNECTING_TOOLTIP,
354         UTF8ToUTF16(wireless->name()))));
355   } else {
356     // Stop connecting animation since we are not connecting.
357     animation_connecting_.Stop();
358     // Only set the icon, if it is an active network that changed.
359     if (network && network->is_active()) {
360       const SkBitmap* right_badge(NULL);
361       const SkBitmap* left_badge(NULL);
362       if (cros->virtual_network())
363         left_badge = rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_SECURE);
364       if (network->type() == TYPE_ETHERNET) {
365         SetIconAndBadges(rb.GetBitmapNamed(IDR_STATUSBAR_WIRED),
366                          right_badge, left_badge);
367         SetTooltipText(
368             UTF16ToWide(l10n_util::GetStringFUTF16(
369                 IDS_STATUSBAR_NETWORK_CONNECTED_TOOLTIP,
370                 l10n_util::GetStringUTF16(
371                     IDS_STATUSBAR_NETWORK_DEVICE_ETHERNET))));
372       } else if (network->type() == TYPE_WIFI) {
373         const WifiNetwork* wifi = static_cast<const WifiNetwork*>(network);
374         SetIconAndBadges(IconForNetworkStrength(wifi, false),
375                          right_badge, left_badge);
376         SetTooltipText(UTF16ToWide(l10n_util::GetStringFUTF16(
377             IDS_STATUSBAR_NETWORK_CONNECTED_TOOLTIP,
378             UTF8ToUTF16(wifi->name()))));
379       } else if (network->type() == TYPE_CELLULAR) {
380         const CellularNetwork* cellular =
381             static_cast<const CellularNetwork*>(network);
382         right_badge = BadgeForNetworkTechnology(cellular);
383         SetIconAndBadges(IconForNetworkStrength(cellular, false),
384                          right_badge, left_badge);
385         SetTooltipText(UTF16ToWide(l10n_util::GetStringFUTF16(
386             IDS_STATUSBAR_NETWORK_CONNECTED_TOOLTIP,
387             UTF8ToUTF16(cellular->name()))));
388       }
389     }
390   }
391 }
392 
RefreshNetworkObserver(NetworkLibrary * cros)393 void NetworkMenuButton::RefreshNetworkObserver(NetworkLibrary* cros) {
394   const Network* network = cros->active_network();
395   std::string new_network = network ? network->service_path() : std::string();
396   if (active_network_ != new_network) {
397     if (!active_network_.empty()) {
398       cros->RemoveNetworkObserver(active_network_, this);
399     }
400     if (!new_network.empty()) {
401       cros->AddNetworkObserver(new_network, this);
402     }
403     active_network_ = new_network;
404   }
405 }
406 
RefreshNetworkDeviceObserver(NetworkLibrary * cros)407 void NetworkMenuButton::RefreshNetworkDeviceObserver(NetworkLibrary* cros) {
408   const NetworkDevice* cellular = cros->FindCellularDevice();
409   std::string new_cellular_device_path = cellular ?
410       cellular->device_path() : std::string();
411   if (cellular_device_path_ != new_cellular_device_path) {
412     if (!cellular_device_path_.empty()) {
413       cros->RemoveNetworkDeviceObserver(cellular_device_path_, this);
414     }
415     if (!new_cellular_device_path.empty()) {
416       was_sim_locked_ = cellular->is_sim_locked();
417       cros->AddNetworkDeviceObserver(new_cellular_device_path, this);
418     }
419     cellular_device_path_ = new_cellular_device_path;
420   }
421 }
422 
ShowOptionalMobileDataPromoNotification(NetworkLibrary * cros)423 void NetworkMenuButton::ShowOptionalMobileDataPromoNotification(
424     NetworkLibrary* cros) {
425   // Display one-time notification for non-Guest users on first use
426   // of Mobile Data connection or if there's a carrier deal defined
427   // show that even if user has already seen generic promo.
428   if (IsBrowserMode() && !UserManager::Get()->IsLoggedInAsGuest() &&
429       check_for_promo_ && BrowserList::GetLastActive() &&
430       cros->cellular_connected() && !cros->ethernet_connected() &&
431       !cros->wifi_connected()) {
432     const ServicesCustomizationDocument::CarrierDeal* deal =
433         GetCarrierDeal(cros);
434     std::string deal_text;
435     int carrier_deal_promo_pref = -1;
436     if (deal) {
437       carrier_deal_promo_pref = GetCarrierDealPromoShown();
438       const std::string locale = g_browser_process->GetApplicationLocale();
439       deal_text = deal->GetLocalizedString(locale, "notification_text");
440       deal_url_ = deal->top_up_url;
441     } else if (!ShouldShow3gPromoNotification()) {
442       check_for_promo_ = false;
443       return;
444     }
445 
446     gfx::Rect button_bounds = GetScreenBounds();
447     // StatusArea button Y position is usually -1, fix it so that
448     // Contains() method for screen bounds works correctly.
449     button_bounds.set_y(button_bounds.y() + 1);
450     gfx::Rect screen_bounds(chromeos::CalculateScreenBounds(gfx::Size()));
451 
452     // Chrome window is initialized in visible state off screen and then is
453     // moved into visible screen area. Make sure that we're on screen
454     // so that bubble is shown correctly.
455     if (!screen_bounds.Contains(button_bounds)) {
456       // If we're not on screen yet, delay notification display.
457       // It may be shown earlier, on next NetworkLibrary callback processing.
458       if (method_factory_.empty()) {
459         MessageLoop::current()->PostDelayedTask(FROM_HERE,
460             method_factory_.NewRunnableMethod(
461                 &NetworkMenuButton::ShowOptionalMobileDataPromoNotification,
462                 cros),
463             kPromoShowDelayMs);
464       }
465       return;
466     }
467 
468     // Add deal text if it's defined.
469     std::wstring notification_text;
470     std::wstring default_text =
471         UTF16ToWide(l10n_util::GetStringUTF16(IDS_3G_NOTIFICATION_MESSAGE));
472     if (!deal_text.empty()) {
473       notification_text = StringPrintf(L"%ls\n\n%ls",
474                                        UTF8ToWide(deal_text).c_str(),
475                                        default_text.c_str());
476     } else {
477       notification_text = default_text;
478     }
479 
480     // Use deal URL if it's defined or general "Network Settings" URL.
481     int link_message_id;
482     if (deal_url_.empty())
483       link_message_id = IDS_OFFLINE_NETWORK_SETTINGS;
484     else
485       link_message_id = IDS_STATUSBAR_NETWORK_VIEW_ACCOUNT;
486 
487     mobile_data_bubble_ = MessageBubble::Show(
488         GetWidget(),
489         button_bounds,
490         BubbleBorder::TOP_RIGHT ,
491         ResourceBundle::GetSharedInstance().GetBitmapNamed(IDR_NOTIFICATION_3G),
492         notification_text,
493         UTF16ToWide(l10n_util::GetStringUTF16(link_message_id)),
494         this);
495 
496     check_for_promo_ = false;
497     SetShow3gPromoNotification(false);
498     if (carrier_deal_promo_pref != kNotificationCountPrefDefault)
499       SetCarrierDealPromoShown(carrier_deal_promo_pref + 1);
500   }
501 }
502 
503 }  // namespace chromeos
504