Home | History | Annotate | Download | only in chromeos
      1 // Copyright 2013 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 cr.define('mobile', function() {
      6 
      7   // TODO(tbarzic): Share code with mobile_setup.js.
      8   var EXTENSION_BASE_URL =
      9       'chrome-extension://iadeocfgjdjdmpenejdbfeaocpbikmab/';
     10   var REDIRECT_POST_PAGE_URL = EXTENSION_BASE_URL + 'redirect.html?autoPost=1';
     11   var PORTAL_OFFLINE_PAGE_URL = EXTENSION_BASE_URL + 'portal_offline.html';
     12   var INVALID_DEVICE_INFO_PAGE_URL =
     13       EXTENSION_BASE_URL + 'invalid_device_info.html';
     14 
     15   var NetworkState = {
     16     UNKNOWN: 0,
     17     PORTAL_REACHABLE: 1,
     18     PORTAL_UNREACHABLE: 2
     19   };
     20 
     21   var CarrierPageType = {
     22     NOT_SET: 0,
     23     PORTAL_OFFLINE: 1,
     24     INVALID_DEVICE_INFO: 2
     25   };
     26 
     27   var localStrings = new LocalStrings();
     28 
     29   function PortalImpl() {
     30     // Mobile device information.
     31     this.deviceInfo_ = null;
     32     this.spinnerInt_ = -1;
     33     this.networkState_ = NetworkState.UNKNOWN;
     34     this.portalFrameSet_ = false;
     35     this.carrierPageType_ = CarrierPageType.NOT_SET;
     36   }
     37 
     38   cr.addSingletonGetter(PortalImpl);
     39 
     40   PortalImpl.prototype = {
     41     initialize: function() {
     42       // Get network device info for which portal should be opened.
     43       // For LTE networks, this will also start observing network connection
     44       // state and raise |updatePortalReachability| messages when the portal
     45       // reachability changes.
     46       chrome.send('getDeviceInfo');
     47     },
     48 
     49     updateDeviceInfo: function(deviceInfo) {
     50       this.deviceInfo_ = deviceInfo;
     51       this.updateState_();
     52     },
     53 
     54     updateNetworkState: function(networkState) {
     55       if (this.networkState_ == networkState)
     56         return;
     57       this.networkState_ = networkState;
     58 
     59       // If the device info is not yet set, the state will be updated on the
     60       // device info update.
     61       if (this.deviceInfo_)
     62         this.updateState_();
     63     },
     64 
     65     updateState_: function() {
     66       if (!this.deviceInfo_ || this.networkState_ == NetworkState.UNKNOWN)
     67         return;
     68 
     69       if (!this.isDeviceInfoValid_()) {
     70         // If the device info is not valid, hide portalFrame and show system
     71         // status displaying 'invalid device info' page.
     72         this.setCarrierPage_(CarrierPageType.INVALID_DEVICE_INFO);
     73         $('portalFrame').hidden = true;
     74         $('systemStatus').hidden = false;
     75       } else if (this.networkState_ != NetworkState.PORTAL_REACHABLE) {
     76         // If the portal is not reachable, hide portalFrame and show system
     77         // status displaying 'offline portal' page.
     78         this.setCarrierPage_(CarrierPageType.PORTAL_OFFLINE);
     79         $('portalFrame').hidden = true;
     80         $('systemStatus').hidden = false;
     81      } else {
     82         // If the portal is reachable and device info is valid, set and show
     83         // portalFrame; and hide system status displaying 'offline portal' page.
     84         this.setPortalFrameIfNeeded_(this.deviceInfo_);
     85         $('portalFrame').hidden = false;
     86         $('systemStatus').hidden = true;
     87         this.stopSpinner_();
     88       }
     89     },
     90 
     91     setCarrierPage_: function(type) {
     92       // The page is already set, nothing to do.
     93       if (type == this.carrierPageType_)
     94         return;
     95 
     96       switch (type) {
     97         case CarrierPageType.PORTAL_OFFLINE:
     98           $('carrierPage').contentWindow.location.href =
     99               PORTAL_OFFLINE_PAGE_URL;
    100           $('statusHeader').textContent =
    101               localStrings.getString('portal_unreachable_header');
    102           this.startSpinner_();
    103           break;
    104         case CarrierPageType.INVALID_DEVICE_INFO:
    105           $('carrierPage').contentWindow.location.href =
    106               INVALID_DEVICE_INFO_PAGE_URL;
    107           $('statusHeader').textContent =
    108               localStrings.getString('invalid_device_info_header');
    109           this.stopSpinner_();
    110           break;
    111         case CarrierPageType.NOT_SET:
    112           $('carrierPage').contentWindow.location.href = 'about:blank';
    113           $('statusHeader').textContent = '';
    114           this.stopSpinner_();
    115           break;
    116         default:
    117           break;
    118       }
    119 
    120       this.carrierPageType_ = type;
    121     },
    122 
    123     setPortalFrameIfNeeded_: function(deviceInfo) {
    124       // The portal should be set only once.
    125       if (this.portalFrameSet_)
    126         return;
    127 
    128       var postData = '';
    129       if (deviceInfo.post_data && deviceInfo.post_data.length)
    130         postData = '&post_data=' + encodeURIComponent(deviceInfo.post_data);
    131 
    132       $('portalFrame').contentWindow.location.href = REDIRECT_POST_PAGE_URL +
    133           postData + '&formUrl=' + encodeURIComponent(deviceInfo.payment_url);
    134 
    135       this.portalFrameSet_ = true;
    136     },
    137 
    138     isDeviceInfoValid_: function() {
    139       // Device info is valid if it has mdn which doesn't contain only '0's.
    140       return this.deviceInfo_ && this.deviceInfo_.MDN &&
    141           this.deviceInfo_.MDN.match('[^0]');
    142     },
    143 
    144     startSpinner_: function() {
    145       this.stopSpinner_();
    146       this.spinnerInt_ = setInterval(this.drawProgress_.bind(this), 100);
    147     },
    148 
    149     stopSpinner_: function() {
    150       if (this.spinnerInt_ != -1) {
    151         clearInterval(this.spinnerInt_);
    152         this.spinnerInt_ = -1;
    153       }
    154       // Clear the spinner canvas.
    155       var ctx = canvas.getContext('2d');
    156       ctx.clearRect(0, 0, canvas.width, canvas.height);
    157     },
    158 
    159     drawProgress_: function() {
    160       var ctx = canvas.getContext('2d');
    161       ctx.clearRect(0, 0, canvas.width, canvas.height);
    162 
    163       var segmentCount = Math.min(12, canvas.width / 1.6); // Number of segments
    164       var rotation = 0.75; // Counterclockwise rotation
    165 
    166       // Rotate canvas over time
    167       ctx.translate(canvas.width / 2, canvas.height / 2);
    168       ctx.rotate(Math.PI * 2 / (segmentCount + rotation));
    169       ctx.translate(-canvas.width / 2, -canvas.height / 2);
    170 
    171       var gap = canvas.width / 24; // Gap between segments
    172       var oRadius = canvas.width / 2; // Outer radius
    173       var iRadius = oRadius * 0.618; // Inner radius
    174       var oCircumference = Math.PI * 2 * oRadius; // Outer circumference
    175       var iCircumference = Math.PI * 2 * iRadius; // Inner circumference
    176       var oGap = gap / oCircumference; // Gap size as fraction of  outer ring
    177       var iGap = gap / iCircumference; // Gap size as fraction of  inner ring
    178       var oArc = Math.PI * 2 * (1 / segmentCount - oGap); // Angle of outer arcs
    179       var iArc = Math.PI * 2 * (1 / segmentCount - iGap); // Angle of inner arcs
    180 
    181       for (i = 0; i < segmentCount; i++) { // Draw each segment
    182         var opacity = Math.pow(1.0 - i / segmentCount, 3.0);
    183         opacity = (0.15 + opacity * 0.8); // Vary from 0.15 to 0.95
    184         var angle = - Math.PI * 2 * i / segmentCount;
    185 
    186         ctx.beginPath();
    187         ctx.arc(canvas.width / 2, canvas.height / 2, oRadius,
    188             angle - oArc / 2, angle + oArc / 2, false);
    189         ctx.arc(canvas.width / 2, canvas.height / 2, iRadius,
    190             angle + iArc / 2, angle - iArc / 2, true);
    191         ctx.closePath();
    192         ctx.fillStyle = 'rgba(240, 30, 29, ' + opacity + ')';
    193         ctx.fill();
    194       }
    195     }
    196   };
    197 
    198   function MobileSetupPortal() {}
    199 
    200   MobileSetupPortal.loadPage = function() {
    201     PortalImpl.getInstance().initialize();
    202   };
    203 
    204   MobileSetupPortal.onGotDeviceInfo = function(deviceInfo) {
    205     PortalImpl.getInstance().updateDeviceInfo(deviceInfo);
    206   };
    207 
    208   MobileSetupPortal.onConnectivityChanged = function(portalReachable) {
    209     PortalImpl.getInstance().updateNetworkState(
    210         portalReachable ? NetworkState.PORTAL_REACHABLE :
    211                           NetworkState.PORTAL_UNREACHABLE);
    212   };
    213 
    214   // Export
    215   return {
    216     MobileSetupPortal: MobileSetupPortal
    217   };
    218 });
    219 
    220 document.addEventListener('DOMContentLoaded',
    221                           mobile.MobileSetupPortal.loadPage);
    222