You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by br...@apache.org on 2014/01/27 17:04:16 UTC

[1/6] git commit: Making Serve and CRX downloading work more robustly.

Updated Branches:
  refs/heads/master aeff680d1 -> 5156ee43a


Making Serve and CRX downloading work more robustly.


Project: http://git-wip-us.apache.org/repos/asf/cordova-app-harness/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-app-harness/commit/9f0189a5
Tree: http://git-wip-us.apache.org/repos/asf/cordova-app-harness/tree/9f0189a5
Diff: http://git-wip-us.apache.org/repos/asf/cordova-app-harness/diff/9f0189a5

Branch: refs/heads/master
Commit: 9f0189a5141a102824ad80c74e160bd4f7952229
Parents: aeff680
Author: Braden Shepherdson <br...@gmail.com>
Authored: Tue Jan 21 09:59:45 2014 -0500
Committer: Braden Shepherdson <br...@gmail.com>
Committed: Tue Jan 21 09:59:45 2014 -0500

----------------------------------------------------------------------
 www/cdvah/js/AppsService.js    | 5 +++--
 www/cdvah/js/CrxInstaller.js   | 2 +-
 www/cdvah/js/ServeInstaller.js | 3 ++-
 3 files changed, 6 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-app-harness/blob/9f0189a5/www/cdvah/js/AppsService.js
