Home | History | Annotate | Download | only in status
      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 
     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 
     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 
     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 
     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.
     82 bool ShouldShow3gPromoNotification() {
     83   return GetBooleanPref(prefs::kShow3gPromoNotification);
     84 }
     85 
     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.
     92 int GetCarrierDealPromoShown() {
     93   return GetIntegerPref(prefs::kCarrierDealPromoShown);
     94 }
     95 
     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 
    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 
    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 
    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 
    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 
    184 void NetworkMenuButton::OnNetworkManagerChanged(NetworkLibrary* cros) {
    185   OnNetworkChanged(cros, cros->active_network());
    186   ShowOptionalMobileDataPromoNotification(cros);
    187 }
    188 
    189 ////////////////////////////////////////////////////////////////////////////////
    190 // NetworkMenuButton, NetworkLibrary::NetworkObserver implementation:
    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 
    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 
    210 bool NetworkMenuButton::IsBrowserMode() const {
    211   return host_->GetScreenMode() == StatusAreaHost::kBrowserMode;
    212 }
    213 
    214 gfx::NativeWindow NetworkMenuButton::GetNativeWindow() const {
    215   return host_->GetNativeWindow();
    216 }
    217 
    218 void NetworkMenuButton::OpenButtonOptions() {
    219   host_->OpenButtonOptions(this);
    220 }
    221 
    222 bool NetworkMenuButton::ShouldOpenButtonOptions() const {
    223   return host_->ShouldOpenButtonOptions(this);
    224 }
    225 
    226 ////////////////////////////////////////////////////////////////////////////////
    227 // NetworkMenuButton, views::View implementation:
    228 
    229 void NetworkMenuButton::OnLocaleChanged() {
    230   NetworkLibrary* lib = CrosLibrary::Get()->GetNetworkLibrary();
    231   SetNetworkIcon(lib, lib->active_network());
    232 }
    233 
    234 ////////////////////////////////////////////////////////////////////////////////
    235 // MessageBubbleDelegate implementation:
    236 
    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*
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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