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.
6 cr.define('mobile', function() {
8 function MobileSetup() {
11 cr.addSingletonGetter(MobileSetup);
13 MobileSetup.PLAN_ACTIVATION_UNKNOWN = -2;
14 MobileSetup.PLAN_ACTIVATION_PAGE_LOADING = -1;
15 MobileSetup.PLAN_ACTIVATION_START = 0;
16 MobileSetup.PLAN_ACTIVATION_TRYING_OTASP = 1;
17 MobileSetup.PLAN_ACTIVATION_INITIATING_ACTIVATION = 3;
18 MobileSetup.PLAN_ACTIVATION_RECONNECTING = 4;
19 MobileSetup.PLAN_ACTIVATION_WAITING_FOR_CONNECTION = 5;
20 MobileSetup.PLAN_ACTIVATION_PAYMENT_PORTAL_LOADING = 6;
21 MobileSetup.PLAN_ACTIVATION_SHOWING_PAYMENT = 7;
22 MobileSetup.PLAN_ACTIVATION_RECONNECTING_PAYMENT = 8;
23 MobileSetup.PLAN_ACTIVATION_DELAY_OTASP = 9;
24 MobileSetup.PLAN_ACTIVATION_START_OTASP = 10;
25 MobileSetup.PLAN_ACTIVATION_OTASP = 11;
26 MobileSetup.PLAN_ACTIVATION_DONE = 12;
27 MobileSetup.PLAN_ACTIVATION_ERROR = 0xFF;
29 MobileSetup.EXTENSION_PAGE_URL =
30 'chrome-extension://iadeocfgjdjdmpenejdbfeaocpbikmab';
31 MobileSetup.ACTIVATION_PAGE_URL = MobileSetup.EXTENSION_PAGE_URL +
33 MobileSetup.PORTAL_OFFLINE_PAGE_URL = MobileSetup.EXTENSION_PAGE_URL +
34 '/portal_offline.html';
35 MobileSetup.REDIRECT_POST_PAGE_URL = MobileSetup.EXTENSION_PAGE_URL +
38 MobileSetup.prototype = {
39 // Mobile device information.
43 fakedTransaction_: false,
46 frameLoadIgnored_: true,
47 carrierPageUrl_: null,
50 state_: MobileSetup.PLAN_ACTIVATION_UNKNOWN,
51 STATE_UNKNOWN_: 'unknown',
52 STATE_CONNECTING_: 'connecting',
53 STATE_ERROR_: 'error',
54 STATE_PAYMENT_: 'payment',
55 STATE_ACTIVATING_: 'activating',
56 STATE_CONNECTED_: 'connected',
58 initialize: function(frame_name, carrierPage) {
59 if (this.initialized_) {
60 console.log('calling initialize() again?');
63 this.initialized_ = true;
65 this.frameName_ = frame_name;
67 cr.ui.dialogs.BaseDialog.OK_LABEL =
68 loadTimeData.getString('ok_button');
69 cr.ui.dialogs.BaseDialog.CANCEL_LABEL =
70 loadTimeData.getString('cancel_button');
71 this.confirm_ = new cr.ui.dialogs.ConfirmDialog(document.body);
73 window.addEventListener('message', function(e) {
74 self.onMessageReceived_(e);
77 $('closeButton').addEventListener('click', function(e) {
78 $('finalStatus').classList.add('hidden');
81 // Kick off activation process.
82 chrome.send('startActivation');
85 startSpinner_: function() {
87 this.spinnerInt_ = setInterval(mobile.MobileSetup.drawProgress, 100);
90 stopSpinner_: function() {
91 if (this.spinnerInt_ != -1) {
92 clearInterval(this.spinnerInt_);
93 this.spinnerInt_ = -1;
97 onFrameLoaded_: function(success) {
98 chrome.send('paymentPortalLoad', [success ? 'ok' : 'failed']);
101 loadPaymentFrame_: function(deviceInfo) {
103 this.frameLoadError_ = 0;
104 this.deviceInfo_ = deviceInfo;
105 if (deviceInfo.post_data && deviceInfo.post_data.length) {
106 this.frameLoadIgnored_ = true;
107 $(this.frameName_).contentWindow.location.href =
108 MobileSetup.REDIRECT_POST_PAGE_URL +
109 '?post_data=' + escape(deviceInfo.post_data) +
110 '&formUrl=' + escape(deviceInfo.payment_url);
112 this.frameLoadIgnored_ = false;
113 $(this.frameName_).contentWindow.location.href =
114 deviceInfo.payment_url;
119 onMessageReceived_: function(e) {
121 this.deviceInfo_.payment_url.substring(0, e.origin.length) &&
122 e.origin != MobileSetup.EXTENSION_PAGE_URL)
125 if (e.data.type == 'requestDeviceInfoMsg') {
126 this.sendDeviceInfo_();
127 } else if (e.data.type == 'framePostReady') {
128 this.frameLoadIgnored_ = false;
129 this.sendPostFrame_(e.origin);
130 } else if (e.data.type == 'reportTransactionStatusMsg') {
131 console.log('calling setTransactionStatus from onMessageReceived_');
132 chrome.send('setTransactionStatus', [e.data.status]);
136 changeState_: function(deviceInfo) {
137 var newState = deviceInfo.state;
138 if (this.state_ == newState)
141 // The mobile setup is already in its final state.
142 if (this.state_ == MobileSetup.PLAN_ACTIVATION_DONE ||
143 this.state_ == MobileSetup.PLAN_ACTIVATION_ERROR) {
147 // Map handler state to UX.
148 var simpleActivationFlow =
149 (deviceInfo.activation_type == 'NonCellular' ||
150 deviceInfo.activation_type == 'OTA');
152 case MobileSetup.PLAN_ACTIVATION_PAGE_LOADING:
153 case MobileSetup.PLAN_ACTIVATION_START:
154 case MobileSetup.PLAN_ACTIVATION_DELAY_OTASP:
155 case MobileSetup.PLAN_ACTIVATION_START_OTASP:
156 case MobileSetup.PLAN_ACTIVATION_RECONNECTING:
157 case MobileSetup.PLAN_ACTIVATION_RECONNECTING_PAYMENT:
158 // Activation page should not be shown for the simple activation flow.
159 if (simpleActivationFlow)
162 $('statusHeader').textContent =
163 loadTimeData.getString('connecting_header');
164 $('auxHeader').textContent =
165 loadTimeData.getString('please_wait');
166 $('paymentForm').classList.add('hidden');
167 $('finalStatus').classList.add('hidden');
168 this.setCarrierPage_(MobileSetup.ACTIVATION_PAGE_URL);
169 $('systemStatus').classList.remove('hidden');
170 $('canvas').classList.remove('hidden');
171 this.startSpinner_();
173 case MobileSetup.PLAN_ACTIVATION_TRYING_OTASP:
174 case MobileSetup.PLAN_ACTIVATION_INITIATING_ACTIVATION:
175 case MobileSetup.PLAN_ACTIVATION_OTASP:
176 // Activation page should not be shown for the simple activation flow.
177 if (simpleActivationFlow)
180 $('statusHeader').textContent =
181 loadTimeData.getString('activating_header');
182 $('auxHeader').textContent =
183 loadTimeData.getString('please_wait');
184 $('paymentForm').classList.add('hidden');
185 $('finalStatus').classList.add('hidden');
186 this.setCarrierPage_(MobileSetup.ACTIVATION_PAGE_URL);
187 $('systemStatus').classList.remove('hidden');
188 $('canvas').classList.remove('hidden');
189 this.startSpinner_();
191 case MobileSetup.PLAN_ACTIVATION_PAYMENT_PORTAL_LOADING:
192 // Activation page should not be shown for the simple activation flow.
193 if (!simpleActivationFlow) {
194 $('statusHeader').textContent =
195 loadTimeData.getString('connecting_header');
196 $('auxHeader').textContent = '';
197 $('paymentForm').classList.add('hidden');
198 $('finalStatus').classList.add('hidden');
199 this.setCarrierPage_(MobileSetup.ACTIVATION_PAGE_URL);
200 $('systemStatus').classList.remove('hidden');
201 $('canvas').classList.remove('hidden');
203 this.loadPaymentFrame_(deviceInfo);
205 case MobileSetup.PLAN_ACTIVATION_WAITING_FOR_CONNECTION:
206 var statusHeaderText;
208 if (deviceInfo.activation_type == 'NonCellular') {
209 statusHeaderText = loadTimeData.getString(
210 'portal_unreachable_header');
211 carrierPage = MobileSetup.PORTAL_OFFLINE_PAGE_URL;
212 } else if (deviceInfo.activation_type == 'OTA') {
214 loadTimeData.getString('connecting_header');
215 carrierPage = MobileSetup.ACTIVATION_PAGE_URL;
217 $('statusHeader').textContent = statusHeaderText;
218 $('auxHeader').textContent = '';
219 $('auxHeader').classList.add('hidden');
220 $('paymentForm').classList.add('hidden');
221 $('finalStatus').classList.add('hidden');
222 $('systemStatus').classList.remove('hidden');
223 this.setCarrierPage_(carrierPage);
224 $('canvas').classList.remove('hidden');
225 this.startSpinner_();
227 case MobileSetup.PLAN_ACTIVATION_SHOWING_PAYMENT:
228 $('statusHeader').textContent = '';
229 $('auxHeader').textContent = '';
230 $('finalStatus').classList.add('hidden');
231 $('systemStatus').classList.add('hidden');
232 $('paymentForm').classList.remove('hidden');
233 $('canvas').classList.add('hidden');
235 this.paymentShown_ = true;
237 case MobileSetup.PLAN_ACTIVATION_DONE:
238 $('statusHeader').textContent = '';
239 $('auxHeader').textContent = '';
240 $('finalHeader').textContent =
241 loadTimeData.getString('completed_header');
242 $('finalMessage').textContent =
243 loadTimeData.getString('completed_text');
244 $('systemStatus').classList.add('hidden');
245 $('closeButton').classList.remove('hidden');
246 $('finalStatus').classList.remove('hidden');
247 $('canvas').classList.add('hidden');
248 $('closeButton').classList.toggle('hidden', !this.paymentShown_);
249 $('paymentForm').classList.toggle('hidden', !this.paymentShown_);
252 case MobileSetup.PLAN_ACTIVATION_ERROR:
253 $('statusHeader').textContent = '';
254 $('auxHeader').textContent = '';
255 $('finalHeader').textContent =
256 loadTimeData.getString('error_header');
257 $('finalMessage').textContent = deviceInfo.error;
258 $('systemStatus').classList.add('hidden');
259 $('canvas').classList.add('hidden');
260 $('closeButton').classList.toggle('hidden', !this.paymentShown_);
261 $('paymentForm').classList.toggle('hidden', !this.paymentShown_);
262 $('finalStatus').classList.remove('hidden');
266 this.state_ = newState;
269 setCarrierPage_: function(url) {
270 if (this.carrierPageUrl_ == url)
272 this.carrierPageUrl_ = url;
273 $('carrierPage').contentWindow.location.href = url;
276 updateDeviceStatus_: function(deviceInfo) {
277 this.changeState_(deviceInfo);
280 portalFrameLoadError_: function(errorCode) {
281 if (this.frameLoadIgnored_)
283 console.log('Portal frame load error detected: ', errorCode);
284 this.frameLoadError_ = errorCode;
287 portalFrameLoadCompleted_: function() {
288 if (this.frameLoadIgnored_)
290 console.log('Portal frame load completed!');
291 this.onFrameLoaded_(this.frameLoadError_ == 0);
294 sendPostFrame_: function(frameUrl) {
295 var msg = { type: 'postFrame' };
296 $(this.frameName_).contentWindow.postMessage(msg, frameUrl);
299 sendDeviceInfo_: function() {
301 type: 'deviceInfoMsg',
302 domain: document.location,
304 'carrier': this.deviceInfo_.carrier,
305 'MEID': this.deviceInfo_.MEID,
306 'IMEI': this.deviceInfo_.IMEI,
307 'MDN': this.deviceInfo_.MDN
310 $(this.frameName_).contentWindow.postMessage(msg,
311 this.deviceInfo_.payment_url);
316 MobileSetup.drawProgress = function() {
317 var ctx = canvas.getContext('2d');
318 ctx.clearRect(0, 0, canvas.width, canvas.height);
320 var segmentCount = Math.min(12, canvas.width / 1.6); // Number of segments
321 var rotation = 0.75; // Counterclockwise rotation
323 // Rotate canvas over time
324 ctx.translate(canvas.width / 2, canvas.height / 2);
325 ctx.rotate(Math.PI * 2 / (segmentCount + rotation));
326 ctx.translate(-canvas.width / 2, -canvas.height / 2);
328 var gap = canvas.width / 24; // Gap between segments
329 var oRadius = canvas.width / 2; // Outer radius
330 var iRadius = oRadius * 0.618; // Inner radius
331 var oCircumference = Math.PI * 2 * oRadius; // Outer circumference
332 var iCircumference = Math.PI * 2 * iRadius; // Inner circumference
333 var oGap = gap / oCircumference; // Gap size as fraction of outer ring
334 var iGap = gap / iCircumference; // Gap size as fraction of inner ring
335 var oArc = Math.PI * 2 * (1 / segmentCount - oGap); // Angle of outer arcs
336 var iArc = Math.PI * 2 * (1 / segmentCount - iGap); // Angle of inner arcs
338 for (i = 0; i < segmentCount; i++) { // Draw each segment
339 var opacity = Math.pow(1.0 - i / segmentCount, 3.0);
340 opacity = (0.15 + opacity * 0.8); // Vary from 0.15 to 0.95
341 var angle = - Math.PI * 2 * i / segmentCount;
344 ctx.arc(canvas.width / 2, canvas.height / 2, oRadius,
345 angle - oArc / 2, angle + oArc / 2, false);
346 ctx.arc(canvas.width / 2, canvas.height / 2, iRadius,
347 angle + iArc / 2, angle - iArc / 2, true);
349 ctx.fillStyle = 'rgba(240, 30, 29, ' + opacity + ')';
354 MobileSetup.deviceStateChanged = function(deviceInfo) {
355 MobileSetup.getInstance().updateDeviceStatus_(deviceInfo);
358 MobileSetup.portalFrameLoadError = function(errorCode) {
359 MobileSetup.getInstance().portalFrameLoadError_(errorCode);
362 MobileSetup.portalFrameLoadCompleted = function() {
363 MobileSetup.getInstance().portalFrameLoadCompleted_();
366 MobileSetup.loadPage = function() {
367 mobile.MobileSetup.getInstance().initialize('paymentForm',
368 mobile.MobileSetup.ACTIVATION_PAGE_URL);
373 MobileSetup: MobileSetup
377 document.addEventListener('DOMContentLoaded', mobile.MobileSetup.loadPage);