----------------------------------------------------------------------
diff --git a/www/cdvah/js/AppsService.js b/www/cdvah/js/AppsService.js
index 2fc5de8..c54164e 100644
--- a/www/cdvah/js/AppsService.js
+++ b/www/cdvah/js/AppsService.js
@@ -84,8 +84,9 @@
 
             addApp : function(installerType, appUrl) {
                 var installerFactory = _installerFactories[installerType];
-                return installerFactory.createFromUrl(appUrl)
-                .then(function(installer) {
+                return initHandlers().then(function() {
+                  return installerFactory.createFromUrl(appUrl);
+                }).then(function(installer) {
                     _installers.push(installer);
                     return writeAppsJson()
                     .then(function() {

http://git-wip-us.apache.org/repos/asf/cordova-app-harness/blob/9f0189a5/www/cdvah/js/CrxInstaller.js
----------------------------------------------------------------------
diff --git a/www/cdvah/js/CrxInstaller.js b/www/cdvah/js/CrxInstaller.js
index 6637b1e..f83a3cb 100644
--- a/www/cdvah/js/CrxInstaller.js
+++ b/www/cdvah/js/CrxInstaller.js
@@ -13,7 +13,7 @@
 
         CrxInstaller.prototype.type = 'crx';
 
-        CrxInstaller.prototype._doUpdateApp = function(installPath) {
+        CrxInstaller.prototype.doUpdateApp = function(installPath) {
             var platformConfig = location.pathname.replace(/\/[^\/]*$/, '/crx_files/config.' + platformId + '.xml');
             var targetConfig = installPath + '/config.xml';
             var xhr;

http://git-wip-us.apache.org/repos/asf/cordova-app-harness/blob/9f0189a5/www/cdvah/js/ServeInstaller.js
----------------------------------------------------------------------
diff --git a/www/cdvah/js/ServeInstaller.js b/www/cdvah/js/ServeInstaller.js
index 09c6e17..aafdb7b 100644
--- a/www/cdvah/js/ServeInstaller.js
+++ b/www/cdvah/js/ServeInstaller.js
@@ -105,7 +105,8 @@
                 var files = self._cachedProjectJson.wwwFileList;
                 files = files.filter(function(f) {
                     // Don't download cordova.js or plugins. We want to use the version bundled with the harness.
-                    var isPlugin = /\/cordova(?:_plugins)?.js$|^\/plugins\//.exec(f.path);
+                    // Do download cordova_plugins.js, since we need that to compare plugins with the harness.
+                    var isPlugin = /\/cordova\.js$|^\/plugins\//.exec(f.path);
                     var haveAlready = self._assetManifest[f.path] == f.etag;
                     return (!isPlugin && !haveAlready);
                 });


[2/6] git commit: Stop the context menu from injecting multiple times.

Posted by br...@apache.org.
Stop the context menu from injecting multiple times.

Otherwise this causes an infinite loop on KitKat, since loading more
scripts triggers onPageFinished to fire again, which triggers injection
of the context menu again, etc.


Project: http://git-wip-us.apache.org/repos/asf/cordova-app-harness/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-app-harness/commit/1245da14
Tree: http://git-wip-us.apache.org/repos/asf/cordova-app-harness/tree/1245da14
Diff: http://git-wip-us.apache.org/repos/asf/cordova-app-harness/diff/1245da14

Branch: refs/heads/master
Commit: 1245da14cf1bbbd44e8334a5d026de6ff265a86e
Parents: 9f0189a
Author: Braden Shepherdson <br...@gmail.com>
Authored: Tue Jan 21 10:00:24 2014 -0500
Committer: Braden Shepherdson <br...@gmail.com>
Committed: Tue Jan 21 10:00:24 2014 -0500

----------------------------------------------------------------------
 www/cdvah/js/ContextMenuInjectScript.js | 1 +
 1 file changed, 1 insertion(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-app-harness/blob/1245da14/www/cdvah/js/ContextMenuInjectScript.js
----------------------------------------------------------------------
diff --git a/www/cdvah/js/ContextMenuInjectScript.js b/www/cdvah/js/ContextMenuInjectScript.js
index d3c75ca..c245105 100644
--- a/www/cdvah/js/ContextMenuInjectScript.js
+++ b/www/cdvah/js/ContextMenuInjectScript.js
@@ -4,6 +4,7 @@
     /* global appIndexPlaceHolder */
     myApp.factory('ContextMenuInjectScript', [ function () {
         var toInject = function() {
+            if (window.__cordovaAppHarnessData) return; // Short-circuit if I've run on this page before.
             console.log('Menu script injected.');
             var contextScript = document.createElement('script');
             contextScript.setAttribute('src', 'app-harness:///cdvahcm/ContextMenu.js');


[4/6] git commit: Update file handling code to modern File plugin (post Ian's refactoring)

Posted by br...@apache.org.
Update file handling code to modern File plugin (post Ian's refactoring)


Project: http://git-wip-us.apache.org/repos/asf/cordova-app-harness/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-app-harness/commit/7d624328
Tree: http://git-wip-us.apache.org/repos/asf/cordova-app-harness/tree/7d624328
Diff: http://git-wip-us.apache.org/repos/asf/cordova-app-harness/diff/7d624328

Branch: refs/heads/master
Commit: 7d6243284e9db5f449596fa4efada22bebf3611e
Parents: 6f99f08
Author: Braden Shepherdson <br...@gmail.com>
Authored: Thu Jan 23 15:11:38 2014 -0500
Committer: Braden Shepherdson <br...@gmail.com>
Committed: Thu Jan 23 15:11:38 2014 -0500

----------------------------------------------------------------------
 www/cdvah/js/ResourcesLoader.js | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-app-harness/blob/7d624328/www/cdvah/js/ResourcesLoader.js
----------------------------------------------------------------------
diff --git a/www/cdvah/js/ResourcesLoader.js b/www/cdvah/js/ResourcesLoader.js
index e6faecf..8d634f3 100644
--- a/www/cdvah/js/ResourcesLoader.js
+++ b/www/cdvah/js/ResourcesLoader.js
@@ -6,8 +6,12 @@
         var rootDir;
 
         function initialiseFileSystem() {
-            // HACK: Need to discuss better way to get the root entry.
-            return $q.when(rootDir = new $window.DirectoryEntry('/', '/'));
+            var d = $q.defer();
+            $window.resolveLocalFileSystemURL('file:///', function(entry) {
+                rootDir = entry;
+                d.resolve(entry);
+            }, d.reject);
+            return d.promise;
         }
 
         //promise returns full path to downloaded file


[6/6] git commit: Only run push-related code when the plugin exists.

Posted by br...@apache.org.
Only run push-related code when the plugin exists.


Project: http://git-wip-us.apache.org/repos/asf/cordova-app-harness/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-app-harness/commit/5156ee43
Tree: http://git-wip-us.apache.org/repos/asf/cordova-app-harness/tree/5156ee43
Diff: http://git-wip-us.apache.org/repos/asf/cordova-app-harness/diff/5156ee43

Branch: refs/heads/master
Commit: 5156ee43ab4fc8c8b00d48cc2710dd9ef6600366
Parents: 018bdd1
Author: Braden Shepherdson <br...@gmail.com>
Authored: Fri Jan 24 15:10:08 2014 -0500
Committer: Braden Shepherdson <br...@gmail.com>
Committed: Mon Jan 27 11:01:27 2014 -0500

----------------------------------------------------------------------
 www/cdvah/js/ListCtrl.js | 108 +++++++++++++++++++++---------------------
 1 file changed, 55 insertions(+), 53 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-app-harness/blob/5156ee43/www/cdvah/js/ListCtrl.js
----------------------------------------------------------------------
diff --git a/www/cdvah/js/ListCtrl.js b/www/cdvah/js/ListCtrl.js
index b74c953..a73aa1f 100644
--- a/www/cdvah/js/ListCtrl.js
+++ b/www/cdvah/js/ListCtrl.js
@@ -21,7 +21,7 @@
                             notifier.error('' + e);
                         });
                     } else if (action == 'update') {
-                        // updating may take a while so we show the apps list like we normally do
+                        // Updating may take a while so we show the apps list like we normally do
                         return AppsService.updateApp(activeApp)
                         .then(function() {
                             return AppsService.launchApp(activeApp);
@@ -99,63 +99,65 @@
 
         initialise();
 
-        appharness.push.listening(function(res) {
-            $scope.listening = res;
-            $scope.$apply();
-        }, notifier.error);
+        if (appharness && appharness.push) {
+            appharness.push.listening(function(res) {
+                $scope.listening = res;
+                $scope.$apply();
+            }, notifier.error);
 
-        appharness.push.pending(function(obj) {
-            console.log('Return from pending: ' + obj);
-            if (obj && obj.type && obj.type === 'serve') {
-                AppsService.getAppList().then(function(list) {
-                    console.log(list);
-                    var matches = list && list.filter(function(x) { return x.appId == obj.name; });
-                    var promise;
-                    if (list && matches.length > 0) {
-                        // App exists.
-                        var app = matches[0];
-                        app.url = obj.url;
-                        promise = $q.when(app);
-                    } else {
-                        // New app.
-                        var handler;
-                        promise = AppsService.addApp(obj.type, obj.url).then(function(h) {
-                            handler = h;
-                            var msg = 'Added new app ' + handler.appId + ' from push';
-                            console.log(msg);
-                            notifier.success(msg);
-                        }).then(function() {
-                            // Reload so the app is visible while it's updating (below).
-                            return $scope.loadAppsList().then(function() {
-                                return handler;
+            appharness.push.pending(function(obj) {
+                console.log('Return from pending: ' + obj);
+                if (obj && obj.type && obj.type === 'serve') {
+                    AppsService.getAppList().then(function(list) {
+                        console.log(list);
+                        var matches = list && list.filter(function(x) { return x.appId == obj.name; });
+                        var promise;
+                        if (list && matches.length > 0) {
+                            // App exists.
+                            var app = matches[0];
+                            app.url = obj.url;
+                            promise = $q.when(app);
+                        } else {
+                            // New app.
+                            var handler;
+                            promise = AppsService.addApp(obj.type, obj.url).then(function(h) {
+                                handler = h;
+                                var msg = 'Added new app ' + handler.appId + ' from push';
+                                console.log(msg);
+                                notifier.success(msg);
+                            }).then(function() {
+                                // Reload so the app is visible while it's updating (below).
+                                return $scope.loadAppsList().then(function() {
+                                    return handler;
+                                });
                             });
-                        });
-                    }
+                        }
 
-                    var theApp;
-                    promise.then(function(app) {
-                        theApp = app;
-                        return AppsService.updateApp(app);
-                    }).then(function() {
-                        notifier.success('Updated ' + theApp.appId + ' due to remote push.');
-                        return $scope.loadAppsList();
-                    }).done(function() {
-                        $scope.launchApp(theApp, { stopPropagation: function() { } });
-                    }, function(err) {
-                        var msg = 'Failed to update ' + app.appId + ': ' + err;
-                        console.error(msg);
-                        notifier.error(msg);
+                        var theApp;
+                        promise.then(function(app) {
+                            theApp = app;
+                            return AppsService.updateApp(app);
+                        }).then(function() {
+                            notifier.success('Updated ' + theApp.appId + ' due to remote push.');
+                            return $scope.loadAppsList();
+                        }).then(function() {
+                            return $scope.launchApp(theApp, { stopPropagation: function() { } });
+                        }).done(null, function(err) {
+                            var msg = 'Failed to update ' + app.appId + ': ' + err;
+                            console.error(msg);
+                            notifier.error(msg);
+                        });
                     });
-                });
-            }
-        });
+                }
+            });
 
-        $scope.listen = function() {
-            appharness.push.listen(function() {
-                $scope.listening = true;
-                $scope.$apply();
-            }, notifier.error);
-        };
+            $scope.listen = function() {
+                appharness.push.listen(function() {
+                    $scope.listening = true;
+                    $scope.$apply();
+                }, notifier.error);
+            };
+        }
     }]);
 })();
 


[3/6] git commit: Update AppHarness to modern MCA, which expects to be loaded with file://

Posted by br...@apache.org.
Update AppHarness to modern MCA, which expects to be loaded with file://

Also required fixing the remapped paths a bit. CRX apps are loading
correctly now.


Project: http://git-wip-us.apache.org/repos/asf/cordova-app-harness/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-app-harness/commit/6f99f08d
Tree: http://git-wip-us.apache.org/repos/asf/cordova-app-harness/tree/6f99f08d
Diff: http://git-wip-us.apache.org/repos/asf/cordova-app-harness/diff/6f99f08d

Branch: refs/heads/master
Commit: 6f99f08d8a2f8fa3aa2067a68c35bdfb53410952
Parents: 1245da1
Author: Braden Shepherdson <br...@gmail.com>
Authored: Tue Jan 21 10:02:49 2014 -0500
Committer: Braden Shepherdson <br...@gmail.com>
Committed: Tue Jan 21 10:02:49 2014 -0500

----------------------------------------------------------------------
 www/cdvah/crx_files/config.android.xml |  2 +-
 www/cdvah/crx_files/config.ios.xml     |  2 +-
 www/cdvah/js/Installer.js              | 12 +++++-------
 3 files changed, 7 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-app-harness/blob/6f99f08d/www/cdvah/crx_files/config.android.xml
----------------------------------------------------------------------
diff --git a/www/cdvah/crx_files/config.android.xml b/www/cdvah/crx_files/config.android.xml
index 24126a4..27f4782 100644
--- a/www/cdvah/crx_files/config.android.xml
+++ b/www/cdvah/crx_files/config.android.xml
@@ -92,7 +92,7 @@
         <param name="onload" value="true" />
     </feature>
 
-    <content src="chrome-extension://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/chromeapp.html" />
+    <content src="plugins/org.chromium.bootstrap/chromeapp.html" />
     <access origin="*" />
     <access origin="chrome-extension://*" />
     <access origin="cdv-app-harness://*" />

http://git-wip-us.apache.org/repos/asf/cordova-app-harness/blob/6f99f08d/www/cdvah/crx_files/config.ios.xml
----------------------------------------------------------------------
diff --git a/www/cdvah/crx_files/config.ios.xml b/www/cdvah/crx_files/config.ios.xml
index c6d446b..422f393 100644
--- a/www/cdvah/crx_files/config.ios.xml
+++ b/www/cdvah/crx_files/config.ios.xml
@@ -78,7 +78,7 @@
         <param name="ios-package" value="ChromeExtensionURLs" />
         <param name="onload" value="true" />
     </feature>
-    <content src="chrome-extension://ohgfbmefaoadakchflddcopcmphnlcba/chromeapp.html" />
+    <content src="plugins/org.chromium.bootstrap/chromeapp.html" />
     <feature name="ChromeSocket">
         <param name="ios-package" value="ChromeSocket" />
     </feature>

http://git-wip-us.apache.org/repos/asf/cordova-app-harness/blob/6f99f08d/www/cdvah/js/Installer.js
----------------------------------------------------------------------
diff --git a/www/cdvah/js/Installer.js b/www/cdvah/js/Installer.js
index 8a58d1c..79161fb 100644
--- a/www/cdvah/js/Installer.js
+++ b/www/cdvah/js/Installer.js
@@ -121,24 +121,22 @@
                 // Override cordova.js, cordova_plugins.js, and www/plugins to point at bundled plugins.
                 UrlRemap.aliasUri('/cordova\\.js.*', '.+', harnessDir + '/cordova.js', false /* redirect */);
                 UrlRemap.aliasUri('/cordova_plugins\\.js.*', '.+', harnessDir + '/cordova_plugins.js', false /* redirect */);
-                if (startLocation.indexOf('chrome-extension://') === 0) {
+
+                // We want the /www/ for Cordova apps, and no /www/ for CRX apps.
+                var installSubdir = type == 'crx' ? '/' : '/www/';
+                if (startLocation.indexOf('chromeapp.html') >= 0) {
                     var pluginsUrl = 'chrome-extension://[^/]+/plugins/';
                     UrlRemap.aliasUri('^' + pluginsUrl, '^' + pluginsUrl, harnessDir + '/plugins/', false /* redirect */);
 
-                    var bootstrapUrl = 'chrome-extension://[^/]+/chrome(?:app\\.html|bgpage\\.html|appstyles\\.css)';
-                    UrlRemap.aliasUri('^' + bootstrapUrl, '^chrome-extension://[^/]+/', harnessDir + '/', false /* redirect */);
-
                     var chromeExtensionUrl = 'chrome-extension://[^\/]+/(?!!gap_exec)';
                     // Add the extra mapping for chrome-extension://aaaa... to point to the install location.
-                    // We want the /www/ for Cordova apps, and no /www/ for CRX apps.
-                    var installSubdir = type == 'crx' ? '/' : '/www/';
                     UrlRemap.aliasUri('^' + chromeExtensionUrl, '^' + chromeExtensionUrl, installUrl + installSubdir, false /* redirect */);
                 } else {
                     var pluginsUrl = startLocation.replace(/\/www\/.*/, '/www/plugins/');
                     UrlRemap.aliasUri('^' + pluginsUrl, '^' + pluginsUrl, harnessDir + '/plugins/', false /* redirect */);
                 }
                 // Make any references to www/ point to the app's install location.
-                UrlRemap.aliasUri('^' + harnessDir, '^' + harnessDir, installUrl + '/www', false /* redirect */);
+                UrlRemap.aliasUri('^' + harnessDir, '^' + harnessDir, installUrl + installSubdir, false /* redirect */);
                 // Set-up app-harness: scheme to point at the harness.
                 UrlRemap.aliasUri('^app-harness:///cdvah/index.html', '^app-harness://', harnessDir, true);
                 return UrlRemap.aliasUri('^app-harness:', '^app-harness://', harnessDir, false)


[5/6] git commit: Add support for Push plugin.

Posted by br...@apache.org.
Add support for Push plugin.


Project: http://git-wip-us.apache.org/repos/asf/cordova-app-harness/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-app-harness/commit/018bdd19
Tree: http://git-wip-us.apache.org/repos/asf/cordova-app-harness/tree/018bdd19
Diff: http://git-wip-us.apache.org/repos/asf/cordova-app-harness/diff/018bdd19

Branch: refs/heads/master
Commit: 018bdd19b8c15988b45543f4a6d5d46735935b0b
Parents: 7d62432
Author: Braden Shepherdson <br...@gmail.com>
Authored: Thu Jan 16 13:34:21 2014 -0500
Committer: Braden Shepherdson <br...@gmail.com>
Committed: Mon Jan 27 11:00:54 2014 -0500

----------------------------------------------------------------------
 www/cdvah/js/Installer.js |  1 +
 www/cdvah/js/ListCtrl.js  | 61 +++++++++++++++++++++++++++++++++++++++++-
 www/cdvah/views/list.html |  6 +++++
 3 files changed, 67 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-app-harness/blob/018bdd19/www/cdvah/js/Installer.js
----------------------------------------------------------------------
diff --git a/www/cdvah/js/Installer.js b/www/cdvah/js/Installer.js
index 79161fb..9864e91 100644
--- a/www/cdvah/js/Installer.js
+++ b/www/cdvah/js/Installer.js
@@ -32,6 +32,7 @@
         }
 
         function getAppPlugins(cordovaPluginsFile) {
+            console.log('Reading plugins from: ' + cordovaPluginsFile);
             return ResourcesLoader.readFileContents(cordovaPluginsFile)
             .then(function(contents) {
                 if (!contents) {

http://git-wip-us.apache.org/repos/asf/cordova-app-harness/blob/018bdd19/www/cdvah/js/ListCtrl.js
----------------------------------------------------------------------
diff --git a/www/cdvah/js/ListCtrl.js b/www/cdvah/js/ListCtrl.js
index d79eaef..b74c953 100644
--- a/www/cdvah/js/ListCtrl.js
+++ b/www/cdvah/js/ListCtrl.js
@@ -1,7 +1,8 @@
 (function(){
     'use strict';
     /* global myApp */
-    myApp.controller('ListCtrl', ['$location', 'notifier', '$rootScope', '$scope', '$routeParams', 'AppsService', function ($location, notifier, $rootScope, $scope, $routeParams, AppsService) {
+    /* global appharness */
+    myApp.controller('ListCtrl', ['$location', 'notifier', '$rootScope', '$scope', '$routeParams', '$q', 'AppsService', function ($location, notifier, $rootScope, $scope, $routeParams, $q, AppsService) {
         $scope.appList = [];
         $rootScope.appTitle = 'Cordova App Harness';
 
@@ -97,6 +98,64 @@
         };
 
         initialise();
+
+        appharness.push.listening(function(res) {
+            $scope.listening = res;
+            $scope.$apply();
+        }, notifier.error);
+
+        appharness.push.pending(function(obj) {
+            console.log('Return from pending: ' + obj);
+            if (obj && obj.type && obj.type === 'serve') {
+                AppsService.getAppList().then(function(list) {
+                    console.log(list);
+                    var matches = list && list.filter(function(x) { return x.appId == obj.name; });
+                    var promise;
+                    if (list && matches.length > 0) {
+                        // App exists.
+                        var app = matches[0];
+                        app.url = obj.url;
+                        promise = $q.when(app);
+                    } else {
+                        // New app.
+                        var handler;
+                        promise = AppsService.addApp(obj.type, obj.url).then(function(h) {
+                            handler = h;
+                            var msg = 'Added new app ' + handler.appId + ' from push';
+                            console.log(msg);
+                            notifier.success(msg);
+                        }).then(function() {
+                            // Reload so the app is visible while it's updating (below).
+                            return $scope.loadAppsList().then(function() {
+                                return handler;
+                            });
+                        });
+                    }
+
+                    var theApp;
+                    promise.then(function(app) {
+                        theApp = app;
+                        return AppsService.updateApp(app);
+                    }).then(function() {
+                        notifier.success('Updated ' + theApp.appId + ' due to remote push.');
+                        return $scope.loadAppsList();
+                    }).done(function() {
+                        $scope.launchApp(theApp, { stopPropagation: function() { } });
+                    }, function(err) {
+                        var msg = 'Failed to update ' + app.appId + ': ' + err;
+                        console.error(msg);
+                        notifier.error(msg);
+                    });
+                });
+            }
+        });
+
+        $scope.listen = function() {
+            appharness.push.listen(function() {
+                $scope.listening = true;
+                $scope.$apply();
+            }, notifier.error);
+        };
     }]);
 })();
 

http://git-wip-us.apache.org/repos/asf/cordova-app-harness/blob/018bdd19/www/cdvah/views/list.html
----------------------------------------------------------------------
diff --git a/www/cdvah/views/list.html b/www/cdvah/views/list.html
index d1857dd..f0ca2ff 100644
--- a/www/cdvah/views/list.html
+++ b/www/cdvah/views/list.html
@@ -20,3 +20,9 @@
     <a href="#/add"><button class="topcoat-button--cta">Add app</button></a>
     <button class="topcoat-button" ng-click="loadAppsList()">Reload</button>
 </div>
+
+<div class="listen" ng-show="listening !== undefined">
+    <button class="topcoat-button" ng-show="!listening" ng-click="listen()">Start listening</button>
+    <strong ng-show="listening">Listening</strong>
+</div>
+