1 // Copyright 2014 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/ui/webui/copresence_ui_handler.h"
11 #include "base/bind.h"
12 #include "base/i18n/time_formatting.h"
13 #include "base/time/time.h"
14 #include "base/values.h"
15 #include "chrome/browser/extensions/api/copresence/copresence_api.h"
16 #include "components/copresence/proto/data.pb.h"
17 #include "components/copresence/public/copresence_manager.h"
18 #include "components/copresence/public/copresence_state.h"
19 #include "components/copresence/tokens.h"
20 #include "content/public/browser/web_contents.h"
21 #include "content/public/browser/web_ui.h"
22 #include "ui/base/l10n/time_format.h"
24 using base::ListValue
;
25 using base::DictionaryValue
;
27 using copresence::Directive
;
28 using copresence::ReceivedToken
;
29 using copresence::TransmittedToken
;
30 using extensions::CopresenceService
;
32 // TODO(ckehoe): Make debug strings translatable?
36 std::string
FormatInstructionType(
37 copresence::TokenInstructionType directive_type
) {
38 switch (directive_type
) {
39 case copresence::TRANSMIT
:
42 case copresence::RECEIVE
:
51 std::string
FormatMedium(copresence::TokenMedium medium
) {
53 case copresence::AUDIO_ULTRASOUND_PASSBAND
:
56 case copresence::AUDIO_AUDIBLE_DTMF
:
65 std::string
ConvertStatus(const TransmittedToken
& token
) {
66 bool done
= token
.stop_time
< base::Time::Now();
67 std::string status
= done
? "done" : "active";
68 if (token
.broadcast_confirmed
)
69 status
+= " confirmed";
73 std::string
ConvertStatus(const ReceivedToken
& token
) {
74 switch (token
.valid
) {
75 case ReceivedToken::VALID
:
78 case ReceivedToken::INVALID
:
81 case ReceivedToken::UNKNOWN
:
91 scoped_ptr
<DictionaryValue
> FormatToken(const T
& token
) {
92 scoped_ptr
<DictionaryValue
> js_token(new DictionaryValue
);
94 js_token
->SetString("id", token
.id
);
95 js_token
->SetString("statuses", ConvertStatus(token
));
96 js_token
->SetString("medium", FormatMedium(token
.medium
));
97 DCHECK(!token
.start_time
.is_null());
98 js_token
->SetString("time",
99 base::TimeFormatTimeOfDay(token
.start_time
));
101 return js_token
.Pass();
104 // Retrieve the CopresenceService, if any.
105 CopresenceService
* GetCopresenceService(WebUI
* web_ui
) {
107 return CopresenceService::GetFactoryInstance()->Get(
108 web_ui
->GetWebContents()->GetBrowserContext());
111 // Safely retrieve the CopresenceState, if any.
112 copresence::CopresenceState
* GetCopresenceState(CopresenceService
* service
) {
113 // During shutdown, there may be no CopresenceService.
114 return service
&& service
->manager() ? service
->manager()->state() : nullptr;
117 // Safely retrieve the CopresenceState, if any. It would be cleaner if we could
118 // put this into CopresenceUIHandler and call WebUIMessageHandler::web_ui()
119 // instead of taking an argument. However, it turns out that web_ui() returns
120 // null when called in the constructor. So we pass in the web_ui explicitly.
121 copresence::CopresenceState
* GetCopresenceState(WebUI
* web_ui
) {
122 return GetCopresenceState(GetCopresenceService(web_ui
));
130 CopresenceUIHandler::CopresenceUIHandler(WebUI
* web_ui
)
131 : state_(GetCopresenceState(web_ui
)) {
133 state_
->AddObserver(this);
136 CopresenceUIHandler::~CopresenceUIHandler() {
137 // Check if the CopresenceService is still up before unregistering.
138 state_
= GetCopresenceState(web_ui());
140 state_
->RemoveObserver(this);
144 // Private functions.
146 void CopresenceUIHandler::RegisterMessages() {
147 web_ui()->RegisterMessageCallback(
148 "populateCopresenceState",
149 base::Bind(&CopresenceUIHandler::HandlePopulateState
,
150 base::Unretained(this)));
151 web_ui()->RegisterMessageCallback(
152 "clearCopresenceState",
153 base::Bind(&CopresenceUIHandler::HandleClearState
,
154 base::Unretained(this)));
157 void CopresenceUIHandler::DirectivesUpdated() {
158 ListValue js_directives
;
159 for (const Directive
& directive
: state_
->active_directives()) {
160 scoped_ptr
<DictionaryValue
> js_directive(new DictionaryValue
);
162 js_directive
->SetString("type", FormatInstructionType(
163 directive
.token_instruction().token_instruction_type()));
164 js_directive
->SetString("medium", FormatMedium(
165 directive
.token_instruction().medium()));
166 js_directive
->SetString("duration", ui::TimeFormat::Simple(
167 ui::TimeFormat::FORMAT_DURATION
,
168 ui::TimeFormat::LENGTH_LONG
,
169 base::TimeDelta::FromMilliseconds(directive
.ttl_millis())));
171 js_directives
.Append(js_directive
.release());
174 web_ui()->CallJavascriptFunction("refreshDirectives", js_directives
);
177 void CopresenceUIHandler::TokenTransmitted(const TransmittedToken
& token
) {
178 web_ui()->CallJavascriptFunction("updateTransmittedToken",
179 *FormatToken(token
));
182 void CopresenceUIHandler::TokenReceived(const ReceivedToken
& token
) {
183 web_ui()->CallJavascriptFunction("updateReceivedToken",
184 *FormatToken(token
));
187 void CopresenceUIHandler::HandlePopulateState(const ListValue
* args
) {
188 DCHECK(args
->empty());
190 // TODO(ckehoe): Pass tokens to JS as a batch.
191 for (const auto& token_entry
: state_
->transmitted_tokens())
192 TokenTransmitted(token_entry
.second
);
193 for (const auto& token_entry
: state_
->received_tokens())
194 TokenReceived(token_entry
.second
);
197 void CopresenceUIHandler::HandleClearState(const ListValue
* args
) {
198 DCHECK(args
->empty());
200 CopresenceService
* service
= GetCopresenceService(web_ui());
202 service
->ResetState();
204 // CopresenceService::ResetState() deletes the CopresenceState object
205 // we were using. We have to get the new one and reconnect to it.
206 state_
= GetCopresenceState(service
);
208 state_
->AddObserver(this);
210 web_ui()->CallJavascriptFunction("clearTokens");