AppUpdate

The AppUpdate plugin is used to enable the contents in the www folder (HTML, JavaScript, CSS, images, etc.) of a deployed Kapsel app and its config.xml to be updated. As an example, a new project named AppUpdateDemo will be created and used to demonstrate how to update a deployed app. A Kapsel app with the AppUpdate plugin installed will check with the SMP 3.0 server on startup to see if there is a newer revision of the app available. The revision number of the Kapsel app is automatically incremented with each deployment to the SMP 3.0 server. If the revision on the server is higher than the revision in the config.xml on the device or simulator, then a notification is shown asking the user if they wish to accept the update.

For additional details see C:\SAP\MobileSDK3\KapselSDK\docs\api\sap.AppUpdate.html or Using the AppUpdate Plugin.

The following steps will demonstrate this plugin.

  • Add the AppUpdate plugin and optionally the logger plugin if you wish to view the messages logged by the AppUpdate plugin. The log level is set to debug if sap.Logger is detected on line 12 of index.html.
    cd C:\Kapsel_Projects\KapselGSDemo
    cordova plugin add kapsel-plugin-appupdate --searchpath %KAPSEL_HOME%/plugins
    cordova plugin add kapsel-plugin-logger --searchpath %KAPSEL_HOME%/plugins
    
    or on a Mac
    
    cd ~/Documents/Kapsel_Projects/KapselGSDemo
    cordova plugin add kapsel-plugin-appupdate --searchpath $KAPSEL_HOME/plugins
    cordova plugin add kapsel-plugin-logger --searchpath $KAPSEL_HOME/plugins
    
  • Replace the contents of C:\Kapsel_Projects\KapselGSDemo\www\index.html with the contents below.
    <html>
        <head>
            <script type="text/javascript" charset="utf-8" src="datajs-1.1.2.min.js"></script>
            <script type="text/javascript" charset="utf-8" src="serverContext.js"></script>
            <script type="text/javascript" charset="utf-8" src="cordova.js"></script>
    
            <style type="text/css">
                body {
                    background-color: yellow;
                }
            </style>
            <script>
                window.onerror = onError;
                
                function onError(msg, url, line) {
                    if (!url) { 
                        console.log("An unknown error occurred");
                        return false;
                    }
                    var idx = url.lastIndexOf("/"); 
                    var file = "unknown";
                    if (idx > -1) {
                        file = url.substring(idx + 1);
                    }
                    alert("An error occurred in " + file + " (at line # " + line + "): " + msg);
                    var r =  confirm("Check to see if an update is available?");
                    if (r == true) {
                        sap.Logon.init(function() { }, function() { alert("Logon Failed"); }, appId);
                        sap.AppUpdate.update();
                    }
                    
                    return false; //suppressErrorAlert;
                };
    
                function init() {
                    if (sap.Logger) {
                        sap.Logger.setLogLevel(sap.Logger.DEBUG);  //enables the display of debug log messages from the Kapsel plugins.
                        sap.Logger.debug("Log level set to DEBUG");
                    }
                    
                    sap.AppUpdate.addEventListener("checking", function(e) {
                        console.log("Checking for update");
                    });
                    
                    sap.AppUpdate.addEventListener("noupdate", function(e) {
                        console.log("No update");
                    });
    
                    sap.AppUpdate.addEventListener("downloading", function(e) {
                        console.log("Downloading update");
                    });
                    
                    //SP02 New Feature
                    sap.AppUpdate.addEventListener("progress", function(e) {
                        if (e.lengthComputable) {
                            var percent = Math.round(e.loaded / e.total * 100);
                            //console.log("Progress " + percent);
                            document.getElementById('statusLabel').innerHTML = "Download progress " + percent + "%";
                        }
                    });
                    
                    
                    sap.AppUpdate.addEventListener("error", function(e) {
                        console.log("Error downloading update. statusCode: " + e.statusCode + " statusMessage: " + e.statusMessage);
                    });
                    
                    /*  //Notice that addEventListener adds the function to the chain of functions that are notified. 
                    sap.AppUpdate.addEventListener("updateready", function(e) {
                        console.log("Update ready");
                    }); 
                    */
    
                    //Notice here that we are overriding the default handler for the updateready event
                    sap.AppUpdate.onupdateready = function() {
                        console.log("Confirming application update");
                        document.getElementById('statusLabel').innerHTML = "";
                        navigator.notification.confirm("New update available",
                            function(buttonIndex) {
                                if (buttonIndex === 2) {
                                    console.log("Applying application update");
                                    sap.AppUpdate.reloadApp();
                                } 
                            }, 
                            "Update", ["Later", "Relaunch Now"]);
                    };
    
                    sap.Logon.init(function() { }, function() { alert("Logon Failed"); }, appId, context);
                }
    
                document.addEventListener("deviceready", init, false);
            </script>
        </head>
        <body>
            <h1>AppUpdate Sample</h1>
            <label id="statusLabel"></label>
        </body>
    </html>
    
  • Optionally modify the KapselGSDemo\config.xml and add
    <preference name="hybridapprevision" value="0" />
    <preference name="sap-development-version" value="1.2.3" />

    Also update the description and email fields if desired.

    Due to a long delay on Android while assets are being copied during the first app update, it is recommended to either not specify hybridapprevision or use value=”0″. When hybridapprevision is not present or set to 0, during the first client update, the client will request the server to send the complete contents of the www folder rather than just the files that changed between the revision that is reported on the client and the revision on the server.

  • Prepare, build and deploy the app with the following command.
    cordova run android
    or
    cordova run ios
  • In the All Output tab in Xcode or in the Android Device Monitor (monitor.bat), under the LogCat view, the debug output from the AppUpdate plugin can be seen. Note that the LogCat view provides a feature to filter messages. The filter shown below is based on the tag SMP_APP_UPDATE.

    Notice above that when the app starts, the AppUpdate plugin checks with the SMP server to see if there is a newer revision of the app. The current revision of the app is appRevision 0. There is not a newer deployed version of the app on the server so a message No update available is logged.
  • The Kapsel command line interface (CLI) provides a way to generate a zip file that contains the HTML files that make up the app. This zip file can then either be uploaded to the SMP 3.0 server using the Management Cockpit or it can be uploaded using the Kapsel command line interface. Open a command prompt or shell to the folder
    C:\SAP\MobileSDK3\KapselSDK\cli
    
     ~/SAP/MobileSDK3/KapselSDK/cli

    Run

    npm install -g

    On a Mac it may be necessary to run

    sudo npm -g install
  • Change directories to the directory containing the project and run the below commands to generate a zip file containing the HTML files that make up the application and then upload the zip file to the SMP 3.0 server.
    kapsel package
    kapsel deploy com.kapsel.gs localhost:8083 smpAdmin s3pAdmin
    

    Note, if the deploy command fails with the following error {“errorCode”:”KAPSEL_APP_ARCHIVE_APPLICATION_CONTENT_VALIDATION_FAIL”}, this may be due to a check that the SMP server makes on icon elements. It attempts to validate the config.xml and notices that the referenced icon does not exist in the zip file. Icons cannot be updated by the appupdate plugin so as a workaround comment out the icon resources from the config.xml or update the default ones rather than specifing them in the config.xml.

    Note, if the deploy command fails with the following error, try adjusting the no_proxy environment variable to exclude the host being deployed to.

    Failed to fetch CSRF token
    1%ERROR: Error: tunneling socket could not be established, cause=Parse Error
    

    The package command can optionally take a platform such as android or iOS. The parameters to the deploy command are the app ID, the SMP 3.0 server host name, the user id, and password for the SMP 3.0 server. After running this command the Management Cockpit will show that revision 1 has been uploaded to the server.

  • Close and reopen the app. This time when the AppUpdate plugin will see that revision 1 is available.
  • Notice above that the response also indicates where to download the app from. If you are using a reverse proxy server between the devices and the SMP server, you may need to include a rule for the reverse proxy to rewrite this address. See also Apache Module mod_substitute. In the below example the apache reverse proxy server is at smpqa12-03.sybase.com and the SMP server is installed on smpqa-win12-03.sybase.com:8080.
    <Location >
       AddOutputFilterByType SUBSTITUTE application/json
       Substitute "s|smpqa-win12-03.sybase.com:8080|smpqa12-03.sybase.com|ni"
    </Location>
    

    The www folder on the Android device or simulator is read only and cannot be updated directly, so the first app update causes the entire contents of the www folder to be copied to a new location causing the size of the deployed application to increase. Updates after the first update should be proportional to the size of the new files being added.

    At this point the version of the application on the device is the same as the version on the server.

  • Update C:\Kapsel_Projects\KapselGSDemo\www\index.html and change the body’s background-color to some other color such as silver.
    <style type="text/css">
      body {
        background-color: silver;
      }
    </style>
    
  • Copy the contents of the www folder to the platform specific www folder of the project using prepare.
    cordova prepare
    
  • Deploy a new version of app to the SMP 3.0 server.
    kapsel package
    kapsel deploy com.kapsel.gs localhost:8083 smpAdmin s3pAdmin

    Close and reopen the KapselGSDemo app.

    The debug output from the AppUpdate plugin shows that the client has revision 1 and the server has revision 2. It also lists the changed files which in this case is index.html.

  • Note that only the changed index.html file is sent from the SMP server to the device.
  • Note, rather than having the end user ok the update, the update can happen without the user being asked to ok it by overriding the sap.AppUpdate.onupdateready event.
    // Override default handler
    sap.AppUpdate.onupdateready = function(e) {
       // No notification just reload
       console.log("Apply update");
       sap.AppUpdate.reloadApp();
    }
    

    See comments in appupdate.js for further details.

