Rework Overlay menu to be another view within the main webview.

Much more performant and less complicated this way!
diff --git a/AppHarnessUI/AppHarnessUI.java b/AppHarnessUI/AppHarnessUI.java
index 2a80dd8..9e6eb82 100644
--- a/AppHarnessUI/AppHarnessUI.java
+++ b/AppHarnessUI/AppHarnessUI.java
@@ -27,7 +27,6 @@
 import org.apache.cordova.CordovaWebViewClient;
 import org.apache.cordova.IceCreamCordovaWebViewClient;
 import org.apache.cordova.LinearLayoutSoftKeyboardDetect;
-import org.apache.cordova.PluginEntry;
 import org.apache.cordova.PluginResult;
 import org.json.JSONException;
 
@@ -46,13 +45,9 @@
     ViewGroup contentView;
     View origMainView;
     CustomCordovaWebView slaveWebView;
-    CordovaWebView overlayWebView;
+    boolean slaveVisible;
     CallbackContext eventsCallback;
 
-    public CordovaWebView getSlave() {
-        return slaveWebView;
-    }
-
     @Override
     public boolean execute(String action, CordovaArgs args, final CallbackContext callbackContext) throws JSONException {
         if ("create".equals(action)) {
@@ -68,17 +63,11 @@
                     destroy(callbackContext);
                 }
             });
-        } else if ("createOverlay".equals(action)) {
-            final String url = args.getString(0);
+        } else if ("setVisible".equals(action)) {
+            final boolean value = args.getBoolean(0);
             this.cordova.getActivity().runOnUiThread(new Runnable() {
                 public void run() {
-                    createOverlay(url, callbackContext);
-                }
-            });
-        } else if ("destroyOverlay".equals(action)) {
-            this.cordova.getActivity().runOnUiThread(new Runnable() {
-                public void run() {
-                    destroyOverlay(callbackContext);
+                    setSlaveVisible(value, callbackContext);
                 }
             });
         } else if ("evalJs".equals(action)) {
@@ -96,7 +85,7 @@
         return true;
     }
 
-    void sendEvent(String eventName) {
+    private void sendEvent(String eventName) {
         PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, eventName);
         pluginResult.setKeepCallback(true);
         eventsCallback.sendPluginResult(pluginResult );
@@ -127,9 +116,8 @@
             if (activity.getBooleanProperty("DisallowOverscroll", false)) {
                 slaveWebView.setOverScrollMode(CordovaWebView.OVER_SCROLL_NEVER);
             }
-            contentView.removeAllViews();
-            contentView.addView((View)slaveWebView.getParent());
             slaveWebView.loadUrl(url);
+            setSlaveVisible(true, null);
         }
         callbackContext.success();
     }
@@ -138,45 +126,38 @@
         if (slaveWebView == null) {
             Log.w(LOG_TAG, "destroy: already destroyed");
         } else {
-            contentView.removeAllViews();
-            contentView.addView(origMainView);
+            setSlaveVisible(false, null);
             slaveWebView.destroy();
             slaveWebView = null;
+            sendEvent("destroyed");
         }
         if (eventsCallback != null) {
-            eventsCallback.success();
+            eventsCallback.success("");
             eventsCallback = null;
         }
         callbackContext.success();
     }
 
