Home | History | Annotate | Download | only in chromeos
      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.
      4 
      5 
      6 cr.define('mobile', function() {
      7 
      8   function MobileSetup() {
      9   }
     10 
     11   cr.addSingletonGetter(MobileSetup);
     12 
     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;
     28 
     29   MobileSetup.EXTENSION_PAGE_URL =
     30       'chrome-extension://iadeocfgjdjdmpenejdbfeaocpbikmab';
     31   MobileSetup.ACTIVATION_PAGE_URL = MobileSetup.EXTENSION_PAGE_URL +
     32                                     '/activation.html';
     33   MobileSetup.PORTAL_OFFLINE_PAGE_URL = MobileSetup.EXTENSION_PAGE_URL +
     34                                         '/portal_offline.html';
     35   MobileSetup.REDIRECT_POST_PAGE_URL = MobileSetup.EXTENSION_PAGE_URL +
     36                                        '/redirect.html';
     37 
     38   MobileSetup.prototype = {
     39     // Mobile device information.
     40     deviceInfo_: null,
     41     frameName_: '',
     42     initialized_: false,
     43     fakedTransaction_: false,
     44     paymentShown_: false,
     45     frameLoadError_: 0,
     46     frameLoadIgnored_: true,
     47     carrierPageUrl_: null,
     48     spinnerInt_: -1,
     49     // UI states.
     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',
     57 
     58     initialize: function(frame_name, carrierPage) {
     59       if (this.initialized_) {
     60         console.log('calling initialize() again?');
     61         return;
     62       }
     63       this.initialized_ = true;
     64       self = this;
     65       this.frameName_ = frame_name;
     66 
     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);
     72 
     73       window.addEventListener('message', function(e) {
     74           self.onMessageReceived_(e);
     75       });
     76 
     77       $('closeButton').addEventListener('click', function(e) {
     78         $('finalStatus').classList.add('hidden');
     79       });
     80 
     81       // Kick off activation process.
     82       chrome.send('startActivation');
     83     },
     84 
     85     startSpinner_: function() {
     86       this.stopSpinner_();
     87       this.spinnerInt_ = setInterval(mobile.MobileSetup.drawProgress, 100);
     88     },
     89 
     90     stopSpinner_: function() {
     91       if (this.spinnerInt_ != -1) {
     92         clearInterval(this.spinnerInt_);
     93         this.spinnerInt_ = -1;
     94       }
     95     },
     96 
     97     onFrameLoaded_: function(success) {
     98       chrome.send('paymentPortalLoad', [success ? 'ok' : 'failed']);
     99     },
    100 
    101     loadPaymentFrame_: function(deviceInfo) {
    102       if (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);
    111         } else {
    112           this.frameLoadIgnored_ = false;
    113           $(this.frameName_).contentWindow.location.href =
    114               deviceInfo.payment_url;
    115         }
    116       }
    117     },
    118 
    119     onMessageReceived_: function(e) {
    120       if (e.origin !=
    121               this.deviceInfo_.payment_url.substring(0, e.origin.length) &&
    122           e.origin != MobileSetup.EXTENSION_PAGE_URL)
    123         return;
    124 
    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]);
    133       }
    134     },
    135 
    136     changeState_: function(deviceInfo) {
    137       var newState = deviceInfo.state;
    138       if (this.state_ == newState)
    139         return;
    140 
    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) {
    144         return;
    145       }
    146 
    147       // Map handler state to UX.
    148       var simpleActivationFlow =
    149           (deviceInfo.activation_type == 'NonCellular' ||
    150            deviceInfo.activation_type == 'OTA');
    151       switch (newState) {
    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)
    160             break;
    161 
    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_();
    172           break;
    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)
    178             break;
    179 
    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_();
    190           break;
    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');
    202           }
    203           this.loadPaymentFrame_(deviceInfo);
    204           break;
    205         case MobileSetup.PLAN_ACTIVATION_WAITING_FOR_CONNECTION:
    206           var statusHeaderText;
    207           var carrierPage;
    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') {
    213             statusHeaderText =
    214                 loadTimeData.getString('connecting_header');
    215             carrierPage = MobileSetup.ACTIVATION_PAGE_URL;
    216           }
    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_();
    226           break;
    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');
    234           this.stopSpinner_();
    235           this.paymentShown_ = true;
    236           break;
    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_);
    250           this.stopSpinner_();
    251           break;
    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');
    263           this.stopSpinner_();
    264           break;
    265       }
    266       this.state_ = newState;
    267     },
    268 
    269     setCarrierPage_: function(url) {
    270       if (this.carrierPageUrl_ == url)
    271         return;
    272       this.carrierPageUrl_ = url;
    273       $('carrierPage').contentWindow.location.href = url;
    274     },
    275 
    276     updateDeviceStatus_: function(deviceInfo) {
    277       this.changeState_(deviceInfo);
    278     },
    279 
    280     portalFrameLoadError_: function(errorCode) {
    281       if (this.frameLoadIgnored_)
    282         return;
    283       console.log('Portal frame load error detected: ', errorCode);
    284       this.frameLoadError_ = errorCode;
    285     },
    286 
    287     portalFrameLoadCompleted_: function() {
    288       if (this.frameLoadIgnored_)
    289         return;
    290       console.log('Portal frame load completed!');
    291       this.onFrameLoaded_(this.frameLoadError_ == 0);
    292     },
    293 
    294     sendPostFrame_: function(frameUrl) {
    295       var msg = { type: 'postFrame' };
    296       $(this.frameName_).contentWindow.postMessage(msg, frameUrl);
    297     },
    298 
    299     sendDeviceInfo_: function() {
    300       var msg = {
    301         type: 'deviceInfoMsg',
    302         domain: document.location,
    303         payload: {
    304           'carrier': this.deviceInfo_.carrier,
    305           'MEID': this.deviceInfo_.MEID,
    306           'IMEI': this.deviceInfo_.IMEI,
    307           'MDN': this.deviceInfo_.MDN
    308         }
    309       };
    310       $(this.frameName_).contentWindow.postMessage(msg,
    311           this.deviceInfo_.payment_url);
    312     }
    313 
    314   };
    315 
    316   MobileSetup.drawProgress = function() {
    317     var ctx = canvas.getContext('2d');
    318     ctx.clearRect(0, 0, canvas.width, canvas.height);
    319 
    320     var segmentCount = Math.min(12, canvas.width / 1.6); // Number of segments
    321     var rotation = 0.75; // Counterclockwise rotation
    322 
    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);
    327 
    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
    337 
    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;
    342 
    343       ctx.beginPath();
    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);
    348       ctx.closePath();
    349       ctx.fillStyle = 'rgba(240, 30, 29, ' + opacity + ')';
    350       ctx.fill();
    351     }
    352   };
    353 
    354   MobileSetup.deviceStateChanged = function(deviceInfo) {
    355     MobileSetup.getInstance().updateDeviceStatus_(deviceInfo);
    356   };
    357 
    358   MobileSetup.portalFrameLoadError = function(errorCode) {
    359     MobileSetup.getInstance().portalFrameLoadError_(errorCode);
    360   };
    361 
    362   MobileSetup.portalFrameLoadCompleted = function() {
    363     MobileSetup.getInstance().portalFrameLoadCompleted_();
    364   };
    365 
    366   MobileSetup.loadPage = function() {
    367     mobile.MobileSetup.getInstance().initialize('paymentForm',
    368         mobile.MobileSetup.ACTIVATION_PAGE_URL);
    369   };
    370 
    371   // Export
    372   return {
    373     MobileSetup: MobileSetup
    374   };
    375 });
    376 
    377 document.addEventListener('DOMContentLoaded', mobile.MobileSetup.loadPage);
    378