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/chromeos/network_message_observer.h"
8 #include "ash/shell_delegate.h"
9 #include "ash/system/chromeos/network/network_observer.h"
10 #include "ash/system/tray/system_tray.h"
11 #include "ash/system/tray/system_tray_notifier.h"
12 #include "base/bind.h"
13 #include "base/callback.h"
14 #include "base/stl_util.h"
15 #include "base/string_number_conversions.h"
16 #include "base/utf_string_conversions.h"
17 #include "chrome/browser/chromeos/cros/cros_library.h"
18 #include "chrome/browser/chromeos/cros/network_library.h"
19 #include "chrome/browser/chromeos/notifications/balloon_view_host_chromeos.h"
20 #include "chrome/browser/prefs/pref_service.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "chrome/browser/profiles/profile_manager.h"
23 #include "chrome/browser/ui/browser.h"
24 #include "chrome/browser/ui/browser_finder.h"
25 #include "chrome/browser/ui/host_desktop.h"
26 #include "chrome/browser/ui/singleton_tabs.h"
27 #include "chrome/common/pref_names.h"
28 #include "chrome/common/time_format.h"
29 #include "grit/generated_resources.h"
30 #include "ui/base/l10n/l10n_util.h"
34 // Returns prefs::kShowPlanNotifications in the profile of the last active
35 // browser. If there is no active browser, returns true.
36 bool ShouldShowMobilePlanNotifications() {
37 Profile
* profile
= ProfileManager::GetDefaultProfileOrOffTheRecord();
38 PrefService
* prefs
= profile
->GetPrefs();
39 return prefs
->GetBoolean(prefs::kShowPlanNotifications
);
46 class NetworkMessageNotification
: public ash::NetworkTrayDelegate
{
48 NetworkMessageNotification(Profile
* profile
,
49 ash::NetworkObserver::MessageType error_type
)
50 : error_type_(error_type
) {
52 case ash::NetworkObserver::ERROR_CONNECT_FAILED
:
53 title_
= l10n_util::GetStringUTF16(IDS_NETWORK_CONNECTION_ERROR_TITLE
);
55 case ash::NetworkObserver::MESSAGE_DATA_LOW
:
56 title_
= l10n_util::GetStringUTF16(IDS_NETWORK_LOW_DATA_TITLE
);
58 case ash::NetworkObserver::MESSAGE_DATA_NONE
:
59 title_
= l10n_util::GetStringUTF16(IDS_NETWORK_OUT_OF_DATA_TITLE
);
61 case ash::NetworkObserver::MESSAGE_DATA_PROMO
:
67 // Overridden from ash::NetworkTrayDelegate:
68 virtual void NotificationLinkClicked(size_t index
) OVERRIDE
{
69 base::ListValue empty_value
;
70 if (!callback_
.is_null())
71 callback_
.Run(&empty_value
);
75 ash::Shell::GetInstance()->system_tray_notifier()->
76 NotifyClearNetworkMessage(error_type_
);
79 void SetTitle(const string16
& title
) {
83 void Show(const string16
& message
,
84 const string16
& link_text
,
85 const BalloonViewHost::MessageCallback
& callback
,
86 bool urgent
, bool sticky
) {
88 std::vector
<string16
> links
;
89 links
.push_back(link_text
);
90 ash::Shell::GetInstance()->system_tray_notifier()->NotifySetNetworkMessage(
91 this, error_type_
, title_
, message
, links
);
94 void ShowAlways(const string16
& message
,
95 const string16
& link_text
,
96 const BalloonViewHost::MessageCallback
& callback
,
97 bool urgent
, bool sticky
) {
98 Show(message
, link_text
, callback
, urgent
, sticky
);
103 ash::NetworkObserver::MessageType error_type_
;
104 BalloonViewHost::MessageCallback callback_
;
107 NetworkMessageObserver::NetworkMessageObserver(Profile
* profile
) {
108 notification_connection_error_
.reset(
109 new NetworkMessageNotification(
110 profile
, ash::NetworkObserver::ERROR_CONNECT_FAILED
));
111 notification_low_data_
.reset(
112 new NetworkMessageNotification(
114 ash::NetworkObserver::MESSAGE_DATA_LOW
));
115 notification_no_data_
.reset(
116 new NetworkMessageNotification(
118 ash::NetworkObserver::MESSAGE_DATA_NONE
));
119 NetworkLibrary
* netlib
= CrosLibrary::Get()->GetNetworkLibrary();
120 OnNetworkManagerChanged(netlib
);
121 // Note that this gets added as a NetworkManagerObserver,
122 // CellularDataPlanObserver, and UserActionObserver in
123 // startup_browser_creator.cc
126 NetworkMessageObserver::~NetworkMessageObserver() {
127 NetworkLibrary
* netlib
= CrosLibrary::Get()->GetNetworkLibrary();
128 netlib
->RemoveNetworkManagerObserver(this);
129 netlib
->RemoveCellularDataPlanObserver(this);
130 netlib
->RemoveUserActionObserver(this);
131 notification_connection_error_
->Hide();
132 notification_low_data_
->Hide();
133 notification_no_data_
->Hide();
137 bool NetworkMessageObserver::IsApplicableBackupPlan(
138 const CellularDataPlan
* plan
, const CellularDataPlan
* other_plan
) {
139 // By applicable plan, we mean that the other plan has data AND the timeframe
140 // will apply: (unlimited OR used bytes < max bytes) AND
141 // ((start time - 1 sec) <= end time of currently active plan).
142 // In other words, there is data available and there is no gap of more than a
143 // second in time between the old plan and the new plan.
144 bool has_data
= other_plan
->plan_type
== CELLULAR_DATA_PLAN_UNLIMITED
||
145 other_plan
->remaining_data() > 0;
147 (other_plan
->plan_start_time
- plan
->plan_end_time
).InSeconds() <= 1;
148 return has_data
&& will_apply
;
151 void NetworkMessageObserver::OpenMobileSetupPage(
152 const std::string
& service_path
, const ListValue
* args
) {
153 ash::Shell::GetInstance()->delegate()->OpenMobileSetup(service_path
);
156 void NetworkMessageObserver::OpenMoreInfoPage(const ListValue
* args
) {
157 Browser
* browser
= browser::FindOrCreateTabbedBrowser(
158 ProfileManager::GetDefaultProfileOrOffTheRecord(),
159 chrome::HOST_DESKTOP_TYPE_ASH
);
160 chromeos::NetworkLibrary
* lib
=
161 chromeos::CrosLibrary::Get()->GetNetworkLibrary();
162 const chromeos::CellularNetwork
* cellular
= lib
->cellular_network();
165 chrome::ShowSingletonTab(browser
, GURL(cellular
->payment_url()));
168 void NetworkMessageObserver::InitNewPlan(const CellularDataPlan
* plan
) {
169 notification_low_data_
->Hide();
170 notification_no_data_
->Hide();
171 if (plan
->plan_type
== CELLULAR_DATA_PLAN_UNLIMITED
) {
172 notification_no_data_
->SetTitle(
173 l10n_util::GetStringFUTF16(IDS_NETWORK_DATA_EXPIRED_TITLE
,
174 ASCIIToUTF16(plan
->plan_name
)));
175 notification_low_data_
->SetTitle(
176 l10n_util::GetStringFUTF16(IDS_NETWORK_NEARING_EXPIRATION_TITLE
,
177 ASCIIToUTF16(plan
->plan_name
)));
179 notification_no_data_
->SetTitle(
180 l10n_util::GetStringFUTF16(IDS_NETWORK_OUT_OF_DATA_TITLE
,
181 ASCIIToUTF16(plan
->plan_name
)));
182 notification_low_data_
->SetTitle(
183 l10n_util::GetStringFUTF16(IDS_NETWORK_LOW_DATA_TITLE
,
184 ASCIIToUTF16(plan
->plan_name
)));
188 void NetworkMessageObserver::ShowNeedsPlanNotification(
189 const CellularNetwork
* cellular
) {
190 notification_no_data_
->SetTitle(
191 l10n_util::GetStringFUTF16(IDS_NETWORK_NO_DATA_PLAN_TITLE
,
192 UTF8ToUTF16(cellular
->name())));
193 notification_no_data_
->Show(
194 l10n_util::GetStringFUTF16(
195 IDS_NETWORK_NO_DATA_PLAN_MESSAGE
,
196 UTF8ToUTF16(cellular
->name())),
197 l10n_util::GetStringUTF16(IDS_NETWORK_PURCHASE_MORE_MESSAGE
),
198 base::Bind(&NetworkMessageObserver::OpenMobileSetupPage
,
200 cellular
->service_path()),
204 void NetworkMessageObserver::ShowNoDataNotification(
205 const CellularNetwork
* cellular
,
206 CellularDataPlanType plan_type
) {
207 notification_low_data_
->Hide(); // Hide previous low data notification.
208 string16 message
= plan_type
== CELLULAR_DATA_PLAN_UNLIMITED
?
209 TimeFormat::TimeRemaining(base::TimeDelta()) :
210 l10n_util::GetStringFUTF16(IDS_NETWORK_DATA_REMAINING_MESSAGE
,
212 notification_no_data_
->Show(message
,
213 l10n_util::GetStringUTF16(IDS_NETWORK_PURCHASE_MORE_MESSAGE
),
214 base::Bind(&NetworkMessageObserver::OpenMobileSetupPage
,
216 cellular
->service_path()),
220 void NetworkMessageObserver::ShowLowDataNotification(
221 const CellularDataPlan
* plan
) {
223 if (plan
->plan_type
== CELLULAR_DATA_PLAN_UNLIMITED
) {
224 message
= plan
->GetPlanExpiration();
226 int64 remaining_mbytes
= plan
->remaining_data() / (1024 * 1024);
227 message
= l10n_util::GetStringFUTF16(IDS_NETWORK_DATA_REMAINING_MESSAGE
,
228 UTF8ToUTF16(base::Int64ToString(remaining_mbytes
)));
230 notification_low_data_
->Show(message
,
231 l10n_util::GetStringUTF16(IDS_NETWORK_MORE_INFO_MESSAGE
),
232 base::Bind(&NetworkMessageObserver::OpenMoreInfoPage
, AsWeakPtr()),
236 void NetworkMessageObserver::OnNetworkManagerChanged(NetworkLibrary
* cros
) {
237 const Network
* new_failed_network
= NULL
;
238 // Check to see if we have any newly failed networks.
239 for (WifiNetworkVector::const_iterator it
= cros
->wifi_networks().begin();
240 it
!= cros
->wifi_networks().end(); it
++) {
241 const WifiNetwork
* net
= *it
;
242 if (net
->notify_failure()) {
243 new_failed_network
= net
;
244 break; // There should only be one failed network.
248 if (!new_failed_network
) {
249 for (WimaxNetworkVector::const_iterator it
= cros
->wimax_networks().begin();
250 it
!= cros
->wimax_networks().end(); ++it
) {
251 const WimaxNetwork
* net
= *it
;
252 if (net
->notify_failure()) {
253 new_failed_network
= net
;
254 break; // There should only be one failed network.
259 if (!new_failed_network
) {
260 for (CellularNetworkVector::const_iterator it
=
261 cros
->cellular_networks().begin();
262 it
!= cros
->cellular_networks().end(); it
++) {
263 const CellularNetwork
* net
= *it
;
264 if (net
->notify_failure()) {
265 new_failed_network
= net
;
266 break; // There should only be one failed network.
271 if (!new_failed_network
) {
272 for (VirtualNetworkVector::const_iterator it
=
273 cros
->virtual_networks().begin();
274 it
!= cros
->virtual_networks().end(); it
++) {
275 const VirtualNetwork
* net
= *it
;
276 if (net
->notify_failure()) {
277 new_failed_network
= net
;
278 break; // There should only be one failed network.
283 // Show connection error notification if necessary.
284 if (new_failed_network
) {
285 VLOG(1) << "Failed Network: " << new_failed_network
->service_path();
286 notification_connection_error_
->ShowAlways(
287 l10n_util::GetStringFUTF16(
288 IDS_NETWORK_CONNECTION_ERROR_MESSAGE_WITH_DETAILS
,
289 UTF8ToUTF16(new_failed_network
->name()),
290 UTF8ToUTF16(new_failed_network
->GetErrorString())),
291 string16(), BalloonViewHost::MessageCallback(), false, false);
295 void NetworkMessageObserver::OnCellularDataPlanChanged(NetworkLibrary
* cros
) {
296 if (!ShouldShowMobilePlanNotifications())
298 const CellularNetwork
* cellular
= cros
->cellular_network();
299 if (!cellular
|| !cellular
->SupportsDataPlan())
302 const CellularDataPlanVector
* plans
=
303 cros
->GetDataPlans(cellular
->service_path());
304 // If no plans available, check to see if we need a new plan.
305 if (!plans
|| plans
->empty()) {
306 // If previously, we had low data, we know that a plan was near expiring.
307 // In that case, because the plan disappeared, we assume that it expired.
308 if (cellular_data_left_
== CellularNetwork::DATA_LOW
) {
309 ShowNoDataNotification(cellular
, cellular_data_plan_type_
);
310 } else if (cellular
->needs_new_plan()) {
311 ShowNeedsPlanNotification(cellular
);
313 SaveLastCellularInfo(cellular
, NULL
);
317 CellularDataPlanVector::const_iterator iter
= plans
->begin();
318 const CellularDataPlan
* current_plan
= *iter
;
320 // If current plan is not the last plan (there is another backup plan),
321 // then we do not show notifications for this plan.
322 // For example, if there is another data plan available when this runs out.
323 for (++iter
; iter
!= plans
->end(); ++iter
) {
324 if (IsApplicableBackupPlan(current_plan
, *iter
)) {
325 SaveLastCellularInfo(cellular
, current_plan
);
330 // If connected cellular network changed, or data plan is different, then
331 // it's a new network. Then hide all previous notifications.
332 bool new_plan
= cellular
->service_path() != cellular_service_path_
||
333 current_plan
->GetUniqueIdentifier() != cellular_data_plan_unique_id_
;
336 InitNewPlan(current_plan
);
339 if (cellular
->data_left() == CellularNetwork::DATA_NONE
) {
340 ShowNoDataNotification(cellular
, current_plan
->plan_type
);
341 } else if (cellular
->data_left() == CellularNetwork::DATA_VERY_LOW
) {
342 // Only show low data notification if we transition to very low data
343 // and we are on the same plan. This is so that users don't get a
344 // notification whenever they connect to a low data 3g network.
345 if (!new_plan
&& (cellular_data_left_
!= CellularNetwork::DATA_VERY_LOW
))
346 ShowLowDataNotification(current_plan
);
349 SaveLastCellularInfo(cellular
, current_plan
);
352 void NetworkMessageObserver::OnConnectionInitiated(NetworkLibrary
* cros
,
353 const Network
* network
) {
354 // If user initiated any network connection, we hide the error notification.
355 notification_connection_error_
->Hide();
358 void NetworkMessageObserver::SaveLastCellularInfo(
359 const CellularNetwork
* cellular
, const CellularDataPlan
* plan
) {
361 cellular_service_path_
= cellular
->service_path();
362 cellular_data_left_
= cellular
->data_left();
364 cellular_data_plan_unique_id_
= plan
->GetUniqueIdentifier();
365 cellular_data_plan_type_
= plan
->plan_type
;
367 cellular_data_plan_unique_id_
= std::string();
368 cellular_data_plan_type_
= CELLULAR_DATA_PLAN_UNKNOWN
;
372 } // namespace chromeos