network_message_observer.cc revision 72a454cd3513ac24fbdd0e0cb9ad70b86a99b801
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/network_message_observer.h"
6
7#include "base/callback.h"
8#include "base/stl_util-inl.h"
9#include "base/string_number_conversions.h"
10#include "base/utf_string_conversions.h"
11#include "chrome/browser/browser_list.h"
12#include "chrome/browser/chromeos/cros/cros_library.h"
13#include "chrome/browser/chromeos/cros/network_library.h"
14#include "chrome/browser/chromeos/notifications/balloon_view_host.h"
15#include "chrome/browser/prefs/pref_service.h"
16#include "chrome/browser/profiles/profile.h"
17#include "chrome/browser/ui/browser.h"
18#include "chrome/common/pref_names.h"
19#include "chrome/common/time_format.h"
20#include "grit/generated_resources.h"
21#include "grit/theme_resources.h"
22#include "ui/base/l10n/l10n_util.h"
23
24namespace {
25
26// Returns prefs::kShowPlanNotifications in the profile of the last active
27// browser. If there is no active browser, returns true.
28bool ShouldShowMobilePlanNotifications() {
29  Browser* browser = BrowserList::GetLastActive();
30  if (!browser || !browser->profile())
31    return true;
32
33  PrefService* prefs = browser->profile()->GetPrefs();
34  return prefs->GetBoolean(prefs::kShowPlanNotifications);
35}
36
37}  // namespace
38
39namespace chromeos {
40
41NetworkMessageObserver::NetworkMessageObserver(Profile* profile)
42    : notification_connection_error_(profile, "network_connection.chromeos",
43          IDR_NOTIFICATION_NETWORK_FAILED,
44          l10n_util::GetStringUTF16(IDS_NETWORK_CONNECTION_ERROR_TITLE)),
45      notification_low_data_(profile, "network_low_data.chromeos",
46          IDR_NOTIFICATION_BARS_CRITICAL,
47          l10n_util::GetStringUTF16(IDS_NETWORK_LOW_DATA_TITLE)),
48      notification_no_data_(profile, "network_no_data.chromeos",
49          IDR_NOTIFICATION_BARS_EMPTY,
50          l10n_util::GetStringUTF16(IDS_NETWORK_OUT_OF_DATA_TITLE)) {
51  NetworkLibrary* netlib = CrosLibrary::Get()->GetNetworkLibrary();
52  OnNetworkManagerChanged(netlib);
53  // Note that this gets added as a NetworkManagerObserver and a
54  // CellularDataPlanObserver in browser_init.cc
55}
56
57NetworkMessageObserver::~NetworkMessageObserver() {
58  NetworkLibrary* netlib = CrosLibrary::Get()->GetNetworkLibrary();
59  netlib->RemoveNetworkManagerObserver(this);
60  netlib->RemoveCellularDataPlanObserver(this);
61  notification_connection_error_.Hide();
62  notification_low_data_.Hide();
63  notification_no_data_.Hide();
64  STLDeleteValues(&cellular_networks_);
65  STLDeleteValues(&wifi_networks_);
66}
67
68// static
69bool NetworkMessageObserver::IsApplicableBackupPlan(
70    const CellularDataPlan* plan, const CellularDataPlan* other_plan) {
71  // By applicable plan, we mean that the other plan has data AND the timeframe
72  // will apply: (unlimited OR used bytes < max bytes) AND
73  //   ((start time - 1 sec) <= end time of currently active plan).
74  // In other words, there is data available and there is no gap of more than a
75  // second in time between the old plan and the new plan.
76  bool has_data = other_plan->plan_type == CELLULAR_DATA_PLAN_UNLIMITED ||
77      other_plan->remaining_data() > 0;
78  bool will_apply =
79      (other_plan->plan_start_time - plan->plan_end_time).InSeconds() <= 1;
80  return has_data && will_apply;
81}
82
83void NetworkMessageObserver::OpenMobileSetupPage(const ListValue* args) {
84  Browser* browser = BrowserList::GetLastActive();
85  if (browser)
86    browser->OpenMobilePlanTabAndActivate();
87}
88
89void NetworkMessageObserver::OpenMoreInfoPage(const ListValue* args) {
90  Browser* browser = BrowserList::GetLastActive();
91  if (!browser)
92    return;
93  chromeos::NetworkLibrary* lib =
94      chromeos::CrosLibrary::Get()->GetNetworkLibrary();
95  const chromeos::CellularNetwork* cellular = lib->cellular_network();
96  if (!cellular)
97    return;
98  browser->ShowSingletonTab(GURL(cellular->payment_url()), false);
99}
100
101void NetworkMessageObserver::HideDataNotifications() {
102  notification_low_data_.Hide();
103  notification_no_data_.Hide();
104}
105
106void NetworkMessageObserver::InitNewPlan(const CellularDataPlan* plan) {
107  HideDataNotifications();
108  if (plan->plan_type == CELLULAR_DATA_PLAN_UNLIMITED) {
109    notification_no_data_.set_title(
110        l10n_util::GetStringFUTF16(IDS_NETWORK_DATA_EXPIRED_TITLE,
111                                   ASCIIToUTF16(plan->plan_name)));
112    notification_low_data_.set_title(
113        l10n_util::GetStringFUTF16(IDS_NETWORK_NEARING_EXPIRATION_TITLE,
114                                   ASCIIToUTF16(plan->plan_name)));
115  } else {
116    notification_no_data_.set_title(
117        l10n_util::GetStringFUTF16(IDS_NETWORK_OUT_OF_DATA_TITLE,
118                                   ASCIIToUTF16(plan->plan_name)));
119    notification_low_data_.set_title(
120        l10n_util::GetStringFUTF16(IDS_NETWORK_LOW_DATA_TITLE,
121                                   ASCIIToUTF16(plan->plan_name)));
122  }
123}
124
125void NetworkMessageObserver::ShowNoDataNotification(
126    const CellularDataPlan* plan) {
127  notification_low_data_.Hide();
128  string16 message =
129      plan->plan_type == CELLULAR_DATA_PLAN_UNLIMITED ?
130          TimeFormat::TimeRemaining(base::TimeDelta()) :
131          l10n_util::GetStringFUTF16(IDS_NETWORK_DATA_REMAINING_MESSAGE,
132                                     ASCIIToUTF16("0"));
133  notification_no_data_.Show(message,
134      l10n_util::GetStringUTF16(IDS_NETWORK_PURCHASE_MORE_MESSAGE),
135      NewCallback(this, &NetworkMessageObserver::OpenMobileSetupPage),
136      false, false);
137}
138
139void NetworkMessageObserver::ShowLowDataNotification(
140    const CellularDataPlan* plan) {
141  notification_no_data_.Hide();
142  string16 message =
143      plan->plan_type == CELLULAR_DATA_PLAN_UNLIMITED ?
144          plan->GetPlanExpiration() :
145          l10n_util::GetStringFUTF16(IDS_NETWORK_DATA_REMAINING_MESSAGE,
146               UTF8ToUTF16(base::Int64ToString(plan->remaining_mbytes())));
147  notification_low_data_.Show(message,
148      l10n_util::GetStringUTF16(IDS_NETWORK_MORE_INFO_MESSAGE),
149      NewCallback(this, &NetworkMessageObserver::OpenMoreInfoPage),
150      false, false);
151}
152
153void NetworkMessageObserver::OnNetworkManagerChanged(NetworkLibrary* obj) {
154  const WifiNetworkVector& wifi_networks = obj->wifi_networks();
155  const CellularNetworkVector& cellular_networks = obj->cellular_networks();
156
157  std::string new_failed_network;
158  // Check to see if we have any newly failed wifi network.
159  for (WifiNetworkVector::const_iterator it = wifi_networks.begin();
160       it < wifi_networks.end(); it++) {
161    const WifiNetwork* wifi = *it;
162    if (wifi->failed()) {
163      ServicePathWifiMap::iterator iter =
164          wifi_networks_.find(wifi->service_path());
165      // If the network did not previously exist, then don't do anything.
166      // For example, if the user travels to a location and finds a service
167      // that has previously failed, we don't want to show a notification.
168      if (iter == wifi_networks_.end())
169        continue;
170
171      const WifiNetwork* wifi_old = iter->second;
172      // If network connection failed, display a notification.
173      // We only do this if we were trying to make a new connection.
174      // So if a previously connected network got disconnected for any reason,
175      // we don't display notification.
176      if (wifi_old->connecting()) {
177        new_failed_network = wifi->name();
178        // Like above, there should only be one newly failed network.
179        break;
180      }
181    }
182
183    // If we find any network connecting, we hide the error notification.
184    if (wifi->connecting()) {
185      notification_connection_error_.Hide();
186    }
187  }
188
189  // Refresh stored networks.
190  STLDeleteValues(&wifi_networks_);
191  wifi_networks_.clear();
192  for (WifiNetworkVector::const_iterator it = wifi_networks.begin();
193       it < wifi_networks.end(); it++) {
194    const WifiNetwork* wifi = *it;
195    wifi_networks_[wifi->service_path()] = new WifiNetwork(*wifi);
196  }
197
198  STLDeleteValues(&cellular_networks_);
199  cellular_networks_.clear();
200  for (CellularNetworkVector::const_iterator it = cellular_networks.begin();
201       it < cellular_networks.end(); it++) {
202    const CellularNetwork* cellular = *it;
203    cellular_networks_[cellular->service_path()] =
204        new CellularNetwork(*cellular);
205  }
206
207  // Show connection error notification if necessary.
208  if (!new_failed_network.empty()) {
209    // Hide if already shown to force show it in case user has closed it.
210    if (notification_connection_error_.visible())
211      notification_connection_error_.Hide();
212    notification_connection_error_.Show(l10n_util::GetStringFUTF16(
213        IDS_NETWORK_CONNECTION_ERROR_MESSAGE,
214        ASCIIToUTF16(new_failed_network)), false, false);
215  }
216}
217
218void NetworkMessageObserver::OnCellularDataPlanChanged(NetworkLibrary* obj) {
219  if (!ShouldShowMobilePlanNotifications()) {
220    HideDataNotifications();
221    return;
222  }
223
224  const CellularNetwork* cellular = obj->cellular_network();
225  if (!cellular)
226    return;
227
228  // If no plans available, check to see if we need a new plan.
229  if (cellular->GetDataPlans().size() == 0) {
230    HideDataNotifications();
231    if (cellular->needs_new_plan()) {
232      notification_no_data_.set_title(
233          l10n_util::GetStringFUTF16(IDS_NETWORK_NO_DATA_PLAN_TITLE,
234                                     UTF8ToUTF16(cellular->service_name())));
235      notification_no_data_.Show(
236          l10n_util::GetStringFUTF16(
237              IDS_NETWORK_NO_DATA_PLAN_MESSAGE,
238              UTF8ToUTF16(cellular->service_name())),
239          l10n_util::GetStringUTF16(IDS_NETWORK_PURCHASE_MORE_MESSAGE),
240          NewCallback(this, &NetworkMessageObserver::OpenMobileSetupPage),
241          false, false);
242    }
243    return;
244  }
245
246  const CellularDataPlanVector& plans = cellular->GetDataPlans();
247  CellularDataPlanVector::const_iterator iter = plans.begin();
248  const CellularDataPlan* current_plan = *iter;
249
250  // If current plan is not the last plan (there is another backup plan),
251  // then we do not show notifications for this plan.
252  // For example, if there is another data plan available when this runs out.
253  for (++iter; iter != plans.end(); ++iter) {
254    if (IsApplicableBackupPlan(current_plan, *iter)) {
255      HideDataNotifications();
256      return;
257    }
258  }
259
260  // If connected cellular network changed, or data plan is different, then
261  // it's a new network. Then hide all previous notifications.
262  bool new_plan = cellular->service_path() != cellular_service_path_ ||
263      current_plan->GetUniqueIdentifier() != cellular_data_plan_unique_id_;
264
265  if (new_plan) {
266    InitNewPlan(current_plan);
267  }
268
269  if (cellular->GetDataLeft() == CellularNetwork::DATA_NONE) {
270    ShowNoDataNotification(current_plan);
271  } else if (cellular->GetDataLeft() == CellularNetwork::DATA_VERY_LOW) {
272    ShowLowDataNotification(current_plan);
273  } else {
274    // Got data, so hide notifications.
275    HideDataNotifications();
276  }
277
278  cellular_service_path_ = cellular->service_path();
279  cellular_data_plan_unique_id_ = current_plan->GetUniqueIdentifier();
280}
281
282}  // namespace chromeos
283