-    private void createOverlay(String url, CallbackContext callbackContext) {
-        if (overlayWebView != null) {
-            Log.w(LOG_TAG, "createOverlay: already exists");
+    private void setSlaveVisible(boolean value, CallbackContext callbackContext) {
+        if (value == slaveVisible) {
+            return;
+        }
+        if (slaveWebView == null) {
+            Log.w(LOG_TAG, "setSlaveVisible: slave not created");
         } else {
-            overlayWebView = new CordovaWebView(cordova.getActivity());
-            initWebView(overlayWebView);
-            overlayWebView.pluginManager.addService(new PluginEntry("OverlayPlugin", new OverlayPlugin()));
-            overlayWebView.setOverScrollMode(CordovaWebView.OVER_SCROLL_NEVER);
-            contentView.addView((View)overlayWebView.getParent());
-            overlayWebView.loadUrl(url);
+            slaveVisible = value;
+            contentView.removeAllViews();
+            View newView = value ? (View)slaveWebView.getParent() : origMainView;
+            // Back button capturing breaks without these:
+            contentView.addView(newView);
+            newView.requestFocus();
 
         }
-        callbackContext.success();
-    }
-
-    private void destroyOverlay(CallbackContext callbackContext) {
-        if (overlayWebView == null) {
-            Log.w(LOG_TAG, "destroyOverlay: already destroyed");
-        } else {
-            contentView.removeView((View)overlayWebView.getParent());
-            overlayWebView.destroy();
-            overlayWebView = null;
+        if (callbackContext != null) {
+            callbackContext.success();
         }
-        callbackContext.success();
     }
 
-
     private void initWebView(CordovaWebView newWebView) {
         CordovaActivity activity = (CordovaActivity)cordova.getActivity();
         if (contentView == null) {
@@ -205,17 +186,6 @@
         layoutView.addView(newWebView);
     }
 
-    private class OverlayPlugin extends CordovaPlugin {
-        @Override
-        public boolean execute(String action, CordovaArgs args, final CallbackContext callbackContext) throws JSONException {
-            if ("sendEvent".equals(action)) {
-                sendEvent(args.getString(0));
-                return true;
-            }
-            return false;
-        }
-    }
-
     // Based on: http://stackoverflow.com/questions/12414680/how-to-implement-a-two-finger-double-click-in-android
     private class TwoFingerDoubleTapGestureDetector {
         private final int TIMEOUT = ViewConfiguration.getDoubleTapTimeout() + 100;
diff --git a/AppHarnessUI/appharnessui.js b/AppHarnessUI/appharnessui.js
index 65864a6..a902f60 100644
--- a/AppHarnessUI/appharnessui.js
+++ b/AppHarnessUI/appharnessui.js
@@ -20,7 +20,7 @@
 var exec = cordova.require('cordova/exec');
 
 function eventHandler(type) {
-    exports.onEvent && exports.onEvent(type);
+    type && exports.onEvent && exports.onEvent(type);
 }
 
 exports.onEvent = null;
@@ -34,12 +34,8 @@
     exec(win, null, 'AppHarnessUI', 'destroy', []);
 };
 
-exports.createOverlay = function(url, win) {
-    exec(win, null, 'AppHarnessUI', 'createOverlay', [url]);
-};
-
-exports.destroyOverlay = function(win) {
-    exec(win, null, 'AppHarnessUI', 'destroyOverlay', []);
+exports.setVisible = function(value, win) {
+    exec(win, null, 'AppHarnessUI', 'setVisible', [value]);
 };
 
 exports.evalJs = function(code, win) {
diff --git a/www/cdvah/css/style.css b/www/cdvah/css/style.css
index daab765..a52ee24 100644
--- a/www/cdvah/css/style.css
+++ b/www/cdvah/css/style.css
@@ -55,3 +55,8 @@
 .notification-error {
     background-color: #f2dede;
 }
+
+.in-app-menu-container button {
+    margin: 20px;
+    text-align: center;
+}
diff --git a/www/cdvah/harnessmenu.html b/www/cdvah/harnessmenu.html
index 41b9d6a..8526930 100644
--- a/www/cdvah/harnessmenu.html
+++ b/www/cdvah/harnessmenu.html
@@ -35,6 +35,7 @@
         <script type="text/javascript" src="js/UrlRemap.js"></script>
         <script type="text/javascript" src="js/ListCtrl.js"></script>
         <script type="text/javascript" src="js/DetailsCtrl.js"></script>
+        <script type="text/javascript" src="js/InAppMenuCtrl.js"></script>
         <script type="text/javascript" src="js/Notify.js"></script>
         <script type="text/javascript" src="js/HttpServer.js"></script>
         <script type="text/javascript" src="js/HarnessServer.js"></script>
diff --git a/www/cdvah/js/AppHarnessUI.js b/www/cdvah/js/AppHarnessUI.js
index eaaca59..01abf65 100644
--- a/www/cdvah/js/AppHarnessUI.js
+++ b/www/cdvah/js/AppHarnessUI.js
@@ -31,14 +31,9 @@
                 cordova.plugins.appharnessui.destroy(deferred.resolve);
                 return deferred.promise;
             },
-            createOverlay: function() {
+            setVisible: function(value) {
                 var deferred = $q.defer();
-                cordova.plugins.appharnessui.createOverlay('app-harness:///cdvahcm/contextMenu.html', deferred.resolve);
-                return deferred.promise;
-            },
-            destroyOverlay: function() {
-                var deferred = $q.defer();
-                cordova.plugins.appharnessui.destroyOverlay(deferred.resolve);
+                cordova.plugins.appharnessui.setVisible(value, deferred.resolve);
                 return deferred.promise;
             },
             setEventHandler: function(f) {
diff --git a/www/cdvah/js/AppsService.js b/www/cdvah/js/AppsService.js
index 43adc0c..a7cb71a 100644
--- a/www/cdvah/js/AppsService.js
+++ b/www/cdvah/js/AppsService.js
@@ -19,7 +19,7 @@
 (function() {
     'use strict';
     /* global myApp */
-    myApp.factory('AppsService', ['$q', 'ResourcesLoader', 'INSTALL_DIRECTORY', 'APPS_JSON', 'notifier', 'AppHarnessUI', function($q, ResourcesLoader, INSTALL_DIRECTORY, APPS_JSON, notifier, AppHarnessUI) {
+    myApp.factory('AppsService', ['$q', '$location', 'ResourcesLoader', 'INSTALL_DIRECTORY', 'APPS_JSON', 'notifier', 'AppHarnessUI', function($q, $location, ResourcesLoader, INSTALL_DIRECTORY, APPS_JSON, notifier, AppHarnessUI) {
         // Map of type -> installer.
         var _installerFactories = Object.create(null);
         // Array of installer objects.
@@ -90,23 +90,6 @@
             return ResourcesLoader.writeFileContents(APPS_JSON, stringContents);
         }
 
-        AppHarnessUI.setEventHandler(function(eventName) {
-            console.log('Got event from UI: ' + eventName);
-            if (eventName == 'showMenu') {
-                AppHarnessUI.createOverlay();
-            } else if (eventName == 'hideMenu') {
-                AppHarnessUI.destroyOverlay();
-            } else if (eventName == 'restartApp') {
-                // TODO: Restart in place?
-                AppsService.launchApp(activeInstaller)
-                .then(null, notifier.error);
-            } else if (eventName == 'quitApp') {
-                AppsService.quitApp();
-            } else {
-                console.warn('Unknown message from AppHarnessUI: ' + eventName);
-            }
-        });
-
         var AppsService = {
             // return promise with the array of apps
             getAppList : function() {
@@ -149,8 +132,8 @@
             quitApp : function() {
                 if (activeInstaller) {
                     activeInstaller.unlaunch();
-                    AppHarnessUI.destroy();
                     activeInstaller = null;
+                    return AppHarnessUI.destroy();
                 }
                 return $q.when();
             },
@@ -167,6 +150,8 @@
                         return AppHarnessUI.create(launchUrl);
                     }, function() {
                         throw new Error('Start file does not exist: ' + launchUrl.replace(/.*?\/www\//, 'www/'));
+                    }).then(function() {
+                        $location.path('/inappmenu');
                     });
                 });
             },
diff --git a/www/cdvah/js/InAppMenuCtrl.js b/www/cdvah/js/InAppMenuCtrl.js
new file mode 100644
index 0000000..1ffb1da
--- /dev/null
+++ b/www/cdvah/js/InAppMenuCtrl.js
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+*/
+(function(){
+    'use strict';
+
+    /* global myApp */
+    myApp.controller('InAppMenuCtrl', ['$rootScope', '$scope', '$window', 'AppsService', 'AppHarnessUI', function($rootScope, $scope, $window, AppsService, AppHarnessUI) {
+        var activeApp = AppsService.getActiveApp();
+        if (!activeApp) {
+            $window.history.back();
+        }
+
+        function backButtonListener() {
+            $scope.$apply($scope.hideMenu);
+        }
+        document.addEventListener('backbutton', backButtonListener);
+        $scope.$on('$destroy', function() {
+            document.removeEventListener('backbutton', backButtonListener);
+        });
+
+        $scope.app = activeApp;
+
+        $scope.hideMenu = function() {
+            return AppHarnessUI.setVisible(true);
+        };
+
+        $scope.restartApp = function() {
+            return AppsService.launchApp(activeApp);
+        };
+
+        $scope.quitApp = function() {
+            return AppsService.quitApp();
+        };
+
+        AppHarnessUI.setEventHandler(function(eventName) {
+            $scope.$apply(function() {
+                if (eventName == 'showMenu') {
+                    AppHarnessUI.setVisible(false);
+                } else if (eventName == 'hideMenu') {
+                    AppHarnessUI.setVisible(true);
+                } else if (eventName == 'restartApp') {
+                    // TODO: Restart in place?
+                    AppsService.launchApp(activeApp);
+                } else if (eventName == 'quitApp') {
+                    AppsService.quitApp();
+                } else if (eventName == 'destroyed') {
+                    $window.history.back();
+                } else {
+                    console.warn('Unknown message from AppHarnessUI: ' + eventName);
+                }
+            });
+        });
+    }]);
+})();
diff --git a/www/cdvah/js/app.js b/www/cdvah/js/app.js
index 8b84a1a..df30b53 100644
--- a/www/cdvah/js/app.js
+++ b/www/cdvah/js/app.js
@@ -27,6 +27,10 @@
         templateUrl: 'views/list.html',
         controller: 'ListCtrl'
     });
+    $routeProvider.when('/inappmenu', {
+        templateUrl: 'views/inappmenu.html',
+        controller: 'InAppMenuCtrl'
+    });
     $routeProvider.when('/details/:index', {
         templateUrl: 'views/details.html',
         controller: 'DetailsCtrl'
diff --git a/www/cdvah/views/inappmenu.html b/www/cdvah/views/inappmenu.html
new file mode 100644
index 0000000..404735c
--- /dev/null
+++ b/www/cdvah/views/inappmenu.html
@@ -0,0 +1,24 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<div class="in-app-menu-container">
+  <h1>{{app.appName || app.appId}}</h1>
+  <button ng-click="hideMenu()">Resume App</button>
+  <button ng-click="restartApp()">Restart App</button><br>
+  <button ng-click="quitApp()">Quit App</button>
+</div>
diff --git a/www/cdvahcm/contextMenu.html b/www/cdvahcm/contextMenu.html
deleted file mode 100644
index 39848aa..0000000
--- a/www/cdvahcm/contextMenu.html
+++ /dev/null
@@ -1,80 +0,0 @@
-<!DOCTYPE html>
-<!--
-  Licensed to the Apache Software Foundation (ASF) under one
-  or more contributor license agreements.  See the NOTICE file
-  distributed with this work for additional information
-  regarding copyright ownership.  The ASF licenses this file
-  to you under the Apache License, Version 2.0 (the
-  "License"); you may not use this file except in compliance
-  with the License.  You may obtain a copy of the License at
-
-  http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing,
-  software distributed under the License is distributed on an
-  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-  KIND, either express or implied.  See the License for the
-  specific language governing permissions and limitations
-  under the License.
--->
-<html onclick="sendEvent('hideMenu')">
-<head>
-    <style>
-        body
-        {
-            background-color:rgba(0,0,0,0.75);
-            width: 100%;
-            height: 100%;
-        }
-        p, label
-        {
-            color: white;
-            text-align: center;
-        }
-        ul
-        {
-            list-style-type: none;
-            margin-left: 20%;
-            margin-right: 20%;
-            padding: 0;
-            width: 60%;
-        }
-        li
-        {
-            margin-bottom: 0.5cm;
-            width : 100%;
-            min-height: 1cm;
-            border-bottom: 1px solid grey;
-        }
-        .fullwidthElement
-        {
-            width: 100%;
-            min-height: 1cm;
-        }
-        .halfwidthElement
-        {
-            min-height: 1cm;
-            width: 48%;
-            padding-left: 0;
-            padding-right: 0;
-        }
-    </style>
-    <script src="app-harness:///cordova.js"></script>
-</head>
-<body>
-    <p>Tap Anywhere to Close</p>
-    <ul>
-        <li>
-            <button class="fullwidthElement" onclick="sendEvent('restartApp')">Restart</button>
-        </li>
-        <li>
-            <button class="fullwidthElement" onclick="sendEvent('quitApp')">Back to Main Menu</button>
-        </li>
-    </ul>
-</body>
-<script>
-  function sendEvent(eventName) {
-      cordova.exec(null, null, 'OverlayPlugin', 'sendEvent', [eventName]);
-  }
-</script>
-</html>