diff --git a/build-artifacts/project-template-gradle/src/main/java/com/tns/RuntimeHelper.java b/build-artifacts/project-template-gradle/src/main/java/com/tns/RuntimeHelper.java index 7d634794b..3e0cee066 100644 --- a/build-artifacts/project-template-gradle/src/main/java/com/tns/RuntimeHelper.java +++ b/build-artifacts/project-template-gradle/src/main/java/com/tns/RuntimeHelper.java @@ -85,11 +85,13 @@ public static Runtime initRuntime(Application app) { String outputDir = app.getFilesDir().getPath() + File.separator; - aE.extractAssets(app, "app", outputDir, extractPolicy); - aE.extractAssets(app, "internal", outputDir, extractPolicy); - aE.extractAssets(app, "metadata", outputDir, extractPolicy); + // will force deletion of previously extracted files in app/files directories + // see https://github.com/NativeScript/NativeScript/issues/4137 for reference + boolean removePreviouslyInstalledAssets = true; + aE.extractAssets(app, "app", outputDir, extractPolicy, removePreviouslyInstalledAssets); + aE.extractAssets(app, "internal", outputDir, extractPolicy, removePreviouslyInstalledAssets); + aE.extractAssets(app, "metadata", outputDir, extractPolicy, false); - // enable with flags? boolean shouldExtractSnapshots = true; // will extract snapshot of the device appropriate architecture @@ -98,7 +100,7 @@ public static Runtime initRuntime(Application app) { logger.write("Extracting snapshot blob"); } - aE.extractAssets(app, "snapshots/" + Build.CPU_ABI, outputDir, extractPolicy); + aE.extractAssets(app, "snapshots/" + Build.CPU_ABI, outputDir, extractPolicy, removePreviouslyInstalledAssets); } extractPolicy.setAssetsThumb(app); diff --git a/runtime/src/main/java/com/tns/AssetExtractor.java b/runtime/src/main/java/com/tns/AssetExtractor.java index 044d9fb28..de2439628 100644 --- a/runtime/src/main/java/com/tns/AssetExtractor.java +++ b/runtime/src/main/java/com/tns/AssetExtractor.java @@ -1,6 +1,7 @@ package com.tns; import java.io.File; +import java.io.IOException; import android.content.Context; import android.util.Log; @@ -13,7 +14,7 @@ public AssetExtractor(File libPath, Logger logger) { this.logger = logger; } - public void extractAssets(Context context, String inputPath, String outputPath, ExtractPolicy extractPolicy) { + public void extractAssets(Context context, String inputPath, String outputPath, ExtractPolicy extractPolicy, boolean shouldCleanUpPreviousAssets) { FileExtractor extractor = extractPolicy.extractor(); if (extractor != null) { boolean success = extractor.extract(context); @@ -21,6 +22,14 @@ public void extractAssets(Context context, String inputPath, String outputPath, logger.write("extract returned " + success); } } else if (extractPolicy.shouldExtract(context)) { + if (shouldCleanUpPreviousAssets) { + try { + delete(new File(outputPath + inputPath)); + } catch (IOException e) { + Log.d(LogTag, "Problem occurred while deleting assets from previous app version: " + outputPath + inputPath); + } + } + String apkPath = context.getPackageCodePath(); boolean forceOverwrite = extractPolicy.forceOverwrite(); @@ -32,4 +41,33 @@ public void extractAssets(Context context, String inputPath, String outputPath, } } } + + /** + * Delete a file or a directory and its children. + * @param file The directory to delete. + * @throws IOException Exception when problem occurs during deleting the directory. + */ + private static void delete(File file) throws IOException { + File[] files = file.listFiles(); + if (files == null) { + Log.d(LogTag, "Can't remove previously installed assets in " + file.getAbsolutePath()); + return; + } + + for (File childFile : files) { + if (childFile.isDirectory()) { + delete(childFile); + } else { + if (!childFile.delete()) { + throw new IOException(); + } + } + } + + if (!file.delete()) { + throw new IOException(); + } + } + + private static String LogTag = "JS: AssetExtraction"; } \ No newline at end of file diff --git a/test-app/app/src/main/java/com/tns/DefaultExtractPolicy.java b/test-app/app/src/main/java/com/tns/DefaultExtractPolicy.java index d1ecf15c0..e9d009013 100644 --- a/test-app/app/src/main/java/com/tns/DefaultExtractPolicy.java +++ b/test-app/app/src/main/java/com/tns/DefaultExtractPolicy.java @@ -29,7 +29,7 @@ public DefaultExtractPolicy(Logger logger) { } public boolean shouldExtract(Context context) { - String assetsThumbFilePath = context.getFilesDir().getPath() + File.separatorChar + ASSETS_THUMB_FILENAME; + String assetsThumbFilePath = getFilesDir(context) + File.separatorChar + ASSETS_THUMB_FILENAME; String oldAssetsThumb = getCachedAssetsThumb(assetsThumbFilePath); if (oldAssetsThumb == null) { return true; @@ -47,7 +47,7 @@ public boolean shouldExtract(Context context) { public void setAssetsThumb(Context context) { String assetsThumb = generateAssetsThumb(context); if (assetsThumb != null) { - String assetsThumbFilePath = context.getFilesDir().getPath() + File.separatorChar + ASSETS_THUMB_FILENAME; + String assetsThumbFilePath = getFilesDir(context) + File.separatorChar + ASSETS_THUMB_FILENAME; saveNewAssetsThumb(assetsThumb, assetsThumbFilePath); } } @@ -114,12 +114,26 @@ private void saveNewAssetsThumb(String newThumb, String assetsThumbFile) { out.close(); } } catch (FileNotFoundException e) { - logger.write("Error while writting current assets thumb"); + logger.write("Error while writing current assets thumb"); e.printStackTrace(); } catch (IOException e) { - logger.write("Error while writting current assets thumb"); + logger.write("Error while writing current assets thumb"); e.printStackTrace(); } } + /* + Write assetsThumb file to a no-backup directory to prevent the thumb from being + backed up on devices of API Level 23 and up. + Devices Level 22 and lower don't support the Auto Backup feature, + so it is safe to keep the thumb in the /files directory + */ + private static String getFilesDir(Context context) { + if (android.os.Build.VERSION.SDK_INT >= /* 21 */ android.os.Build.VERSION_CODES.LOLLIPOP) { + return context.getNoBackupFilesDir().getPath(); + } else { + return context.getFilesDir().getPath(); + } + } + } diff --git a/test-app/app/src/main/java/com/tns/RuntimeHelper.java b/test-app/app/src/main/java/com/tns/RuntimeHelper.java index 7d634794b..3e0cee066 100644 --- a/test-app/app/src/main/java/com/tns/RuntimeHelper.java +++ b/test-app/app/src/main/java/com/tns/RuntimeHelper.java @@ -85,11 +85,13 @@ public static Runtime initRuntime(Application app) { String outputDir = app.getFilesDir().getPath() + File.separator; - aE.extractAssets(app, "app", outputDir, extractPolicy); - aE.extractAssets(app, "internal", outputDir, extractPolicy); - aE.extractAssets(app, "metadata", outputDir, extractPolicy); + // will force deletion of previously extracted files in app/files directories + // see https://github.com/NativeScript/NativeScript/issues/4137 for reference + boolean removePreviouslyInstalledAssets = true; + aE.extractAssets(app, "app", outputDir, extractPolicy, removePreviouslyInstalledAssets); + aE.extractAssets(app, "internal", outputDir, extractPolicy, removePreviouslyInstalledAssets); + aE.extractAssets(app, "metadata", outputDir, extractPolicy, false); - // enable with flags? boolean shouldExtractSnapshots = true; // will extract snapshot of the device appropriate architecture @@ -98,7 +100,7 @@ public static Runtime initRuntime(Application app) { logger.write("Extracting snapshot blob"); } - aE.extractAssets(app, "snapshots/" + Build.CPU_ABI, outputDir, extractPolicy); + aE.extractAssets(app, "snapshots/" + Build.CPU_ABI, outputDir, extractPolicy, removePreviouslyInstalledAssets); } extractPolicy.setAssetsThumb(app);