diff --git a/android/src/main/java/org/hstar/reactnative/esayupgrade/IORejectionException.java b/android/src/main/java/org/hstar/reactnative/esayupgrade/IORejectionException.java new file mode 100644 index 0000000..b44abc7 --- /dev/null +++ b/android/src/main/java/org/hstar/reactnative/esayupgrade/IORejectionException.java @@ -0,0 +1,14 @@ +package org.hstar.reactnative.esayupgrade; + +public class IORejectionException extends Exception { + private String code; + + public IORejectionException(String code, String message) { + super(message); + this.code = code; + } + + public String getCode() { + return code; + } +} diff --git a/android/src/main/java/org/hstar/reactnative/esayupgrade/RNEasyUpgradeModule.java b/android/src/main/java/org/hstar/reactnative/esayupgrade/RNEasyUpgradeModule.java index cdcfca7..c6e2ae5 100644 --- a/android/src/main/java/org/hstar/reactnative/esayupgrade/RNEasyUpgradeModule.java +++ b/android/src/main/java/org/hstar/reactnative/esayupgrade/RNEasyUpgradeModule.java @@ -16,9 +16,9 @@ import android.support.v4.util.LongSparseArray; import android.util.Log; import com.facebook.react.bridge.*; +import org.hstar.reactnative.esayupgrade.IORejectionException; -import java.io.File; -import java.io.FileNotFoundException; +import java.io.*; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -63,7 +63,6 @@ public String getName() { return "RNEasyUpgrade"; } - BroadcastReceiver downloadReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -87,6 +86,19 @@ public void onReceive(Context context, Intent intent) { } }; + private Uri getFileUri(String filepath) throws IORejectionException { + Uri uri = Uri.parse(filepath); + if (uri.getScheme() == null) { + // No prefix, assuming that provided path is absolute path to file + File file = new File(filepath); + if (file.isDirectory()) { + throw new IORejectionException("EISDIR", "EISDIR: illegal operation on a directory, read '" + filepath + "'"); + } + uri = Uri.parse("file://" + filepath); + } + return uri; + } + @ReactMethod public void download(String url, ReadableMap headers, ReadableMap config, Callback onDone) { @@ -163,6 +175,108 @@ public void isFileExists(String filepath, Promise promise) { } } + @ReactMethod + public void moveFile(String filepath, String destPath, Promise promise) { + try { + File externalStorageDirectory = Environment.getExternalStorageDirectory(); + if (externalStorageDirectory != null && !filepath.contains(externalStorageDirectory.getAbsolutePath())) { + filepath = externalStorageDirectory.getAbsolutePath() + filepath; + destPath = externalStorageDirectory.getAbsolutePath() + destPath; + } + + File inFile = new File(filepath); + + if (!inFile.renameTo(new File(destPath))) { + copyFile(filepath, destPath); + inFile.delete(); + } + + promise.resolve(true); + } catch (Exception ex) { + ex.printStackTrace(); + reject(promise, filepath, ex); + } + } + + @ReactMethod + public void copyFile(String filepath, String destPath, Promise promise) { + try { + copyFile(filepath, destPath); + + promise.resolve(null); + } catch (Exception ex) { + ex.printStackTrace(); + reject(promise, filepath, ex); + } + } + + @ReactMethod + public void unlink(String filepath, Promise promise) { + try { + File file = new File(filepath); + + if (!file.exists()) throw new Exception("File does not exist"); + + DeleteRecursive(file); + + promise.resolve(null); + } catch (Exception ex) { + ex.printStackTrace(); + reject(promise, filepath, ex); + } + } + + private void DeleteRecursive(File fileOrDirectory) { + if (fileOrDirectory.isDirectory()) { + for (File child : fileOrDirectory.listFiles()) { + DeleteRecursive(child); + } + } + + fileOrDirectory.delete(); + } + + private void copyFile(String filepath, String destPath) throws IOException, IORejectionException { + InputStream in = getInputStream(filepath); + OutputStream out = getOutputStream(destPath, false); + + byte[] buffer = new byte[1024]; + int length; + while ((length = in.read(buffer)) > 0) { + out.write(buffer, 0, length); + } + in.close(); + out.close(); + } + + private InputStream getInputStream(String filepath) throws IORejectionException { + Uri uri = getFileUri(filepath); + InputStream stream; + try { + stream = reactContext.getContentResolver().openInputStream(uri); + } catch (FileNotFoundException ex) { + throw new IORejectionException("ENOENT", "ENOENT: no such file or directory, open '" + filepath + "'"); + } + if (stream == null) { + throw new IORejectionException("ENOENT", "ENOENT: could not open an input stream for '" + filepath + "'"); + } + return stream; + } + + private OutputStream getOutputStream(String filepath, boolean append) throws IORejectionException { + Uri uri = getFileUri(filepath); + OutputStream stream; + try { + stream = reactContext.getContentResolver().openOutputStream(uri, append ? "wa" : "w"); + } catch (FileNotFoundException ex) { + throw new IORejectionException("ENOENT", "ENOENT: no such file or directory, open '" + filepath + "'"); + } + if (stream == null) { + throw new IORejectionException("ENOENT", "ENOENT: could not open an output stream for '" + filepath + "'"); + } + return stream; + } + private void rejectFileNotFound(Promise promise, String filepath) { promise.reject("ENOENT", "ENOENT: no such file or directory, open '" + filepath + "'"); } diff --git a/index.js b/index.js index 2214362..dc6abd4 100644 --- a/index.js +++ b/index.js @@ -19,6 +19,7 @@ const defaults = { downloadDestDirectory: DocumentDirectoryPath, downloadApkName: DEFAULT_DOWNLOAD_APK_NAME, downloadApkEnd: path => RNEasyUpgrade.installApk(path), + shouldCheckApkHasDownloaded: true, onError: () => {} }; @@ -73,10 +74,13 @@ class AppUpgrade { return; } + const tempDownloadApkName = 'temp_download.apk'; + const tempDownloadPath = this.downloadDestDirectory + '/' + tempDownloadApkName; + const downloadConf = { downloadTitle: this.options.downloadTitle, downloadDescription: this.options.downloadDescription, - saveAsName: this.options.downloadApkName, + saveAsName: this.options.shouldCheckApkHasDownloaded ? tempDownloadApkName : this.options.downloadApkName, allowedInRoaming: true, allowedInMetered: true, showInDownloads: true, @@ -85,8 +89,11 @@ class AppUpgrade { }; jobId = 1; download(apkUrl, downloadConf) - .then(res => { + .then(async res => { jobId = -1; + if (this.options.shouldCheckApkHasDownloaded) { + await RNEasyUpgrade.moveFile(tempDownloadPath, this.downloadDestPath); + } this.options.downloadApkEnd(this.downloadDestPath); }) .catch(err => { @@ -132,9 +139,9 @@ class AppUpgrade { RNEasyUpgrade.openURL(trackViewUrl || this.trackViewUrl); } - startAppUpdate(apkUrl, appStoreUrl = this.trackViewUrl, options = { checkApkHasDownloaded: true }) { + startAppUpdate(apkUrl, appStoreUrl = this.trackViewUrl) { if (isAndroid) { - if (options.checkApkHasDownloaded) { + if (this.options.shouldCheckApkHasDownloaded) { this.checkApkHasDownloaded().then(async hasDownloaded => { if (hasDownloaded) { this.options.downloadApkEnd(this.downloadDestPath);