diff --git a/CHANGELOG.md b/CHANGELOG.md index b96e891a..f07cb774 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,24 @@ # Change Log +## 1.5.0 (2016-08-30) + +**Bug fixes:** + +- [Issue #197](https://github.com/nordnet/cordova-hot-code-push/issues/197). Android can now update files with spaces in their names. + +**Improvements:** + +- Merged [pull request #204](https://github.com/nordnet/cordova-hot-code-push/pull/204). Initial installation of the assets on the external storage on Android is now much faster. Thanks to [@Mikey1982](https://github.com/Mikey1982). +- Added new JS method: `getVersionInfo`. Based on [pull request #170](https://github.com/nordnet/cordova-hot-code-push/pull/170) from [@Manduro](https://github.com/Manduro) - thank you. For documentation, please, refer to [wikki page](https://github.com/nordnet/cordova-hot-code-push/wiki/Get-version-information). + ## 1.4.0 (2016-06-21) **Bug fixes:** -- [Issue #155](https://github.com/nordnet/cordova-hot-code-push/issues/155). Android app should not crash if server has a bad chcp.json file. + +- [Issue #155](https://github.com/nordnet/cordova-hot-code-push/issues/155). Android app should not crash if server has a bad `chcp.json` file. **Improvements:** + - [Issue #153](https://github.com/nordnet/cordova-hot-code-push/issues/153). You can now pass `chcp.json` file url into `fetchUpdate` method on JS side. Also, you can provide additional HTTP headers to the request. For example, authorization info. These headers will be used for loading configuration files and updated/changed files from your server. Thanks to [@davidovich](https://github.com/davidovich) for [pull request #150](https://github.com/nordnet/cordova-hot-code-push/pull/150). - [Issue #99](https://github.com/nordnet/cordova-hot-code-push/issues/99). iOS build version can now be a string. So, changing it from 1.0.0 to 1.0.1 will trigger reinstallation of `www` folder. Before that it had to be an integer. - [Issue #160](https://github.com/nordnet/cordova-hot-code-push/issues/160). Old releases are now gets removed after update installation. Previously cleanup was performed only on application restart. diff --git a/README.md b/README.md index bed7aa0e..04dfab40 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ As a result, your application receives updates of the web content as soon as pos ### Installation -This requires cordova 5.0+ (current stable 1.4.0) +This requires cordova 5.0+ (current stable 1.5.0) ```sh cordova plugin add cordova-hot-code-push-plugin diff --git a/package.json b/package.json index 86978900..6366004b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cordova-hot-code-push-plugin", - "version": "1.4.0", + "version": "1.5.0", "description": "Cordova plugin to perform code updates on the fly", "cordova": { "id": "cordova-hot-code-push-plugin", @@ -35,7 +35,7 @@ } ], "dependencies": { - "xml2js": ">=0.4" + "xml2js": "^0.4" }, "author": "Nikolay Demyankov for Nordnet Bank AB", "license": "MIT", diff --git a/plugin.xml b/plugin.xml index 3b2f56a3..7e6e5171 100644 --- a/plugin.xml +++ b/plugin.xml @@ -1,6 +1,6 @@ - + Hot Code Push Plugin Cordova plugin to perform code updates on the fly diff --git a/src/android/src/com/nordnetab/chcp/main/HotCodePushPlugin.java b/src/android/src/com/nordnetab/chcp/main/HotCodePushPlugin.java index e038b4fc..7fd6652f 100644 --- a/src/android/src/com/nordnetab/chcp/main/HotCodePushPlugin.java +++ b/src/android/src/com/nordnetab/chcp/main/HotCodePushPlugin.java @@ -1,5 +1,6 @@ package com.nordnetab.chcp.main; +import android.content.Context; import android.os.Handler; import android.text.TextUtils; import android.util.Log; @@ -251,6 +252,8 @@ public boolean execute(String action, CordovaArgs args, CallbackContext callback jsRequestAppUpdate(args, callbackContext); } else if (JSAction.IS_UPDATE_AVAILABLE_FOR_INSTALLATION.equals(action)) { jsIsUpdateAvailableForInstallation(callbackContext); + } else if (JSAction.GET_VERSION_INFO.equals(action)) { + jsGetVersionInfo(callbackContext); } else { cmdProcessed = false; } @@ -444,6 +447,24 @@ private void jsIsUpdateAvailableForInstallation(final CallbackContext callback) callback.sendPluginResult(pluginResult); } + /** + * Get information about app and web versions. + * + * @param callback callback where to send the result + */ + private void jsGetVersionInfo(final CallbackContext callback) { + final Context context = cordova.getActivity(); + final Map data = new HashMap(); + data.put("currentWebVersion", pluginInternalPrefs.getCurrentReleaseVersionName()); + data.put("readyToInstallWebVersion", pluginInternalPrefs.getReadyForInstallationReleaseVersionName()); + data.put("previousWebVersion", pluginInternalPrefs.getPreviousReleaseVersionName()); + data.put("appVersion", VersionHelper.applicationVersionName(context)); + data.put("buildVersion", VersionHelper.applicationVersionCode(context)); + + final PluginResult pluginResult = PluginResultHelper.createPluginResult(null, data, null); + callback.sendPluginResult(pluginResult); + } + // convenience method private void fetchUpdate() { fetchUpdate(null, new FetchUpdateOptions()); diff --git a/src/android/src/com/nordnetab/chcp/main/js/JSAction.java b/src/android/src/com/nordnetab/chcp/main/js/JSAction.java index abdd951e..e19b925c 100644 --- a/src/android/src/com/nordnetab/chcp/main/js/JSAction.java +++ b/src/android/src/com/nordnetab/chcp/main/js/JSAction.java @@ -13,6 +13,7 @@ public final class JSAction { public static final String CONFIGURE = "jsConfigure"; public static final String REQUEST_APP_UPDATE = "jsRequestAppUpdate"; public static final String IS_UPDATE_AVAILABLE_FOR_INSTALLATION = "jsIsUpdateAvailableForInstallation"; + public static final String GET_VERSION_INFO = "jsGetVersionInfo"; // Private API public static final String INIT = "jsInitPlugin"; diff --git a/src/android/src/com/nordnetab/chcp/main/utils/AssetsHelper.java b/src/android/src/com/nordnetab/chcp/main/utils/AssetsHelper.java index 8eca5b63..7024ba6f 100644 --- a/src/android/src/com/nordnetab/chcp/main/utils/AssetsHelper.java +++ b/src/android/src/com/nordnetab/chcp/main/utils/AssetsHelper.java @@ -6,7 +6,6 @@ import com.nordnetab.chcp.main.events.AssetsInstalledEvent; import com.nordnetab.chcp.main.events.BeforeAssetsInstalledEvent; -import org.apache.cordova.LOG; import org.greenrobot.eventbus.EventBus; import java.io.File; @@ -54,11 +53,8 @@ public static void copyAssetDirectoryToAppDirectory(final Context applicationCon @Override public void run() { try { - long start = System.currentTimeMillis(); - copyAssetDirectory(applicationContext, fromDirectory, toDirectory); + execute(applicationContext, fromDirectory, toDirectory); EventBus.getDefault().post(new AssetsInstalledEvent()); - long time = System.currentTimeMillis() - start; - LOG.d("CHCP", "Copied assets in : %d ms", time); } catch (IOException e) { e.printStackTrace(); EventBus.getDefault().post(new AssetsInstallationErrorEvent()); @@ -69,31 +65,35 @@ public void run() { }).start(); } - private static void copyAssetDirectory(Context applicationContext, String fromDirectory, String toDirectory) throws IOException { + private static void execute(Context applicationContext, String fromDirectory, String toDirectory) throws IOException { // recreate cache folder FilesUtility.delete(toDirectory); FilesUtility.ensureDirectoryExists(toDirectory); - JarFile jarFile = new JarFile(applicationContext.getApplicationInfo().sourceDir); - - String prefix = "assets/" + fromDirectory; - int prefixLength = prefix.length(); - Enumeration enu = jarFile.entries(); - while (enu.hasMoreElements()) { - JarEntry fileJarEntry = enu.nextElement(); - String name = fileJarEntry.getName(); - if (!fileJarEntry.isDirectory() && name.startsWith(prefix)) { + + final String assetsDir = "assets/" + fromDirectory; + copyAssets(applicationContext.getApplicationInfo().sourceDir, assetsDir, toDirectory); + } + + private static void copyAssets(final String appJarPath, final String assetsDir, final String toDirectory) throws IOException { + final JarFile jarFile = new JarFile(appJarPath); + final int prefixLength = assetsDir.length(); + final Enumeration filesEnumeration = jarFile.entries(); + + while (filesEnumeration.hasMoreElements()) { + final JarEntry fileJarEntry = filesEnumeration.nextElement(); + final String name = fileJarEntry.getName(); + if (!fileJarEntry.isDirectory() && name.startsWith(assetsDir)) { final String destinationFileAbsolutePath = Paths.get(toDirectory, name.substring(prefixLength)); - copyAssetFile(jarFile.getInputStream(fileJarEntry), destinationFileAbsolutePath); + copyFile(jarFile.getInputStream(fileJarEntry), destinationFileAbsolutePath); } } - } /** * Copies asset file to destination path */ - private static void copyAssetFile(InputStream in, String destinationFilePath) throws IOException { + private static void copyFile(final InputStream in, final String destinationFilePath) throws IOException { FilesUtility.ensureDirectoryExists(new File(destinationFilePath).getParent()); OutputStream out = new FileOutputStream(destinationFilePath); // Transfer bytes from in to out diff --git a/src/android/src/com/nordnetab/chcp/main/utils/URLUtility.java b/src/android/src/com/nordnetab/chcp/main/utils/URLUtility.java index 26a65296..b114afa9 100644 --- a/src/android/src/com/nordnetab/chcp/main/utils/URLUtility.java +++ b/src/android/src/com/nordnetab/chcp/main/utils/URLUtility.java @@ -1,6 +1,9 @@ package com.nordnetab.chcp.main.utils; +import android.util.Log; + import java.net.MalformedURLException; +import java.net.URI; import java.net.URL; import java.net.URLDecoder; @@ -21,12 +24,14 @@ public class URLUtility { public static URL stringToUrl(String urlString) { URL url = null; try { - url = new URL(URLDecoder.decode(urlString, "UTF-8")); + url = new URL(urlString); + final URI uri = new URI(url.getProtocol(), url.getUserInfo(), url.getHost(), url.getPort(), url.getPath(), url.getQuery(), url.getRef()); + url = uri.toURL(); } catch (Exception e) { try { url = new URL(urlString); } catch (MalformedURLException e2) { - e2.printStackTrace(); + Log.d("CHCP", "Failed to transfer url string \"" + urlString + "\" to actual url", e2); } } diff --git a/src/android/src/com/nordnetab/chcp/main/utils/VersionHelper.java b/src/android/src/com/nordnetab/chcp/main/utils/VersionHelper.java index c8ed8dbc..8bd9122e 100644 --- a/src/android/src/com/nordnetab/chcp/main/utils/VersionHelper.java +++ b/src/android/src/com/nordnetab/chcp/main/utils/VersionHelper.java @@ -2,6 +2,7 @@ import android.content.Context; import android.content.pm.PackageManager; +import android.util.Log; /** * Created by Nikolay Demyankov on 30.07.15. @@ -19,14 +20,31 @@ private VersionHelper() { * @param context application context * @return build version */ - public static int applicationVersionCode(Context context) { + public static int applicationVersionCode(final Context context) { int versionCode = 0; try { versionCode = context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionCode; } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); + Log.d("CHCP", "Can't get version code", e); } return versionCode; } + + /** + * Getter for application version name. + * + * @param context application context + * @return version name + */ + public static String applicationVersionName(final Context context) { + String versionName = ""; + try { + versionName = context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName; + } catch (PackageManager.NameNotFoundException e) { + Log.d("CHCP", "Can't get version name", e); + } + + return versionName; + } } \ No newline at end of file diff --git a/src/ios/HCPPlugin.h b/src/ios/HCPPlugin.h index 41a37f90..13592bc0 100644 --- a/src/ios/HCPPlugin.h +++ b/src/ios/HCPPlugin.h @@ -66,4 +66,11 @@ */ - (void)jsIsUpdateAvailableForInstallation:(CDVInvokedUrlCommand *)command; +/** + * Get information about app and web versions. + * + * @param command command with which the method is called + */ +- (void)jsGetVersionInfo:(CDVInvokedUrlCommand *)command; + @end diff --git a/src/ios/HCPPlugin.m b/src/ios/HCPPlugin.m index 5695dab5..1fea42a2 100644 --- a/src/ios/HCPPlugin.m +++ b/src/ios/HCPPlugin.m @@ -787,6 +787,17 @@ - (void)jsIsUpdateAvailableForInstallation:(CDVInvokedUrlCommand *)command { [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; } +- (void)jsGetVersionInfo:(CDVInvokedUrlCommand *)command { + NSDictionary *data = @{@"currentWebVersion": _pluginInternalPrefs.currentReleaseVersionName, + @"readyToInstallWebVersion": _pluginInternalPrefs.readyForInstallationReleaseVersionName, + @"previousWebVersion": _pluginInternalPrefs.previousReleaseVersionName, + @"appVersion": [NSBundle applicationVersionName], + @"buildVersion": [NSBundle applicationBuildVersion]}; + + CDVPluginResult *result = [CDVPluginResult pluginResultWithActionName:nil data:data error:nil]; + [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; +} + - (void)sendPluginNotReadyToWorkMessageForEvent:(NSString *)eventName callbackID:(NSString *)callbackID { NSError *error = [NSError errorWithCode:kHCPAssetsNotYetInstalledErrorCode description:@"WWW folder from the bundle is not yet installed on the external device. Please, wait for this operation to finish."]; diff --git a/src/ios/Utils/NSBundle+HCPExtension.h b/src/ios/Utils/NSBundle+HCPExtension.h index 33d13525..65d3192d 100644 --- a/src/ios/Utils/NSBundle+HCPExtension.h +++ b/src/ios/Utils/NSBundle+HCPExtension.h @@ -18,6 +18,13 @@ */ + (NSString *)applicationBuildVersion; +/** + * Getter for application's version name. + * + * @return version name of the app + */ ++ (NSString *)applicationVersionName; + /** * Path to the www folder in the application bundle. * diff --git a/src/ios/Utils/NSBundle+HCPExtension.m b/src/ios/Utils/NSBundle+HCPExtension.m index 12ae96d5..69901a88 100644 --- a/src/ios/Utils/NSBundle+HCPExtension.m +++ b/src/ios/Utils/NSBundle+HCPExtension.m @@ -17,7 +17,13 @@ @implementation NSBundle (HCPExtension) + (NSString *)applicationBuildVersion { NSBundle *mainBundle = [NSBundle mainBundle]; - return [mainBundle objectForInfoDictionaryKey:(NSString *)kCFBundleVersionKey]; + return [mainBundle objectForInfoDictionaryKey:@"CFBundleVersion"]; +} + ++ (NSString *)applicationVersionName { + NSBundle *mainBundle = [NSBundle mainBundle]; + + return [mainBundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; } + (NSString *)pathToWwwFolder { diff --git a/www/chcp.js b/www/chcp.js index 83af77ac..4b2743c4 100644 --- a/www/chcp.js +++ b/www/chcp.js @@ -13,7 +13,8 @@ var exec = require('cordova/exec'), INSTALL_UPDATE: 'jsInstallUpdate', CONFIGURE: 'jsConfigure', REQUEST_APP_UPDATE: 'jsRequestAppUpdate', - IS_UPDATE_AVAILABLE_FOR_INSTALLATION: 'jsIsUpdateAvailableForInstallation' + IS_UPDATE_AVAILABLE_FOR_INSTALLATION: 'jsIsUpdateAvailableForInstallation', + GET_INFO: 'jsGetVersionInfo' }; // Called when Cordova is ready for work. @@ -262,6 +263,16 @@ var chcp = { */ isUpdateAvailableForInstallation: function(callback) { callNativeMethod(pluginNativeMethod.IS_UPDATE_AVAILABLE_FOR_INSTALLATION, null, callback); + }, + + /** + * Get information about the current version like current release version, app build version and so on. + * The "data" property of the callback will contain all the information. + * + * @param {Callback(error, data)} callback - called, when information is retrieved from the native side. + */ + getVersionInfo: function(callback) { + callNativeMethod(pluginNativeMethod.GET_INFO, null, callback); } };