diff options
-rw-r--r-- | chrome/app/generated_resources.grd | 18 | ||||
-rw-r--r-- | chrome/browser/chromeos/cros/network_library.cc | 18 | ||||
-rw-r--r-- | chrome/browser/chromeos/cros/network_library.h | 7 | ||||
-rw-r--r-- | chrome/browser/chromeos/dom_ui/mobile_setup_ui.cc | 336 | ||||
-rw-r--r-- | chrome/browser/resources/connection_manager.js | 21 | ||||
-rw-r--r-- | chrome/browser/resources/mobile_setup.html | 161 | ||||
-rw-r--r-- | chrome/browser/resources/mobile_setup.js | 89 |
7 files changed, 572 insertions, 78 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 5c031788..3d96e42 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -4073,10 +4073,22 @@ Keep your key file in a safe place. You will need it to create new versions of y <!-- chrome://mobilesetup strings --> <message name="IDS_MOBILE_SETUP_TITLE" desc="ChromeOS mobile device activation page title"> - Activate your cellular connection + Cellular data service management </message> - <message name="IDS_MOBILE_SETUP_HEADER" desc="ChromeOS mobile device activation page heade"> - Cellular device activation + <message name="IDS_MOBILE_CONNECTING_HEADER" desc="Mobile connection page header while connecting to the provider"> + Connecting to <ph name="PROVIDER_NAME">$1<ex>Acme Mobile</ex></ph> + </message> + <message name="IDS_MOBILE_ERROR_HEADER" desc="Mobile connection page header when connection error occur"> + Connection error: + </message> + <message name="IDS_MOBILE_ACTIVATING_HEADER" desc="Message header when data service is being activated"> + Activating your data service + </message> + <message name="IDS_MOBILE_COMPLETED_HEADER" desc="Message header when data service is completed"> + Activation complete + </message> + <message name="IDS_MOBILE_COMPLETED_TEXT" desc="Message when cellular data service activation or payment is completed"> + Your data service is activated and ready to use </message> </if> diff --git a/chrome/browser/chromeos/cros/network_library.cc b/chrome/browser/chromeos/cros/network_library.cc index 1653cd2..b86f62e 100644 --- a/chrome/browser/chromeos/cros/network_library.cc +++ b/chrome/browser/chromeos/cros/network_library.cc @@ -195,6 +195,24 @@ bool CellularNetwork::StartActivation() const { void CellularNetwork::Clear() { WirelessNetwork::Clear(); + activation_state_ = ACTIVATION_STATE_UNKNOWN; + roaming_state_ = ROAMING_STATE_UNKNOWN; + network_technology_ = NETWORK_TECHNOLOGY_UNKNOWN; + operator_name_.clear(); + operator_code_.clear(); + payment_url_.clear(); + meid_.clear(); + imei_.clear(); + imsi_.clear(); + esn_.clear(); + mdn_.clear(); + min_.clear(); + model_id_.clear(); + manufacturer_.clear(); + firmware_revision_.clear(); + hardware_revision_.clear(); + last_update_.clear(); + prl_version_ = 0; } void CellularNetwork::ConfigureFromService(const ServiceInfo& service) { diff --git a/chrome/browser/chromeos/cros/network_library.h b/chrome/browser/chromeos/cros/network_library.h index b147206..38411d5 100644 --- a/chrome/browser/chromeos/cros/network_library.h +++ b/chrome/browser/chromeos/cros/network_library.h @@ -23,6 +23,7 @@ class Network { const std::string& device_path() const { return device_path_; } const std::string& ip_address() const { return ip_address_; } ConnectionType type() const { return type_; } + ConnectionState connection_state() const { return state_; } bool connecting() const { return state_ == STATE_ASSOCIATION || state_ == STATE_CONFIGURATION || state_ == STATE_CARRIER; } bool connected() const { return state_ == STATE_READY; } @@ -62,7 +63,7 @@ class Network { std::string device_path_; std::string ip_address_; ConnectionType type_; - int state_; + ConnectionState state_; ConnectionError error_; }; @@ -129,8 +130,8 @@ class CellularNetwork : public WirelessNetwork { const NetworkTechnology network_technology() const { return network_technology_; } const NetworkRoamingState roaming_state() const { return roaming_state_; } - const std::string& operator_name() const { return payment_url_; } - const std::string& operator_code() const { return payment_url_; } + const std::string& operator_name() const { return operator_name_; } + const std::string& operator_code() const { return operator_code_; } const std::string& payment_url() const { return payment_url_; } const std::string& meid() const { return meid_; } const std::string& imei() const { return imei_; } diff --git a/chrome/browser/chromeos/dom_ui/mobile_setup_ui.cc b/chrome/browser/chromeos/dom_ui/mobile_setup_ui.cc index 1c11c09..405ec7e 100644 --- a/chrome/browser/chromeos/dom_ui/mobile_setup_ui.cc +++ b/chrome/browser/chromeos/dom_ui/mobile_setup_ui.cc @@ -4,18 +4,26 @@ #include "chrome/browser/chromeos/dom_ui/mobile_setup_ui.h" +#include <map> #include <string> #include "app/l10n_util.h" #include "app/resource_bundle.h" #include "base/callback.h" +#include "base/file_util.h" +#include "base/json/json_reader.h" #include "base/logging.h" #include "base/string_piece.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" #include "base/values.h" #include "base/weak_ptr.h" +#include "chrome/browser/browser.h" +#include "chrome/browser/browser_list.h" #include "chrome/browser/browser_process.h" +#include "chrome/browser/chromeos/cros/cros_library.h" +#include "chrome/browser/chromeos/cros/network_library.h" +#include "chrome/browser/chromeos/cros/system_library.h" #include "chrome/browser/chrome_thread.h" #include "chrome/browser/dom_ui/chrome_url_data_manager.h" #include "chrome/browser/tab_contents/tab_contents.h" @@ -27,21 +35,65 @@ #include "grit/generated_resources.h" #include "grit/locale_settings.h" -#include "chrome/browser/chromeos/cros/cros_library.h" -#include "chrome/browser/chromeos/cros/network_library.h" -#include "chrome/browser/chromeos/cros/system_library.h" - namespace { // Host page JS API function names. -const char kJsApiGetDeviceInfo[] = "getDeviceInfo"; +const char kJsApiCloseTab[] = "closeTab"; const char kJsApiSetTransactionStatus[] = "setTransactionStatus"; const wchar_t kJsDeviceStatusChangedHandler[] = - L"mobile.MobileSetup.onDeviceStatusChanged"; + L"handler.MobileSetup.deviceStateChanged"; + +// Collular device states that are reported to DOM UI layer. +const char kStateUnknown[] = "unknown"; +const char kStateConnecting[] = "connecting"; +const char kStateError[] = "error"; +const char kStateNeedsPayment[] = "payment"; +const char kStateActivating[] = "activating"; +const char kStateDisconnected[] = "disconnected"; +const char kStateConnected[] = "connected"; +const char kFailedPayment[] = "failed_payment"; + +// Error codes matching codes defined in the cellular config file. +const char kErrorDefault[] = "default"; +const char kErrorBadConnectionPartial[] = "bad_connection_partial"; +const char kErrorBadConnectionActivated[] = "bad_connection_activated"; +const char kErrorRoamingOnConnection[] = "roaming_connection"; +const char kErrorNoEVDO[] = "no_evdo"; +const char kErrorRoamingActivation[] = "roaming_activation"; +const char kErrorRoamingPartiallyActivated[] = "roaming_partially_activated"; +const char kErrorNoService[] = "no_service"; +const char kFailedPaymentError[] = "failed_payment"; + +// Cellular configuration file path. +const char kCellularConfigPath[] = + "/usr/share/chromeos-assets/mobile/mobile_config.json"; + +// Cellular config file field names. +const char kVersionField[] = "version"; +const char kErrorsField[] = "errors"; } // namespace +class CellularConfigDocument { + public: + CellularConfigDocument() {} + + // Return error message for a given code. + std::string GetErrorMessage(const std::string& code); + const std::string& version() { return version_; } + + bool LoadFromFile(const FilePath& config_path); + + private: + std::string version_; + std::map<std::string, std::string> error_map_; + + DISALLOW_COPY_AND_ASSIGN(CellularConfigDocument); +}; + +static std::map<std::string, std::string> error_messages_; + class MobileSetupUIHTMLSource : public ChromeURLDataManager::DataSource { public: MobileSetupUIHTMLSource(); @@ -63,32 +115,102 @@ class MobileSetupUIHTMLSource : public ChromeURLDataManager::DataSource { // The handler for Javascript messages related to the "register" view. class MobileSetupHandler : public DOMMessageHandler, + public chromeos::NetworkLibrary::Observer, public base::SupportsWeakPtr<MobileSetupHandler> { public: MobileSetupHandler(); virtual ~MobileSetupHandler(); // Init work after Attach. - void Init(); + void Init(TabContents* contents); // DOMMessageHandler implementation. virtual DOMMessageHandler* Attach(DOMUI* dom_ui); virtual void RegisterMessages(); + // NetworkLibrary::Observer implementation. + virtual void NetworkChanged(chromeos::NetworkLibrary* obj); + private: // Handlers for JS DOMUI messages. - void HandleGetDeviceInfo(const ListValue* args); + void HandleCloseTab(const ListValue* args); void HandleSetTransactionStatus(const ListValue* args); // Sends message to host registration page with system/user info data. void SendDeviceInfo(); - // Converts device the current CelularNetork into JS object. - bool GetDeviceInfo(DictionaryValue* value); + // Converts the currently active CellularNetwork device into a JS object. + static bool GetDeviceInfo(DictionaryValue* value); + static bool ShouldReportDeviceState(std::string* state, std::string* error); + + // Performs activation state cellular device evaluation. + // Returns false if device activation failed. In this case |error| + // will contain error message to be reported to DOM UI. + static bool CheckForActivationError(chromeos::CellularNetwork network, + std::string* error); + + // Return error message for a given code. + static std::string GetErrorMessage(const std::string& code); + static void LoadCellularConfig(); + static scoped_ptr<CellularConfigDocument> cellular_config_; + + TabContents* tab_contents_; DISALLOW_COPY_AND_ASSIGN(MobileSetupHandler); }; +scoped_ptr<CellularConfigDocument> MobileSetupHandler::cellular_config_; + +//////////////////////////////////////////////////////////////////////////////// +// +// CellularConfigDocument +// +//////////////////////////////////////////////////////////////////////////////// + +std::string CellularConfigDocument::GetErrorMessage(const std::string& code) { + std::map<std::string, std::string>::iterator iter = error_map_.find(code); + if (iter == error_map_.end()) + return code; + return iter->second; +} + +bool CellularConfigDocument::LoadFromFile(const FilePath& config_path) { + error_map_.clear(); + + std::string config; + if (!file_util::ReadFileToString(config_path, &config)) + return false; + + scoped_ptr<Value> root(base::JSONReader::Read(config, true)); + DCHECK(root.get() != NULL); + if (!root.get() || root->GetType() != Value::TYPE_DICTIONARY) { + LOG(INFO) << "Bad cellular config file"; + return false; + } + + DictionaryValue* root_dict = static_cast<DictionaryValue*>(root.get()); + if (!root_dict->GetString(kVersionField, &version_)) { + LOG(INFO) << "Cellular config file missing version"; + return false; + } + DictionaryValue* errors = NULL; + if (!root_dict->GetDictionary(kErrorsField, &errors)) + return false; + for (DictionaryValue::key_iterator keys = errors->begin_keys(); + keys != errors->end_keys(); + ++keys) { + std::string value; + if (!errors->GetString(*keys, &value)) { + LOG(INFO) << "Bad cellular config error value"; + error_map_.clear(); + return false; + } + + error_map_.insert(std::pair<std::string, std::string>(*keys, value)); + } + return true; +} + //////////////////////////////////////////////////////////////////////////////// // // MobileSetupUIHTMLSource @@ -104,8 +226,16 @@ void MobileSetupUIHTMLSource::StartDataRequest(const std::string& path, int request_id) { DictionaryValue strings; strings.SetString("title", l10n_util::GetStringUTF16(IDS_MOBILE_SETUP_TITLE)); - strings.SetString("header", - l10n_util::GetStringUTF16(IDS_MOBILE_SETUP_HEADER)); + strings.SetString("connecting_header", + l10n_util::GetStringUTF16(IDS_MOBILE_CONNECTING_HEADER)); + strings.SetString("error_header", + l10n_util::GetStringUTF16(IDS_MOBILE_ERROR_HEADER)); + strings.SetString("activating_header", + l10n_util::GetStringUTF16(IDS_MOBILE_ACTIVATING_HEADER)); + strings.SetString("completed_header", + l10n_util::GetStringUTF16(IDS_MOBILE_COMPLETED_HEADER)); + strings.SetString("completed_text", + l10n_util::GetStringUTF16(IDS_MOBILE_COMPLETED_TEXT)); SetFontAndTextDirection(&strings); static const base::StringPiece html( @@ -126,42 +256,52 @@ void MobileSetupUIHTMLSource::StartDataRequest(const std::string& path, // MobileSetupHandler // //////////////////////////////////////////////////////////////////////////////// -MobileSetupHandler::MobileSetupHandler() { +MobileSetupHandler::MobileSetupHandler() : tab_contents_(NULL) { } MobileSetupHandler::~MobileSetupHandler() { + chromeos::CrosLibrary::Get()->GetNetworkLibrary()->RemoveObserver(this); } DOMMessageHandler* MobileSetupHandler::Attach(DOMUI* dom_ui) { return DOMMessageHandler::Attach(dom_ui); } -void MobileSetupHandler::Init() { - // TODO(zelidag): Register for network change notification to make sure - // that the status of the cellular network does not flip to something we - // can't handle (disabled, suddenly activated by divine intervention...). +void MobileSetupHandler::Init(TabContents* contents) { + tab_contents_ = contents; + chromeos::CrosLibrary::Get()->GetNetworkLibrary()->AddObserver(this); + + // TODO(zelidrag): We might want to move this to another thread. + LoadCellularConfig(); } void MobileSetupHandler::RegisterMessages() { - dom_ui_->RegisterMessageCallback(kJsApiGetDeviceInfo, - NewCallback(this, &MobileSetupHandler::HandleGetDeviceInfo)); + dom_ui_->RegisterMessageCallback(kJsApiCloseTab, + NewCallback(this, &MobileSetupHandler::HandleCloseTab)); dom_ui_->RegisterMessageCallback(kJsApiSetTransactionStatus, NewCallback(this, &MobileSetupHandler::HandleSetTransactionStatus)); } -void MobileSetupHandler::HandleGetDeviceInfo(const ListValue* args) { - const size_t kGetDeviceInfoParamCount = 1; - if (args->GetSize() != kGetDeviceInfoParamCount) +void MobileSetupHandler::NetworkChanged(chromeos::NetworkLibrary* cros) { + if (!dom_ui_) return; - - // Get change callback function name. - string16 callback_func_name; - if (!args->GetString(0, &callback_func_name)) + DictionaryValue device; + GetDeviceInfo(&device); + std::string state, error; + if (!ShouldReportDeviceState(&state, &error)) return; - DictionaryValue value; - if (GetDeviceInfo(&value)) - dom_ui_->CallJavascriptFunction(UTF16ToWide(callback_func_name), value); + device.SetString("state", state); + if (error.length()) + device.SetString("error", error); + dom_ui_->CallJavascriptFunction( + kJsDeviceStatusChangedHandler, device); +} + +void MobileSetupHandler::HandleCloseTab(const ListValue* args) { + Browser* browser = BrowserList::GetLastActive(); + if (browser) + browser->CloseTabContents(tab_contents_); } void MobileSetupHandler::HandleSetTransactionStatus(const ListValue* args) { @@ -180,17 +320,110 @@ void MobileSetupHandler::HandleSetTransactionStatus(const ListValue* args) { network_lib->cellular_networks(); if (!cell_networks.size()) return; + + // We assume only one cellular network will come from flimflam for now. const chromeos::CellularNetwork& network = *(cell_networks.begin()); - if (LowerCaseEqualsASCII(status, "OK") && network.StartActivation()) { + if (LowerCaseEqualsASCII(status, "OK")) { + network.StartActivation(); + } else { DictionaryValue value; - if (GetDeviceInfo(&value)) - dom_ui_->CallJavascriptFunction(kJsDeviceStatusChangedHandler, value); + value.SetString("state", kFailedPaymentError); + dom_ui_->CallJavascriptFunction(kJsDeviceStatusChangedHandler, value); } - // TODO(zelidrag): Close the setup tab automatically when payment fails? - // Pending UX decision on this one. } +bool MobileSetupHandler::ShouldReportDeviceState(std::string* state, + std::string* error) { + DCHECK(state); + DCHECK(error); + chromeos::NetworkLibrary* network_lib = + chromeos::CrosLibrary::Get()->GetNetworkLibrary(); + + const chromeos::CellularNetworkVector& cell_networks = + network_lib->cellular_networks(); + // No cellular network present? Treat as network is disconnected. + // This could be transient state that is the result of activation process. + if (!cell_networks.size()) { + *state = kStateDisconnected; + return true; + } + const chromeos::CellularNetwork& network = cell_networks.at(0); + + // First, check if device activation / plan payment failed. + // It's slightly more complex than just single state check + // that we are doing for other states below. + if (!CheckForActivationError(network, error)) { + *state = kStateError; + return true; + } + + switch (network.activation_state()) { + case chromeos::ACTIVATION_STATE_UNKNOWN: + case chromeos::ACTIVATION_STATE_NOT_ACTIVATED: + // If this page is shown, I assume that we have already kicked off the + // process of starting the activation even though the device status + // reporting might not have caught up with us yet. + *state = kStateConnecting; + return true; + case chromeos::ACTIVATION_STATE_PARTIALLY_ACTIVATED: + case chromeos::ACTIVATION_STATE_ACTIVATING: + *state = kStateActivating; + return true; + case chromeos::ACTIVATION_STATE_ACTIVATED: + *state = kStateConnected; + return true; + } + + // We don't report states that we don't understand to DOM UI. + *state = kStateUnknown; + return false; +} + + +bool MobileSetupHandler::CheckForActivationError( + chromeos::CellularNetwork network, std::string* error) { + bool got_error = false; + const char* error_code = kErrorDefault; + + // This is the magic of error selecting based + if (network.connection_state() == chromeos::STATE_FAILURE && + network.error() == chromeos::ERROR_AAA_FAILED ) { + if (network.activation_state() == + chromeos::ACTIVATION_STATE_PARTIALLY_ACTIVATED) { + error_code = kErrorBadConnectionPartial; + } else if (network.activation_state() == + chromeos::ACTIVATION_STATE_ACTIVATED) { + if (network.roaming_state() == chromeos::ROAMING_STATE_HOME) { + error_code = kErrorBadConnectionActivated; + } else if (network.roaming_state() == chromeos::ROAMING_STATE_ROAMING) { + error_code = kErrorRoamingOnConnection; + } + } + got_error = true; + } else if (network.connection_state() == + chromeos::STATE_ACTIVATION_FAILURE) { + if (network.error() == chromeos::ERROR_NEED_EVDO) { + if (network.activation_state() == + chromeos::ACTIVATION_STATE_PARTIALLY_ACTIVATED) + error_code = kErrorNoEVDO; + } else if (network.error() == chromeos::ERROR_NEED_HOME_NETWORK) { + if (network.activation_state() == + chromeos::ACTIVATION_STATE_NOT_ACTIVATED) { + error_code = kErrorRoamingActivation; + } else if (network.activation_state() == + chromeos::ACTIVATION_STATE_PARTIALLY_ACTIVATED) { + error_code = kErrorRoamingPartiallyActivated; + } + } + got_error = true; + } + + if (got_error) + *error = GetErrorMessage(error_code); + + return !got_error; +} bool MobileSetupHandler::GetDeviceInfo(DictionaryValue* value) { DCHECK(value); @@ -199,14 +432,13 @@ bool MobileSetupHandler::GetDeviceInfo(DictionaryValue* value) { const chromeos::CellularNetworkVector& cell_networks = network_lib->cellular_networks(); - if (!cell_networks.size()) + if (!cell_networks.size()) { return false; + } const chromeos::CellularNetwork& network = *(cell_networks.begin()); - value->SetString("carrier", UTF8ToUTF16(network.name())); value->SetString("payment_url", UTF8ToUTF16(network.payment_url())); - value->SetInteger("activation_state", network.activation_state()); value->SetString("MEID", UTF8ToUTF16(network.meid())); value->SetString("IMEI", UTF8ToUTF16(network.imei())); value->SetString("IMSI", UTF8ToUTF16(network.imsi())); @@ -215,6 +447,34 @@ bool MobileSetupHandler::GetDeviceInfo(DictionaryValue* value) { return true; } +std::string MobileSetupHandler::GetErrorMessage(const std::string& code) { + if (!cellular_config_.get()) + return ""; + return cellular_config_->GetErrorMessage(code); +} + +void MobileSetupHandler::LoadCellularConfig() { + static bool config_loaded = false; + if (config_loaded) + return; + config_loaded = true; + // Load partner customization startup manifest if it is available. + FilePath config_path(kCellularConfigPath); + if (file_util::PathExists(config_path)) { + scoped_ptr<CellularConfigDocument> config(new CellularConfigDocument()); + bool config_loaded = config->LoadFromFile(config_path); + if (config_loaded) { + LOG(INFO) << "Cellular config file loaded: " << kCellularConfigPath; + // lock + cellular_config_.reset(config.release()); + } else { + LOG(ERROR) << "Error loading cellular config file: " << + kCellularConfigPath; + } + } +} + + //////////////////////////////////////////////////////////////////////////////// // // MobileSetupUI @@ -224,7 +484,7 @@ bool MobileSetupHandler::GetDeviceInfo(DictionaryValue* value) { MobileSetupUI::MobileSetupUI(TabContents* contents) : DOMUI(contents){ MobileSetupHandler* handler = new MobileSetupHandler(); AddMessageHandler((handler)->Attach(this)); - handler->Init(); + handler->Init(contents); MobileSetupUIHTMLSource* html_source = new MobileSetupUIHTMLSource(); // Set up the chrome://mobilesetup/ source. diff --git a/chrome/browser/resources/connection_manager.js b/chrome/browser/resources/connection_manager.js index bf443c4..baa3737 100644 --- a/chrome/browser/resources/connection_manager.js +++ b/chrome/browser/resources/connection_manager.js @@ -9,15 +9,9 @@ function chromeos() { chromeos.connectionManager = function() { }; -chromeos.connectionManager.device_info_callback_ = null; chromeos.connectionManager.transaction_status_callback_ = null; chromeos.connectionManager.parent_page_url_ = 'chrome://mobilesetup'; -chromeos.connectionManager.getDeviceInfo = function(callback) { - chromeos.connectionManager.device_info_callback_ = callback; - chromeos.connectionManager.requestDeviceInfo_(); -}; - chromeos.connectionManager.setTransactionStatus = function(status, callback) { chromeos.connectionManager.transaction_status_callback_ = callback; chromeos.connectionManager.reportTransactionStatus_(status); @@ -32,18 +26,3 @@ chromeos.connectionManager.reportTransactionStatus_ = function(status) { window.parent.postMessage(msg, chromeos.connectionManager.parent_page_url_); }; -chromeos.connectionManager.requestDeviceInfo_ = function() { - var msg = { - 'type': 'requestDeviceInfoMsg', - 'domain': location.href, - }; - window.parent.postMessage(msg, chromeos.connectionManager.parent_page_url_); -} - -window.addEventListener('message', function(e) { - if (e.data.type == 'deviceInfoMsg') { - if (chromeos.connectionManager.device_info_callback_) - chromeos.connectionManager.device_info_callback_(e.data.payload); - } -}); - diff --git a/chrome/browser/resources/mobile_setup.html b/chrome/browser/resources/mobile_setup.html index 1811e7b..5fa2e49 100644 --- a/chrome/browser/resources/mobile_setup.html +++ b/chrome/browser/resources/mobile_setup.html @@ -5,18 +5,139 @@ <title i18n-content="title"></title> <style> body { - font-family: sans-serif; - font-size: 10px; + font-size: 100%; + -webkit-user-select: none; +} + +iframe { + overflow-x: scroll; + overflow-y: scroll; +} + +.overlay { + position: fixed; + left: 0; + right: 0; + background: rgba(0, 0, 0, .2); + top: 0; + bottom: 0; + z-index: 10; + padding: 20px; + -webkit-box-align: center; + -webkit-box-pack: center; +} + +.overlay > div { + background: white; + border-radius: 5px; + padding: 15px; + border: 1px solid #666; + -webkit-box-shadow: 3px 3px 3px #666; +} + +.startup { + width: 500px; + position: absolute; + top: 50%; + left: 50%; + margin-left:-250px; + margin-top:-250px; + text-align: center; } #payment-form { - width: 600px; - height: 300px; - overflow: hidden; + display: -webkit-box; + position: absolute; + left: 0; + right: 0; + top: 0; + bottom: 0; + width: 100%; + height: 100%; +} + +.logo { + background: url('file:///usr/share/chromeos-assets/mobile/carrier_logo.png') no-repeat; + height: 58px; +} + +#error-msg { + margin: 20px; +} + +.box { + display: -webkit-box; +} + +#final-logo { + position: absolute; + right: 30px; + width: 150px; +} + +#activation-logo { + background-position: center; + margin-bottom: 20px; + margin-top: 20px; } -#error-div { +#splitter { + margin-top: 10px; + left: 50%; + margin-left: 150px; + width: 200px; + border-bottom: 1px solid #EEE; + height: 1px; } + +body[state='connecting'] > #payment-form, +body[state='connecting'] > #final-message, +body[state='connecting'] > * > #error-message { + display: none +} +body[state='connecting'] > #system-status { + display: block +} + +body[state='error'] > #payment-form, +body[state='error'] > #final-message { + display: none +} +body[state='error'] > * > #error-message, +body[state='error'] > #system-status { + display: block +} + +body[state='payment'] > * > #error-message, +body[state='payment'] > #final-message, +body[state='payment'] > #system-status { + display: none +} +body[state='payment'] > #payment-form { + display: block +} + +body[state='activating'] > #payment-form, +body[state='activating'] > #final-message, +body[state='activating'] > * > #error-message { + display: none +} +body[state='activating'] > #system-status { + display: block +} + +body[state='connected'] > * > #error-message, +body[state='connected'] > #system-status { + display: none +} +body[state='connected'] > #payment-form, +body[state='connected'] > #final-message { + display: block +} + +.testing-only { +} + </style> <script src="chrome://resources/js/cr.js"></script> <script src="chrome://resources/js/util.js"></script> @@ -25,8 +146,28 @@ body { mobile.MobileSetup.getInstance().initialize('payment-form'); </script> </head> -<body i18n-values=".style.fontFamily:fontfamily;.style.fontSize:fontsize"> -<h1 i18n-content="header"></h1> -<div id="error-div"></div> -<iframe id="payment-form" frameborder="0"></iframe></body> +<body state="connecting" onload="setInterval(mobile.MobileSetup.drawProgress, 100);" + i18n-values=".style.fontFamily:fontfamily;.style.fontSize:fontsize"> + <iframe id="payment-form" frameborder="0"></iframe> + <div id="system-status" class="startup"> + <div class="status-header"><h3 id="header" + i18n-content="status_header"></h3></div> + <div id="error-message"></div> + <canvas id="canvas" width="56" height="56"></canvas> + <div id="splitter"></div> + <div id="activation-logo" class="logo"></div> + </div> + <div id="final-message" class="overlay"> + <div class="box"> + <div> + <div class="header"><h3 i18n-content="completed_header"></h3></div> + <div id="action" i18n-content="completed_text"></div> + </div> + <div id="final-logo" class="logo"></div> + </div> + </div> + <div class="testing-only"> + <button id="cheat">Fake activation!</button> + </div> +</body> </html> diff --git a/chrome/browser/resources/mobile_setup.js b/chrome/browser/resources/mobile_setup.js index fa1e98e..f6c481d 100644 --- a/chrome/browser/resources/mobile_setup.js +++ b/chrome/browser/resources/mobile_setup.js @@ -14,6 +14,15 @@ cr.define('mobile', function() { // Mobile device information. deviceInfo_: null, frame_name_ : '', + local_strings_: new LocalStrings(); + // UI states. + state_ : 0, + STATE_UNKNOWN_: "unknown", + STATE_CONNECTING_: "connecting", + STATE_ERROR_: "error", + STATE_PAYMENT_: "payment", + STATE_ACTIVATING_: "activating", + STATE_CONNECTED_: "connected", initialize: function(frame_name) { self = this; @@ -31,9 +40,6 @@ cr.define('mobile', function() { if (deviceInfo) { this.deviceInfo_ = deviceInfo; - // HACK(zelidrag): Remove the next line for real testing. - this.deviceInfo_.payment_url = 'http://link.to/mobile_activation.html'; - $(this.frame_name_).contentWindow.location.href = this.deviceInfo_.payment_url; } @@ -51,6 +57,38 @@ cr.define('mobile', function() { } }, + changeState: function(new_state, errorText) { + if (state_ == new_state) + return; + if (new_state == STATE_UNKNOWN_) + document.body.setAttribute('state', STATE_CONNECTING_); + else + document.body.setAttribute('state', new_state); + switch(new_state) { + case STATE_UNKNOWN_: + case STATE_CONNECTING_: + $('status-header').textContent = + local_strings_.getString('connecting_header'); + $('error-message').textContent = ''; + break; + case STATE_ERROR_: + $('status-header').textContent = + local_strings_.getString('error_header'); + $('error-message').textContent = errorText; + break; + case STATE_ACTIVATING_: + $('status-header').textContent = + local_strings_.getString('activating_header'); + $('error-message').textContent = ''; + break; + } + state_ = new_state; + }, + + updateDeviceStatus_: function(deviceInfo) { + this.changeState(deviceInfo.state, deviceInfo.error); + }, + sendDeviceInfo_ : function() { var msg = { type: 'deviceInfoMsg', @@ -67,12 +105,57 @@ cr.define('mobile', function() { $(this.frame_name_).contentWindow.postMessage(msg, this.deviceInfo_.payment_url); } + + }; + + MobileSetup.drawProgress = function () { + var canvas = $('wheel'); + var ctx = canvas.getContext('2d'); + ctx.clearRect(0, 0, canvas.width, canvas.height); + + var segmentCount = Math.min(12, canvas.width/1.6) // Number of segments + var rotation = 0.75; // Counterclockwise rotation + + // Rotate canvas over time + ctx.translate(canvas.width/2, canvas.height/2); + ctx.rotate(Math.PI * 2 / (segmentCount + rotation)); + ctx.translate(-canvas.width/2, -canvas.height/2); + + var gap = canvas.width / 24; // Gap between segments + var oRadius = canvas.width/2; // Outer radius + var iRadius = oRadius * 0.618; // Inner radius + var oCircumference = Math.PI * 2 * oRadius; // Outer circumference + var iCircumference = Math.PI * 2 * iRadius; // Inner circumference + var oGap = gap / oCircumference; // Gap size as fraction of outer ring + var iGap = gap / iCircumference; // Gap size as fraction of inner ring + var oArc = Math.PI * 2 * ( 1 / segmentCount - oGap); // Angle of outer arcs + var iArc = Math.PI * 2 * ( 1 / segmentCount - iGap); // Angle of inner arcs + + for (i = 0; i < segmentCount; i++){ // Draw each segment + var opacity = Math.pow(1.0 - i / segmentCount, 3.0); + opacity = (0.15 + opacity * 0.8) // Vary from 0.15 to 0.95 + var angle = - Math.PI * 2 * i / segmentCount; + + ctx.beginPath(); + ctx.arc(canvas.width/2, canvas.height/2, oRadius, + angle - oArc/2, angle + oArc/2, false); + ctx.arc(canvas.width/2, canvas.height/2, iRadius, + angle + iArc/2, angle - iArc/2, true); + ctx.closePath(); + ctx.fillStyle = "rgba(240, 30, 29, " + opacity + ")"; + ctx.fill(); + } }; MobileSetup.getDeviceInfoCallback = function(deviceInfo) { MobileSetup.getInstance().loadPaymentFrame(deviceInfo); }; + + MobileSetup.deviceStateChanged = function(deviceInfo) { + MobileSetup.getInstance().updateDeviceStatus_(deviceInfo); + }; + // Export return { MobileSetup: MobileSetup |