Staging Changes

  • There is an option to deploy a revision to a select set of test users. The following steps demonstrate this.
  • Make another change to index.html to change the background-color to be green.
  • Package the change and this time use the upload command rather than the deploy command.
    cordova prepare
    kapsel package
    kapsel upload com.kapsel.gs localhost:8083 smpAdmin s3pAdmin
    kapsel status com.kapsel.gs localhost:8083 smpAdmin s3pAdmin
    

    Notice that the new version appears. From the kapsel status command, it is reported as revision -1

    Stage the application so that it becomes available to test users.

    kapsel stage com.kapsel.gs android -1 localhost:8083 smpAdmin s3pAdmin
    

    Close and reopen the app. Notice that no update is available.

    Set the Is Tester flag for the registered user as shown below.

    Close and reopen the app and notice that the staged version of the app is downloaded.

Testing Changes before Deploying

  • Note, it is important to test the changes before publishing them to the server. If an error is introduced, it could cause the application to break and potentially prevent future updates from being downloaded. You can also validate your JavaScript with tools like JSLint, or JSHint. CSS can be validated with CSS Lint. If you encounter a problem after an update has occurred and you are using an iOS simulator, the contents of the update can be viewed at the following location.
    /Users/user_name/Library/Developer/CoreSimulator/Devices/602C969F-F0CD-4043-AFBF-583C62F5A8BA/data/Containers/Data/Application/97CE6DBB-50F1-451E-A503-B5E79F4C7302/Documents/SMP/appupdate/app/www
    

    Note, the above location can be seen as the first line in the Xcode All Output window.

    If using an Android emulator, the updated files can be viewed at the following location.

    data\data\com.kapsel.gs\files\download\www
    

    One technique to aid with this problem is to open an error page that can check for new updates if an error is detected.
    Replace the contents of C:\Kapsel_Projects\KapselGSDemo\www\index.html with following contents.

    <html>
        <head>
            <script src="cordova.js"></script>
            <script type="text/javascript" charset="utf-8" src="serverContext.js"></script>
            <style type="text/css">
                body {
                    background-color: silver;
                }
            </style>
            <script>
                window.onerror = onError;
                
                //callNonExistantMethod();  //Introduce an error that can be recovered by deploying a new version to the SMP 3.0 server
                //window.location.replace("doesNotExist.html");  //Introduce an error that can only be recovered on Android by the errorUrl config parameter.  Requires the app to be force closed before it can be opened again after receiving the download
                
                function onError(msg, url, line) {
                    if (!url) {
                        console.log("An unknown error occurred");
                        return false;
                    }
                    var idx = url.lastIndexOf("/");
                    var file = "unknown";
                    if (idx > -1) {
                        file = url.substring(idx + 1);
                    }
                    alert("An error occurred in " + file + " (at line # " + line + "): " + msg);
                    
                    window.location.replace("error.html");
                    //var ref = window.open("./error.html", "_blank", "location=no");
                    
                    return false; //suppressErrorAlert;
                };
            
            function init() {
                if (sap.Logger) {
                    sap.Logger.setLogLevel(sap.Logger.DEBUG);  //enables the display of debug log messages from the Kapsel plugins.
                    sap.Logger.debug("Log level set to DEBUG");
                }
                
                sap.Logon.init(function() { }, function() { alert("Logon Failed"); }, appId, context);
            }
            
            document.addEventListener("deviceready", init, false);
                </script>
        </head>
        <body>
            <h1>AppUpdate Sample</h1>
        </body>
    </html>
    

    Create a file named error.html and copy in the following contents.

    <html>
        <head>
            <script type="text/javascript" charset="utf-8" src="cordova.js"></script>
            <script type="text/javascript" charset="utf-8" src="serverContext.js"></script>
            <style type="text/css">
                body {
                    background-color: goldenrod;
                }
            </style>
        </head>
        <body>
            <h1>AppUpdate Sample</h1>
            If you are here something bad happened.  Sorry about that!
            <p>
            If this is the first time you are seeing this, please call the help desk: <a href="tel:8005551212">(800)555-1212</a>
            <p>
            The problem may have already been fixed.  Click the update button to see if there is a fix.
            <input id="updateCheck" type="button" value="Check For Update" onclick="checkForError()" />
            <p>
            <p>
            <label id="statusLabel"></label>
        </body>
        <script>
            logonView = null;
            logon = null;
            applicationContext = null;
            
            function checkForError() {
                var appId = "com.kapsel.gs";  // Change this to app id on server
                
                sap.AppUpdate.onchecking = function() {
                    console.log("AppUpdate_checking");
                };
                
                sap.AppUpdate.ondownloading = function() {
                    console.log("AppUpdate_downloading");
                }
                
                sap.AppUpdate.onnoupdate = function() {
                    console.log("AppUpdate_noupdate");
                }
                
                sap.AppUpdate.onupdateready = function() {
                    //No notification just reload
                    console.log("AppUpdate_onupdateready");
                    sap.AppUpdate.reloadApp(); // Go back to the application
                    window.location.replace("index.html");
                };
                
                sap.AppUpdate.onerror = function() {
                    console.log("AppUpdate_error");
                }
                
                sap.Logon.init(function() {sap.AppUpdate.update()}, function() { alert("Logon Failed"); }, appId, context);
            }
        </script>
    </html>
    
    

    Line 12 can be uncommented which makes a call to a non-existent JavaScript method. The app that now has an error in it can be packaged and deployed to the SMP 3.0 server. When the app is reopened, the newer version is uploaded and the error handler detects an error and redirects to the error page.

    Correct the error, prepare, package and deploy the app.

    cordova prepare && kapsel package
    kapsel deploy com.kapsel.gs localhost:8083 smpAdmin s3pAdmin
    

    Now press check for updates and notice that app has recovered from the error.

Back to Getting Started With Kapsel

To report this post you need to login first.

1 Comment

You must be Logged on to comment or reply to a post.

Leave a Reply