Refactored such that download works from cordova serve
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/9ae368ee Tree: http://git-wip-us.apache.org/repos/asf/cordova-app-harness/tree/9ae368ee Diff: http://git-wip-us.apache.org/repos/asf/cordova-app-harness/diff/9ae368ee Branch: refs/heads/master Commit: 9ae368eec38fd838321432b8d07cda3692f3ec0a Parents: c46221f Author: Andrew Grieve <[email protected]> Authored: Thu Oct 10 15:57:07 2013 -0400 Committer: Andrew Grieve <[email protected]> Committed: Wed Oct 23 21:55:17 2013 -0400 ---------------------------------------------------------------------- createproject.sh | 6 +- www/cdvah_index.html | 6 +- www/cdvah_js/AddCtrl.js | 53 ++-- www/cdvah_js/AppConstants.js | 12 - www/cdvah_js/AppsService.js | 346 +++++++------------------- www/cdvah_js/KnownExtensionDownloader.js | 25 +- www/cdvah_js/ListCtrl.js | 30 ++- www/cdvah_js/Notify.js | 4 - www/cdvah_js/ResourcesLoader.js | 198 ++++----------- www/cdvah_js/ServeCordovaJSHandler.js | 78 ++++++ www/cdvah_js/app.js | 16 ++ www/cdvah_views/add.html | 39 ++- www/cdvah_views/list.html | 12 +- www/config.xml | 1 + 14 files changed, 296 insertions(+), 530 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cordova-app-harness/blob/9ae368ee/createproject.sh ---------------------------------------------------------------------- diff --git a/createproject.sh b/createproject.sh index b386bc2..435ef9d 100755 --- a/createproject.sh +++ b/createproject.sh @@ -21,7 +21,10 @@ module.exports = function(grunt) { grunt.loadNpmTasks('grunt-contrib-watch'); grunt.registerTask('prepare', 'Runs cdv prepare', function() { - cordova.prepare(); + var done = this.async(); + cordova.prepare(function(e) { + done(!e); + }); }); // Default task(s). @@ -41,6 +44,7 @@ $CORDOVA plugin add ../../../mobile_chrome_apps/zip $CORDOVA plugin add ../../../BarcodeScanner # https://github.com/wildabeast/BarcodeScanner.git $CORDOVA plugin add ../../cordova-plugin-file $CORDOVA plugin add ../../cordova-plugin-file-transfer +$CORDOVA plugin add ../../cordova-labs/file-extras exit 0 http://git-wip-us.apache.org/repos/asf/cordova-app-harness/blob/9ae368ee/www/cdvah_index.html ---------------------------------------------------------------------- diff --git a/www/cdvah_index.html b/www/cdvah_index.html index 1d2065c..9f0f82d 100644 --- a/www/cdvah_index.html +++ b/www/cdvah_index.html @@ -6,22 +6,18 @@ <script type="text/javascript" src="cdvah_js/libs/q.min.js"></script> <script type="text/javascript" src="cdvah_js/libs/angular.min.js"></script> <script type="text/javascript" src="cdvah_js/app.js"></script> - <script type="text/javascript" src="cdvah_js/AppConstants.js"></script> <script type="text/javascript" src="cdvah_js/ContextMenuInjectScript.js"></script> <script type="text/javascript" src="cdvah_js/ServeCordovaJSHandler.js"></script> <script type="text/javascript" src="cdvah_js/ResourcesLoader.js"></script> <script type="text/javascript" src="cdvah_js/AppsService.js"></script> <script type="text/javascript" src="cdvah_js/AppBundleAlias.js"></script> - <script type="text/javascript" src="cdvah_js/KnownExtensionDownloader.js"></script> - <script type="text/javascript" src="cdvah_js/CdvhPackageHandler.js"></script> - <script type="text/javascript" src="cdvah_js/CrxPackageHandler.js"></script> <script type="text/javascript" src="cdvah_js/ListCtrl.js"></script> <script type="text/javascript" src="cdvah_js/AddCtrl.js"></script> <script type="text/javascript" src="cdvah_js/Notify.js"></script> <link rel="stylesheet" type="text/css" href="css/topcoat-mobile-light.min.css" /> <link rel="stylesheet" type="text/css" href="css/style.css" /> </head> - <body ng-app="CordovaAppHarness"> + <body> <div class="topcoat-navigation-bar"> <div class="topcoat-navigation-bar__item center full"> <h1 class="topcoat-navigation-bar__title">{{ appTitle }}</h1> http://git-wip-us.apache.org/repos/asf/cordova-app-harness/blob/9ae368ee/www/cdvah_js/AddCtrl.js ---------------------------------------------------------------------- diff --git a/www/cdvah_js/AddCtrl.js b/www/cdvah_js/AddCtrl.js index 54e73b0..e004f21 100644 --- a/www/cdvah_js/AddCtrl.js +++ b/www/cdvah_js/AddCtrl.js @@ -1,62 +1,41 @@ (function(){ "use strict"; /* global myApp */ - myApp.controller("AddCtrl", ["notifier", "$rootScope", "$scope", "$location", "$window", "AppsService", function (notifier, $rootScope, $scope, $location, $window, AppsService) { + myApp.controller("AddCtrl", ["notifier", "$rootScope", "$scope", "$window", "AppsService", function (notifier, $rootScope, $scope, $window, AppsService) { $rootScope.appTitle = 'Add App'; $scope.appData = { - appName : "", - appSource : "pattern", - appSourcePattern : "", - appSourceServe : "" + appUrl : 'localhost', + installerType: 'serve' }; $scope.addApp = function() { - var serviceCall; - if($scope.appData.appSource === "pattern") { - if(!$scope.appData.appSourcePattern) { - notifier.error('Url of package not specified'); - return; - } - serviceCall = AppsService.addAppFromPattern($scope.appData.appName, $scope.appData.appSourcePattern); - } else if($scope.appData.appSource === "serve") { - if(!$scope.appData.appSourceServe) { - notifier.error('Url of config file not specified'); - return; - } - serviceCall = AppsService.addAppFromServe($scope.appData.appName, $scope.appData.appSourceServe); - } + var serviceCall = AppsService.addApp($scope.appData.installerType, $scope.appData.appUrl); - if(serviceCall){ - serviceCall.then(function() { - console.log('successfully installed'); - notifier.success('Successfully installed'); - }, function(error) { - console.error(error); - notifier.error('Unable to add application because: ' + error.message); - }); - } else { - notifier.error('Error adding application: Unrecognized application source: ' + $scope.appData.appSource); - } + serviceCall.then(function(handler) { + console.log('successfully installed'); + notifier.success('Successfully installed'); + return AppsService.updateApp(handler) + .done(); + }, function(error) { + console.error(error); + notifier.error('Unable to add application because: ' + error.message); + }); }; // True if the optional barcodescanner plugin is installed. - $scope.qr_enabled = !!$window.barcodescanner; + $scope.qr_enabled = !!(cordova.plugins && cordova.plugins.barcodeScanner); // Scans a QR code, placing the URL into the currently selected of source and pattern. $scope.fetchQR = function() { console.log('calling'); - $window.barcodescanner.scan(function(result) { + $window.cordova.plugins.barcodeScanner.scan(function(result) { console.log('success'); if (!result || result.cancelled || !result.text) { notifier.error('No QR code received.'); } else { - if ($scope.appData.appSource == 'pattern') { - $scope.appData.appSourcePattern = result.text; - } else { - $scope.appData.appSourceServe = result.text; - } + $scope.appData.appUrl = result.text; notifier.success('QR code received'); $scope.$apply(); } http://git-wip-us.apache.org/repos/asf/cordova-app-harness/blob/9ae368ee/www/cdvah_js/AppConstants.js ---------------------------------------------------------------------- diff --git a/www/cdvah_js/AppConstants.js b/www/cdvah_js/AppConstants.js deleted file mode 100644 index 81f8b4d..0000000 --- a/www/cdvah_js/AppConstants.js +++ /dev/null @@ -1,12 +0,0 @@ -(function() { - var TEMP_DIRECTORY = "cordova_app_harness_tempDir/"; - var INSTALL_DIRECTORY = "cordova_app_harness_installed_apps/"; - var APPS_JSON = "cordova_app_harness_installed_apps/apps.json"; - var METADATA_JSON = "cordova_app_harness_installed_apps/metadata.json"; - - /* global myApp */ - myApp.value("TEMP_DIRECTORY", TEMP_DIRECTORY); - myApp.value("INSTALL_DIRECTORY", INSTALL_DIRECTORY); - myApp.value("APPS_JSON", APPS_JSON); - myApp.value("METADATA_JSON", METADATA_JSON); -})(); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cordova-app-harness/blob/9ae368ee/www/cdvah_js/AppsService.js ---------------------------------------------------------------------- diff --git a/www/cdvah_js/AppsService.js b/www/cdvah_js/AppsService.js index 0e59e3b..cd565d0 100644 --- a/www/cdvah_js/AppsService.js +++ b/www/cdvah_js/AppsService.js @@ -1,77 +1,73 @@ (function() { "use strict"; /* global myApp */ - myApp.factory("AppsService", [ "ResourcesLoader", "INSTALL_DIRECTORY", "TEMP_DIRECTORY", "APPS_JSON", "METADATA_JSON", function(ResourcesLoader, INSTALL_DIRECTORY, TEMP_DIRECTORY, APPS_JSON, METADATA_JSON) { + myApp.factory("AppsService", [ "ResourcesLoader", "INSTALL_DIRECTORY", "APPS_JSON", function(ResourcesLoader, INSTALL_DIRECTORY, APPS_JSON) { - var platformId = cordova.require("cordova/platform").id; - // downloaders that know how to download from certain patterns. - // Eg: The KnownExtensionDownloader MAY know how to download from any uri's that end in known extensions - var downloadHandlers = []; - // handlers that have registered to unpack certain extensions during the installation of an app - var extensionHandlers = {}; + var platformId = cordova.platformId; + // Map of type -> handler. + var _installHandlerFactories = {}; // functions to run before launching an app var preLaunchHooks = []; - function grabExtensionFromUri(uri) { - var lastSegment = uri.split("#")[0].split("?")[0].split("/").pop(); - var dotLocation = lastSegment.lastIndexOf("."); - var extension = (dotLocation !== -1)? lastSegment.substring(dotLocation + 1) : ""; - return extension; + var _installHandlers = null; + var _lastLaunchedAppId = null; + + function createInstallHandlersFromJson(json) { + var appList = json['appList'] || []; + var ret = []; + for (var i = 0, entry; entry = appList[i]; ++i) { + var factory = _installHandlerFactories[entry['appType']]; + var handler = factory.createFromJson(entry['appUrl'], entry['appId']); + handler.lastUpdated = entry['lastUpdated'] && new Date(entry['lastUpdated']); + ret.push(handler); + } + return ret; } - function addNewAppFromPattern(appName, appSourcePattern) { - var fullFilePath; - - return ResourcesLoader.deleteDirectory(INSTALL_DIRECTORY + appName) - .then(function(){ - for(var i = 0; i < downloadHandlers.length; i++){ - if(downloadHandlers[i].handler.canHandleSourcePattern(appSourcePattern)){ - return downloadHandlers[i].handler.downloadFromPattern(appName, appSourcePattern, TEMP_DIRECTORY); - } - } - throw new Error("App Harness does not know how to install an app from the pattern: " + appSourcePattern); - }) - .then(function(_fullFilePath){ - fullFilePath = _fullFilePath; - return ResourcesLoader.ensureDirectoryExists(INSTALL_DIRECTORY + appName); - }) - .then(function(directoryPath){ - var extension = grabExtensionFromUri(fullFilePath); - if(!extensionHandlers[extension]) { - throw new Error("No handler for extension " + extension + " found"); - } - return extensionHandlers[extension].extractPackageToDirectory(appName, fullFilePath, directoryPath); - }) - .then(function(){ - return registerApp(appName, "pattern", appSourcePattern); + function readAppsJson() { + var deferred = Q.defer(); + ResourcesLoader.readJSONFileContents(APPS_JSON) + .then(function(result) { + deferred.resolve(result); + }, function() { + // Error means first run. + deferred.resolve({}); }); + return deferred.promise; } - function addNewAppFromServe(appName, appSourceServe){ - return ResourcesLoader.deleteDirectory(INSTALL_DIRECTORY + appName) - .then(function(){ - return ResourcesLoader.ensureDirectoryExists(INSTALL_DIRECTORY + appName + "/" + platformId); - }) - .then(function(){ - return registerApp(appName, "serve", appSourceServe); + function initHandlers() { + if (_installHandlers) { + return Q(); + } + + return readAppsJson() + .then(function(appsJson) { + _lastLaunchedAppId = appsJson['lastLaunched']; + _installHandlers = createInstallHandlersFromJson(appsJson); }); } - function registerApp(appName, appSource, appSourceData) { - return ResourcesLoader.readJSONFileContents(APPS_JSON) - .then(function(result){ - result.installedApps = result.installedApps || []; - result.installedApps.push({ - "Name" : appName, - "Source" : appSource, - "Data" : appSourceData, - "Installed" : (new Date()).toLocaleString() + function writeAppsJson() { + var appsJson = { + 'lastLaunched': _lastLaunchedAppId, + 'appList': [] + }; + var appList = appsJson['appList']; + for (var i = 0, handler; handler = _installHandlers[i]; ++i) { + appList.push({ + 'appId' : handler.appId, + 'appType' : handler.type, + 'appUrl' : handler.url, + 'lastUpdated': handler.lastUpdated && +handler.lastUpdated }); - return ResourcesLoader.writeJSONFileContents(APPS_JSON, result); - }); + } + + var stringContents = JSON.stringify(appsJson); + return ResourcesLoader.writeFileContents(APPS_JSON, stringContents); } - function isPathAbsolute(path){ + function isUrlAbsolute(path){ return (path.match(/^[a-z0-9+.-]+:/) != null); } @@ -93,7 +89,7 @@ var el = els[i]; var srcValue = el.getAttribute("src"); if(srcValue) { - if(isPathAbsolute(srcValue)) { + if(isUrlAbsolute(srcValue)) { startLocation = srcValue; } else { srcValue = srcValue.charAt(0) === "/" ? srcValue.substring(1) : srcValue; @@ -109,110 +105,19 @@ }); } - function removeApp(appName){ - var entry; - return ResourcesLoader.ensureDirectoryExists(APPS_JSON) - .then(function() { - return ResourcesLoader.readJSONFileContents(APPS_JSON); - }) - .then(function(result){ - result.installedApps = result.installedApps || []; - for(var i = 0; i < result.installedApps.length; i++){ - if(result.installedApps[i].Name === appName) { - entry = result.installedApps.splice(i, 1)[0]; - break; - } - } - if(!entry) { - throw new Error("The app " + appName + " was not found."); - } - return ResourcesLoader.writeJSONFileContents(APPS_JSON, result); - }) - .then(function(){ - return ResourcesLoader.deleteDirectory(INSTALL_DIRECTORY + appName); - }) - .then(function(){ - return entry; - }); - } - - function getAppsList(getFullEntries){ - return ResourcesLoader.ensureDirectoryExists(APPS_JSON) - .then(function() { - return ResourcesLoader.readJSONFileContents(APPS_JSON); - }) - .then(function(result){ - result.installedApps = result.installedApps || []; - var newAppsList = []; - - for(var i = 0; i < result.installedApps.length; i++){ - if(getFullEntries) { - newAppsList.push(result.installedApps[i]); - } else { - newAppsList.push(result.installedApps[i].Name); - } - } - - return newAppsList; - }); - } - - function isUniqueApp(appName){ - return getAppsList(false /* App names only */) - .then(function(appsList){ - if(appsList.indexOf(appName) !== -1) { - throw new Error("An app with this name already exists"); - } - }); - } - - function getAppEntry(appName){ - return getAppsList(true /* Get full app entry */) - .then(function(appEntries){ - var entry; - for(var i = 0; i < appEntries.length; i++){ - if(appEntries[i].Name === appName){ - entry = appEntries[i]; - break; - } - } - if(!entry){ - throw new Error("Could not find the app " + appName + " in the installed apps"); - } - return entry; - }); - } - // On success, this function returns the following paths // appInstallLocation - INSTALL_DIR/app/platform/ // platformWWWLocation - location containing the html, css and js files // configLocation - location of config.xml // startLocation - the path of the page to start the app with - function getAppPathsForAppEntry(entry){ + function getAppPathsForHandler(handler) { var appPaths = {}; - return ResourcesLoader.getFullFilePath(INSTALL_DIRECTORY + entry.Name + "/" + platformId) - .then(function(platformLocation){ - if(!isPathAbsolute(platformLocation)){ - // assume file uri - platformLocation = "file://" + platformLocation; - } - appPaths.appInstallLocation = platformLocation; - if(entry.Source === "pattern"){ - appPaths.platformWWWLocation = platformLocation + "/www/"; - appPaths.configLocation = platformLocation + "/config.xml"; - } else if(entry.Source === "serve"){ - var configFile = entry.Data; - var location = configFile.indexOf("/config.xml"); - if(location === -1){ - throw new Error("The location of config.xml provided is invalid. Expected the location to end with 'config.xml'"); - } - //grab path including upto last slash - var appLocation = configFile.substring(0, location + 1); - appPaths.platformWWWLocation = appLocation; - appPaths.configLocation = configFile; - } else { - throw new Error("Unknown app source: " + entry.Source); - } + var installPath = INSTALL_DIRECTORY + '/' + handler.appId; + appPaths.appInstallLocation = installPath; + appPaths.configLocation = installPath + "/config.xml"; + appPaths.platformWWWLocation = installPath + "/www/"; + + return Q.fcall(function(){ return getAppStartPageFromConfig(appPaths.configLocation, appPaths.platformWWWLocation); }) .then(function(startLocation){ @@ -233,26 +138,21 @@ } return { - //return promise with the array of apps - getAppsList : function(getFullEntries) { - return getAppsList(getFullEntries); + // return promise with the array of apps + getAppList : function() { + return initHandlers() + .then(function() { + return _installHandlers.slice(); + }); }, - launchApp : function(appName) { + launchApp : function(handler) { var appEntry; var startLocation; - return ResourcesLoader.readJSONFileContents(METADATA_JSON) - .then(function(settings){ - settings = settings || {}; - settings.lastLaunched = appName; - return ResourcesLoader.writeJSONFileContents(METADATA_JSON, settings); - }) + _lastLaunchedAppId = handler.appId; + return writeAppsJson() .then(function(){ - return getAppEntry(appName); - }) - .then(function(_appEntry){ - appEntry = _appEntry; - return getAppPathsForAppEntry(appEntry); + return getAppPathsForHandler(handler); }) .then(function(appPaths){ startLocation = appPaths.startLocation; @@ -269,101 +169,45 @@ }); }, - addAppFromPattern : function(appName, appSourcePattern) { - return isUniqueApp(appName) - .then(function(){ - return addNewAppFromPattern(appName, appSourcePattern); - }); - }, - - addAppFromServe : function(appName, appSourceServe) { - return ResourcesLoader.readFileContents(appSourceServe) - .then(function(contents){ - if(!contents){ - throw new Error("The contents of config.xml at " + appSourceServe + " could not be read."); - } - }) - .then(null, function(error){ - // Code reaches here either because the get request to config.xml fails or config.xml is empty - var continueInstall = confirm("The contents of config.xml at " + appSourceServe + " could not be read.\n" + - "Have you run cordova serve?\n\n" + - "Press OK to continue the install process. Note that the config.xml should be visible before you start the app."); - if(!continueInstall){ - throw error; - } - }) - .then(function(){ - return isUniqueApp(appName); - }) - .then(function(){ - return ResourcesLoader.deleteDirectory(INSTALL_DIRECTORY + appName); + addApp : function(installerType, appUrl) { + var handlerFactory = _installHandlerFactories[installerType]; + return Q.fcall(function(){ + return handlerFactory.createFromUrl(appUrl); }) - .then(function(){ - return addNewAppFromServe(appName, appSourceServe); + .then(function(handler) { + _installHandlers.push(handler); + return writeAppsJson() + .then(function() { + return handler; + }); }); }, - uninstallApp : function(appName) { - return removeApp(appName); - }, - - getLastRunApp : function() { - return ResourcesLoader.readJSONFileContents(METADATA_JSON) - .then(function(settings){ - if(!settings || !settings.lastLaunched) { - throw new Error("No App has been launched yet"); - } - return settings.lastLaunched; + uninstallApp : function(handler) { + _installHandlers.splice(_installHandlers.indexOf(handler), 1); + return writeAppsJson() + .then(function() { + var installPath = INSTALL_DIRECTORY + '/' + handler.appId; + return ResourcesLoader.deleteDirectory(installPath); }); }, - registerPatternDownloader : function(handler, priority){ - if(!handler) { - throw new Error("Expected handler"); - } - if(typeof(handler.canHandleSourcePattern) !== "function") { - throw new Error("Expected function for bool handler.canHandleSourcePattern(string pattern) to exist"); - } - if(typeof(handler.downloadFromPattern) !== "function") { - throw new Error("Expected function for (string fullFilePath or QPromise) handler.downloadFromPattern(string appName, string pattern, string tempDirectory) to exist"); - } - if(!priority) { - // Assign a default priority - priority = 500; - } - insertObjectAtPriority(downloadHandlers, handler, priority); - }, - - registerPackageHandler : function(extension, handler) { - if(!extension) { - throw new Error("Expcted extension"); - } - if(!handler || typeof(handler.extractPackageToDirectory) !== "function") { - throw new Error("Expected function for void handler.extractPackageToDirectory(string fullFilePath, string directoryPath) to exist"); - } - if(handler[extension]) { - throw new Error("Handler already exists for the extension: " + extension); - } - extensionHandlers[extension] = handler; + getLastRunApp : function() { + throw new Error('Not implemented.'); }, - updateApp : function(appName){ - return removeApp(appName) - .then(function(entry){ - if(entry.Source === "pattern") { - return addNewAppFromPattern(entry.Name, entry.Data); - } + updateApp : function(handler){ + return Q.fcall(function() { + var installPath = INSTALL_DIRECTORY + '/' + handler.appId; + return handler.updateApp(installPath); }); }, - getKnownExtensions : function() { - return Object.keys(extensionHandlers); + registerInstallHandlerFactory : function(handlerFactory) { + _installHandlerFactories[handlerFactory.type] = handlerFactory; }, addPreLaunchHook : function(handler, priority){ - if(!handler || typeof(handler) !== "function") { - throw new Error("Expected (QPromise or void) function(appName, wwwLocation) for handler"); - } if(!priority) { // Assign a default priority priority = 500; @@ -372,4 +216,4 @@ } }; }]); -})(); \ No newline at end of file +})(); http://git-wip-us.apache.org/repos/asf/cordova-app-harness/blob/9ae368ee/www/cdvah_js/KnownExtensionDownloader.js ---------------------------------------------------------------------- diff --git a/www/cdvah_js/KnownExtensionDownloader.js b/www/cdvah_js/KnownExtensionDownloader.js index cec413a..1274ef1 100644 --- a/www/cdvah_js/KnownExtensionDownloader.js +++ b/www/cdvah_js/KnownExtensionDownloader.js @@ -9,35 +9,14 @@ return ret; } - function grabExtensionFromUri(uri) { - var lastSegment = uri.split("#")[0].split("?")[0].split("/").pop(); - var dotLocation = lastSegment.lastIndexOf("."); - var extension = (dotLocation !== -1)? lastSegment.substring(dotLocation + 1) : ""; - return extension; - } - // Note the priority given has no meaning in and of itself. It is used solely to compare if any other component has higher priority. - AppsService.registerPatternDownloader({ - canHandleSourcePattern : function (pattern) { - var canHandle = false; - if(isUri(pattern)) { - var currentExtension = grabExtensionFromUri(pattern); - if(currentExtension) { - var knownExtensions = AppsService.getKnownExtensions(); - if(knownExtensions.indexOf(currentExtension) !== -1){ - canHandle = true; - } - } - } - return canHandle; - }, - + AppsService.registerInstallHandler({ downloadFromPattern : function (appName, pattern, tempDirectory) { var extension = grabExtensionFromUri(pattern); var fileName = tempDirectory + appName + "." + extension; return ResourcesLoader.downloadFromUrl(pattern, fileName); } - }, 500 /* assign a priority */); + }); }]); })(); http://git-wip-us.apache.org/repos/asf/cordova-app-harness/blob/9ae368ee/www/cdvah_js/ListCtrl.js ---------------------------------------------------------------------- diff --git a/www/cdvah_js/ListCtrl.js b/www/cdvah_js/ListCtrl.js index 3940554..928fb48 100644 --- a/www/cdvah_js/ListCtrl.js +++ b/www/cdvah_js/ListCtrl.js @@ -2,10 +2,11 @@ "use strict"; /* global myApp */ myApp.controller("ListCtrl", [ "notifier", "$rootScope", "$scope", "$location", "$routeParams", "AppsService", function (notifier, $rootScope, $scope, $location, $routeParams, AppsService) { - - $scope.appsList = []; + $scope.appList = []; $rootScope.appTitle = 'Cordova App Harness'; + initialise(); + function clearAppBundleAliases(){ var deferred = Q.defer(); var appBundle = cordova.require("AppBundle.AppBundle"); @@ -61,19 +62,17 @@ } $scope.loadAppsList = function(callApply) { - return AppsService.getAppsList(true /* get full information about the app */) + return AppsService.getAppList() .then(function(newAppsList){ newAppsList.sort(function(a, b){ - if(a.Name < b.Name) { + if (a.appId < b.appId) { return -1; - } else if(a.Name > b.Name) { + } else if(a.appId > b.appId) { return 1; } return 0; }); - //clear the old apps list - $scope.appsList.splice(0, $scope.appsList.length); - angular.extend($scope.appsList, newAppsList); + $scope.appList = newAppsList; if(callApply) { $scope.$apply(); } @@ -87,7 +86,7 @@ $scope.launchApp = function(app){ return AppsService.launchApp(app) .then(null, function(error){ - console.error("Error during loading of app " + app + ": " + error); + console.error("Error during loading of app " + app.appId + ": " + error); notifier.error("Something went wrong during the loading of the app. Please try again." + error); }); }; @@ -98,23 +97,22 @@ notifier.success("Updated successfully"); console.log('successfully updated'); }, function(error){ - console.error("Error during updating of app " + app + ": " + error); + console.error("Error during updating of app " + app.appId + ": " + error); notifier.error("Something went wrong during the updating of the app. Please try again."); }); }; $scope.removeApp = function(app) { - var shouldUninstall = confirm("Are you sure you want to uninstall " + app + "?"); + var shouldUninstall = confirm("Are you sure you want to uninstall " + app.appId + "?"); if(shouldUninstall) { return AppsService.uninstallApp(app) - .then(function() { $scope.loadAppsList(true); }, function(error){ - console.error("Error during uninstall of app " + app + ": " + error); + .then(function() { $scope.loadAppsList(true); }, + function(error) { + console.error(error); notifier.error("Something went wrong during the uninstall of the app. Please try again."); }); } - }; - - document.addEventListener("deviceready", initialise, false); + } }]); })(); http://git-wip-us.apache.org/repos/asf/cordova-app-harness/blob/9ae368ee/www/cdvah_js/Notify.js ---------------------------------------------------------------------- diff --git a/www/cdvah_js/Notify.js b/www/cdvah_js/Notify.js index 921d15d..e76dc4b 100644 --- a/www/cdvah_js/Notify.js +++ b/www/cdvah_js/Notify.js @@ -14,10 +14,6 @@ scope.notification = {}; scope.notification.message = newValue.message; scope.notification.css = 'notification-' + newValue.type; - - $timeout(function() { - $rootScope.notification = undefined; - }, 5000); } }); } http://git-wip-us.apache.org/repos/asf/cordova-app-harness/blob/9ae368ee/www/cdvah_js/ResourcesLoader.js ---------------------------------------------------------------------- diff --git a/www/cdvah_js/ResourcesLoader.js b/www/cdvah_js/ResourcesLoader.js index e6c5ae8..9a3e1ea 100644 --- a/www/cdvah_js/ResourcesLoader.js +++ b/www/cdvah_js/ResourcesLoader.js @@ -3,61 +3,29 @@ /* global myApp */ myApp.factory("ResourcesLoader", [ "$window", function ($window) { - var fs; - var initialised = false; + var rootDir; function initialiseFileSystem() { - var deferred = Q.defer(); - - if(!initialised) { - - var failedFileSystemLookUp = function (error) { - var errorString = "An error occurred while reading the file system."; - if(error) { - errorString += " " + JSON.stringify(error); - } - deferred.reject(new Error(errorString)); - }; - - var success = function(_fs) { - fs = _fs; - initialised = true; - deferred.resolve(fs); - }; - - try { - $window.requestFileSystem($window.LocalFileSystem.PERSISTENT, 0, success, failedFileSystemLookUp); - } catch (e) { - failedFileSystemLookUp(e); - } - } else { - deferred.resolve(fs); - } - - return deferred.promise; + // HACK: Need to discuss better way to get the root entry. + return Q(rootDir = new $window.DirectoryEntry('/', '/')); } //promise returns full path to downloaded file function downloadFromUrl(url, fullFilePath) { var deferred = Q.defer(); - try { - var downloadFail = function(error) { - var str = "There was an error while downloading the file " + JSON.stringify(error); - deferred.reject(new Error(str)); - }; + var downloadFail = function(error) { + var str = "There was an error while downloading the file " + JSON.stringify(error); + deferred.reject(new Error(str)); + }; - var downloadSuccess = function(fileEntry) { - deferred.resolve(fileEntry.fullPath); - }; + var downloadSuccess = function(fileEntry) { + deferred.resolve(fileEntry.fullPath); + }; - var fileTransfer = new $window.FileTransfer(); - fileTransfer.download(url, fullFilePath, downloadSuccess, downloadFail); - } catch(e) { - deferred.reject(new Error(e)); - } finally { - return deferred.promise; - } + var fileTransfer = new $window.FileTransfer(); + fileTransfer.download(url, fullFilePath, downloadSuccess, downloadFail); + return deferred.promise; } function trim(str) { @@ -83,41 +51,31 @@ function getDirectoryEntry(directoryName) { var deferred = Q.defer(); - try { - var errorWhileGettingDirectoryEntry = function(error) { - var str = "There was an error while getting the directory entry for directory " + directoryName + " " + JSON.stringify(error); - deferred.reject(new Error(str)); - }; - var success = function(directoryEntry) { - deferred.resolve(directoryEntry); - }; - fs.root.getDirectory(directoryName, {create: true, exclusive: false}, success, errorWhileGettingDirectoryEntry); - } catch(e) { - deferred.reject(new Error(e)); - } finally { - return deferred.promise; - } + var errorWhileGettingDirectoryEntry = function(error) { + var str = "There was an error while getting the directory entry for directory " + directoryName + " " + JSON.stringify(error); + deferred.reject(new Error(str)); + }; + var success = function(directoryEntry) { + deferred.resolve(directoryEntry); + }; + rootDir.getDirectory(directoryName, {create: true, exclusive: false}, success, errorWhileGettingDirectoryEntry); + return deferred.promise; } //promise returns the file entry function getFileEntry(fileName, createFlag) { var deferred = Q.defer(); - try { - var errorWhileGettingFileEntry = function(error) { - var str = "There was an error while getting the file entry for file " + fileName + " " + JSON.stringify(error); - deferred.reject(new Error(str)); - }; - var success = function(fileEntry) { - deferred.resolve(fileEntry); - }; - // !! - ensures a boolean value - fs.root.getFile(fixFilePath(fileName), {create: !!createFlag, exclusive: false}, success, errorWhileGettingFileEntry); - } catch(e) { - deferred.reject(new Error(e)); - } finally { - return deferred.promise; - } + var errorWhileGettingFileEntry = function(error) { + var str = "There was an error while getting the file entry for file " + fileName + " " + JSON.stringify(error); + deferred.reject(new Error(str)); + }; + var success = function(fileEntry) { + deferred.resolve(fileEntry); + }; + // !! - ensures a boolean value + rootDir.getFile(fixFilePath(fileName), {create: !!createFlag, exclusive: false}, success, errorWhileGettingFileEntry); + return deferred.promise; } //promise returns the file @@ -126,28 +84,18 @@ then(function(fileEntry){ var deferred = Q.defer(); - try { - var errorWhileGettingFile = function(error) { - var str = "There was an error while getting the file for file " + fileName + " " + JSON.stringify(error); - deferred.reject(new Error(str)); - }; + var errorWhileGettingFile = function(error) { + var str = "There was an error while getting the file for file " + fileName + " " + JSON.stringify(error); + deferred.reject(new Error(str)); + }; - fileEntry.file(deferred.resolve, errorWhileGettingFile); - } catch(e) { - deferred.reject(new Error(e)); - } finally { - return deferred.promise; - } + fileEntry.file(deferred.resolve, errorWhileGettingFile); + return deferred.promise; }); } function truncateToDirectoryPath(path) { - //remove the filename if it exists - var lastLevelIndex = path.search(/[\w ]+(\.[\w ]+)+$/g); - if(lastLevelIndex !== -1) { - path = path.substring(0, lastLevelIndex); - } - return path; + return path.replace(/\/[^\/]+$/, '/'); } function getPathSegments(path){ @@ -174,7 +122,7 @@ deferred.reject(new Error(str)); }; - fs.root.getDirectory(directory, {create: true, exclusive: false}, gotDirEntry, failedToGetDirEntry); + rootDir.getDirectory(directory, {create: true, exclusive: false}, gotDirEntry, failedToGetDirEntry); return deferred.promise; } @@ -229,7 +177,7 @@ if(xhr.status === 200) { deferred.resolve(xhr); } else { - deferred.reject("XHR return status: " + xhr.status + " for url: " + url); + deferred.reject(new Error("XHR return status: " + xhr.status + " for url: " + url)); } } }; @@ -259,57 +207,8 @@ }); }, - // promise returns full path to file - getFullFilePath : function(filePath) { - return initialiseFileSystem() - .then(function(){ - var deferred = Q.defer(); - - // Use the file's parent folder to get the full path - var directory = filePath; - var fileName = ""; - - //remove the filename if it exists - var lastLevelIndex = directory.search(/\/[\w ]+\.[\w ]+$/g); - if(lastLevelIndex !== -1) { - directory = filePath.substring(0, lastLevelIndex); - fileName = filePath.substring(lastLevelIndex + 1); - } - - //we need the directory name w.r.t the root, so remove any slashes in the beginning - if(directory.charAt(0) === "/") { - directory = directory.substring(1); - } - - var gotFullPath = function(dirEntry) { - var fullFilePath = dirEntry.fullPath + "/" + fileName; - deferred.resolve(fullFilePath); - }; - - var failedToGetFullPath = function(error) { - var str = "There was an error getting the full path of file: " + filePath + " " + JSON.stringify(error); - deferred.reject(new Error(str)); - }; - - fs.root.getDirectory(directory, {create: true, exclusive: false}, gotFullPath, failedToGetFullPath); - return deferred.promise; - }); - }, - // returns a promise with a full path to the downloaded file - downloadFromUrl : function(url, filePath) { - var self = this; - return initialiseFileSystem() - .then(function(){ - return self.ensureDirectoryExists(filePath); - }) - .then(function(){ - return self.getFullFilePath(filePath); - }) - .then(function(fullFilePath){ - return downloadFromUrl(url, fullFilePath); - }); - }, + downloadFromUrl : downloadFromUrl, //returns a promise with the contents of the file readFileContents : function(fileName) { @@ -382,17 +281,6 @@ }); }, - //returns a promise when json file is written - writeJSONFileContents : function(fileName, contents) { - var stringContents; - if(typeof contents === "string") { - stringContents = contents; - } else { - stringContents = JSON.stringify(contents); - } - return this.writeFileContents(fileName, stringContents); - }, - deleteDirectory : function(directoryName) { return initialiseFileSystem() .then(function(){ @@ -435,4 +323,4 @@ }; }]); -})(); \ No newline at end of file +})(); http://git-wip-us.apache.org/repos/asf/cordova-app-harness/blob/9ae368ee/www/cdvah_js/ServeCordovaJSHandler.js ---------------------------------------------------------------------- diff --git a/www/cdvah_js/ServeCordovaJSHandler.js b/www/cdvah_js/ServeCordovaJSHandler.js index 55bad69..eb8beed 100644 --- a/www/cdvah_js/ServeCordovaJSHandler.js +++ b/www/cdvah_js/ServeCordovaJSHandler.js @@ -2,6 +2,7 @@ "use strict"; /* global myApp */ myApp.run(["AppsService", "ResourcesLoader", "ContextMenuInjectScript", function(AppsService, ResourcesLoader, ContextMenuInjectScript){ + var platformId = cordova.platformId; AppsService.addPreLaunchHook(function(appEntry, appInstallLocation , wwwLocation) { if(appEntry.Source === "serve"){ @@ -21,5 +22,82 @@ }); } }, 250 /* Give it a priority */); + + function ServeHandler(url, appId) { + this.url = url; + this.appId = appId || ''; + this.lastUpdated = null; + this._cachedProjectJson = null; + this._cachedConfigXml = null; + } + + ServeHandler.prototype.type = 'serve'; + + ServeHandler.prototype._updateAppMeta = function() { + var self = this; + return ResourcesLoader.xhrGet(this.url + '/' + platformId + '/project.json') + .then(function(xhr) { + self._cachedProjectJson = JSON.parse(xhr.responseText); + return ResourcesLoader.xhrGet(self.url + self._cachedProjectJson['configPath']); + }) + .then(function(xhr) { + self._cachedConfigXml = xhr.responseText; + var configXml = new DOMParser().parseFromString(self._cachedConfigXml, 'text/xml'); + self.appId = configXml.firstChild.getAttribute('id'); + }); + }; + + // TODO: update should be more atomic. Maybe download to a new directory? + ServeHandler.prototype.updateApp = function(installPath) { + var self = this; + return this._updateAppMeta() + .then(function() { + var wwwPath = self._cachedProjectJson['wwwPath']; + var files = self._cachedProjectJson['wwwFileList']; + var i = 0; + function downloadNext() { + if (!files[i]) { + self.lastUpdated = new Date(); + return; + } + console.log('now downloading ' + i + ' of ' + files.length); + var sourceUrl = self.url + wwwPath + files[i]; + var destPath = installPath + '/www' + files[i]; + console.log(destPath); + i += 1; + return ResourcesLoader.downloadFromUrl(sourceUrl, destPath).then(downloadNext); + } + return ResourcesLoader.ensureDirectoryExists(installPath + '/config.xml') + .then(function() { + return ResourcesLoader.writeFileContents(installPath + '/config.xml', self._cachedConfigXml); + }) + .then(downloadNext); + }); + }; + + function createFromUrl(url) { + // Strip platform and trailing slash if they exist. + url = url.replace(/\/$/, '').replace(new RegExp(platformId + '$'), '').replace(/\/$/, ''); + if (!/^http:/.test(url)) { + url = 'http://' + url; + } + if (!/:(\d)/.test(url)) { + url = url.replace(/(.*?\/\/[^\/]*)/, '$1:8000'); + } + // Fetch config.xml. + var ret = new ServeHandler(url); + + return ret._updateAppMeta().then(function() { return ret; }); + } + + function createFromJson(url, appId) { + return new ServeHandler(url, appId); + } + + AppsService.registerInstallHandlerFactory({ + type: 'serve', + createFromUrl: createFromUrl, // returns a promise. + createFromJson: createFromJson // does not return a promise. + }); }]); })(); http://git-wip-us.apache.org/repos/asf/cordova-app-harness/blob/9ae368ee/www/cdvah_js/app.js ---------------------------------------------------------------------- diff --git a/www/cdvah_js/app.js b/www/cdvah_js/app.js index 105a10b..c40bda7 100644 --- a/www/cdvah_js/app.js +++ b/www/cdvah_js/app.js @@ -1,4 +1,9 @@ + +Q.longStackSupport = true; + var myApp = angular.module("CordovaAppHarness", []); + + myApp.config(["$routeProvider", function($routeProvider){ $routeProvider.when("/", { templateUrl: "cdvah_views/list.html", @@ -9,3 +14,14 @@ myApp.config(["$routeProvider", function($routeProvider){ controller: "AddCtrl" }); }]); + +// foo +document.addEventListener('deviceready', function() { + cordova.plugins.fileextras.getDataDirectory(false, function(dirEntry) { + var path = dirEntry.fullPath; + myApp.value("INSTALL_DIRECTORY", path + "/apps"); + myApp.value("APPS_JSON", path + "/apps.json"); + alert('booting strap'); + angular.bootstrap(document, ['CordovaAppHarness']); + }); +}, false); http://git-wip-us.apache.org/repos/asf/cordova-app-harness/blob/9ae368ee/www/cdvah_views/add.html ---------------------------------------------------------------------- diff --git a/www/cdvah_views/add.html b/www/cdvah_views/add.html index a2602c8..d49ef54 100644 --- a/www/cdvah_views/add.html +++ b/www/cdvah_views/add.html @@ -1,25 +1,24 @@ <form name="addForm" ng-controller="AddCtrl"> - <label for="inputAppName">Enter the App Name</label><br /> - <input class="topcoat-text-input" type="text" name="appName" ng-model="appData.appName" ng-pattern="/^[\w ]*$/" ng-maxlength="64" required /> - <span ng-show="addForm.appName.$error.required">Required</span> - <span ng-show="addForm.appName.$error.pattern">Invalid characters used</span> - <span ng-show="addForm.appName.$error.maxlength">Too long</span> - <br /> - - <input type="radio" ng-model="appData.appSource" name="appSource" value="pattern" ng-checked="appData.appSource=='pattern'" /> - <label for="inputAppPattern">Enter the URL to a file</label><br /> - <input class="topcoat-text-input" id="inputAppPattern" type="text" name="appSourcePattern" ng-model="appData.appSourcePattern" ng-disabled="appData.appSource!='pattern'" ng-required="appData.appSource=='pattern'" autocorrect="off" autocapitalize="off" /> - <span ng-show="addForm.appSourcePattern.$error.required">Required</span> - <br /> - <input type="radio" ng-model="appData.appSource" name="appSource" value="serve" ng-checked="appData.appSource=='serve'" /> - <label for="inputAppServe">Enter the URL to the server hosting the app</label><br /> - <input class="topcoat-text-input" id="inputAppServe" type="text" name="appSourceServe" ng-model="appData.appSourceServe" ng-disabled="appData.appSource!='serve'" ng-pattern="/.*\/config.xml(\?.*|#.*)?$/" ng-required="appData.appSource=='serve'" autocorrect="off" autocapitalize="off" /> - <span ng-show="addForm.appSourceServe.$error.required">Required</span> - <span ng-show="addForm.appSourceServe.$error.pattern">Url must point to a config.xml file</span> - <br /> + <label>How to retrieve the app:<br> + <select ng-model="appData.installerType" /> + <option value="serve">cordova serve</option> + <option value="cdvh">.cdvh file</option> + </select> + </label> + <div ng-show="appData.installerType == 'serve'"> + <label>Enter server URL<br> + <input class="topcoat-text-input" type="text" ng-model="appData.appUrl" autocorrect="off" autocapitalize="off" /> + <button class="topcoat-button" ng-click="fetchQR(appData, 'appUrl')" ng-show="qr_enabled">Scan QR Code</button><br> + </label> + </div> + <div ng-show="appData.installerType == 'cdvh'"> + <label>Enter URL<br> + <input class="topcoat-text-input" type="text" ng-model="appData.appUrl" autocorrect="off" autocapitalize="off" /> + <button class="topcoat-button" ng-click="fetchQR(appData, 'appUrl')" ng-show="qr_enabled">Scan QR Code</button><br> + </label> + </div> <div class="buttons"> - <button class="topcoat-button" ng-click="fetchQR()" ng-show="qr_enabled">Scan QR Code</button><br /> - <button class="topcoat-button--cta" ng-click="addApp()" ng-disabled="!(addForm.$valid)">Add</button> + <button class="topcoat-button--cta" ng-click="addApp()">Add</button> <a href="#/"><button class="topcoat-button" ng-click="back()">Back</button></a> </div> </form> http://git-wip-us.apache.org/repos/asf/cordova-app-harness/blob/9ae368ee/www/cdvah_views/list.html ---------------------------------------------------------------------- diff --git a/www/cdvah_views/list.html b/www/cdvah_views/list.html index bc69941..e6b80e1 100644 --- a/www/cdvah_views/list.html +++ b/www/cdvah_views/list.html @@ -1,12 +1,12 @@ <div class="topcoat-list__container"> <h3 class="topcoat-list__header">Installed Apps</h3> <ul class="topcoat-list"> - <li class="topcoat-list__item" ng-repeat="app in appsList"> - <p>{{app.Name}}</p> - <p>Installed on: {{app.Installed}}</p> - <button ng-click="launchApp(app.Name)">Launch</button> - <button ng-click="updateApp(app.Name)" ng-disabled="app.Source==='serve'">Update</button> - <button ng-click="removeApp(app.Name)">Remove</button> + <li class="topcoat-list__item" ng-repeat="app in appList"> + <p>{{app.appId}}</p> + <p>Last updated: {{app.lastUpdated || 'never'}}</p> + <button ng-click="launchApp(app)">Launch</button> + <button ng-click="updateApp(app)">Update</button> + <button ng-click="removeApp(app)">Remove</button> </li> </ul> </div> http://git-wip-us.apache.org/repos/asf/cordova-app-harness/blob/9ae368ee/www/config.xml ---------------------------------------------------------------------- diff --git a/www/config.xml b/www/config.xml index 3ad17db..231c337 100644 --- a/www/config.xml +++ b/www/config.xml @@ -20,4 +20,5 @@ <widget id="org.apache.appharness" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0"> <name>CordovaAppHarness</name> <content src="cdvah_index.html" /> + <access origin="*" /> </widget>
