diff --git a/Example/testHotUpdate/src/index.js b/Example/testHotUpdate/src/index.js
index 6859e58..164158b 100644
--- a/Example/testHotUpdate/src/index.js
+++ b/Example/testHotUpdate/src/index.js
@@ -21,6 +21,7 @@ import {
switchVersion,
switchVersionLater,
markSuccess,
+ downloadAndInstallApk,
} from 'react-native-update';
import _updateConfig from '../update.json';
@@ -94,11 +95,29 @@ export default class App extends Component {
return;
}
if (info.expired) {
- Alert.alert('提示', '您的应用版本已更新,请前往应用商店下载新的版本', [
+ Alert.alert('提示', '您的应用版本已更新,点击确定下载安装新版本', [
{
text: '确定',
onPress: () => {
- info.downloadUrl && Linking.openURL(info.downloadUrl);
+ if (info.downloadUrl) {
+ // apk可直接下载安装
+ if (
+ Platform.OS === 'android' &&
+ info.downloadUrl.endsWith('.apk')
+ ) {
+ downloadAndInstallApk({
+ url: info.downloadUrl,
+ onDownloadProgress: ({received, total}) => {
+ this.setState({
+ received,
+ total,
+ });
+ },
+ });
+ } else {
+ Linking.openURL(info.downloadUrl);
+ }
+ }
},
},
]);
diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml
index 868a3a1..99a484e 100644
--- a/android/src/main/AndroidManifest.xml
+++ b/android/src/main/AndroidManifest.xml
@@ -1,7 +1,17 @@
+
+
+
+
diff --git a/android/src/main/java/cn/reactnative/modules/update/DownloadTask.java b/android/src/main/java/cn/reactnative/modules/update/DownloadTask.java
index 36ec1b1..917553b 100644
--- a/android/src/main/java/cn/reactnative/modules/update/DownloadTask.java
+++ b/android/src/main/java/cn/reactnative/modules/update/DownloadTask.java
@@ -72,7 +72,7 @@ class DownloadTask extends AsyncTask {
private void downloadFile(DownloadTaskParams param) throws IOException {
String url = param.url;
- File writePath = param.zipFilePath;
+ File writePath = param.targetFile;
this.hash = param.hash;
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url)
@@ -243,10 +243,10 @@ class DownloadTask extends AsyncTask {
copyFilesWithBlacklist("", from, to, blackList);
}
- private void doDownload(DownloadTaskParams param) throws IOException {
+ private void doFullPatch(DownloadTaskParams param) throws IOException {
downloadFile(param);
- ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(param.zipFilePath)));
+ ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(param.targetFile)));
ZipEntry ze;
String filename;
@@ -303,7 +303,7 @@ class DownloadTask extends AsyncTask {
private void doPatchFromApk(DownloadTaskParams param) throws IOException, JSONException {
downloadFile(param);
- ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(param.zipFilePath)));
+ ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(param.targetFile)));
ZipEntry ze;
int count;
String filename;
@@ -379,7 +379,7 @@ class DownloadTask extends AsyncTask {
private void doPatchFromPpk(DownloadTaskParams param) throws IOException, JSONException {
downloadFile(param);
- ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(param.zipFilePath)));
+ ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(param.targetFile)));
ZipEntry ze;
int count;
String filename;
@@ -464,8 +464,8 @@ class DownloadTask extends AsyncTask {
protected Void doInBackground(DownloadTaskParams... params) {
try {
switch (params[0].type) {
- case DownloadTaskParams.TASK_TYPE_FULL_DOWNLOAD:
- doDownload(params[0]);
+ case DownloadTaskParams.TASK_TYPE_PATCH_FULL:
+ doFullPatch(params[0]);
break;
case DownloadTaskParams.TASK_TYPE_PATCH_FROM_APK:
doPatchFromApk(params[0]);
@@ -473,11 +473,18 @@ class DownloadTask extends AsyncTask {
case DownloadTaskParams.TASK_TYPE_PATCH_FROM_PPK:
doPatchFromPpk(params[0]);
break;
- case DownloadTaskParams.TASK_TYPE_CLEARUP:
+ case DownloadTaskParams.TASK_TYPE_CLEANUP:
doCleanUp(params[0]);
break;
+ case DownloadTaskParams.TASK_TYPE_PLAIN_DOWNLOAD:
+ downloadFile(params[0]);
+ break;
+ default:
+ break;
+ }
+ if (params[0].listener != null) {
+ params[0].listener.onDownloadCompleted(params[0]);
}
- params[0].listener.onDownloadCompleted();
} catch (Throwable e) {
if (UpdateContext.DEBUG) {
e.printStackTrace();
diff --git a/android/src/main/java/cn/reactnative/modules/update/DownloadTaskParams.java b/android/src/main/java/cn/reactnative/modules/update/DownloadTaskParams.java
index 042cb69..248a392 100644
--- a/android/src/main/java/cn/reactnative/modules/update/DownloadTaskParams.java
+++ b/android/src/main/java/cn/reactnative/modules/update/DownloadTaskParams.java
@@ -8,17 +8,18 @@ import java.io.File;
* Created by tdzl2003 on 3/31/16.
*/
class DownloadTaskParams {
- static final int TASK_TYPE_FULL_DOWNLOAD = 1;
+ static final int TASK_TYPE_PATCH_FULL = 1;
static final int TASK_TYPE_PATCH_FROM_APK = 2;
static final int TASK_TYPE_PATCH_FROM_PPK = 3;
+ static final int TASK_TYPE_PLAIN_DOWNLOAD = 4;
- static final int TASK_TYPE_CLEARUP = 0; //Keep hash & originHash
+ static final int TASK_TYPE_CLEANUP = 0; //Keep hash & originHash
int type;
String url;
String hash;
String originHash;
- File zipFilePath;
+ File targetFile;
File unzipDirectory;
File originDirectory;
UpdateContext.DownloadFileListener listener;
diff --git a/android/src/main/java/cn/reactnative/modules/update/PushyFileProvider.java b/android/src/main/java/cn/reactnative/modules/update/PushyFileProvider.java
new file mode 100644
index 0000000..b7af30c
--- /dev/null
+++ b/android/src/main/java/cn/reactnative/modules/update/PushyFileProvider.java
@@ -0,0 +1,14 @@
+package cn.reactnative.modules.update;
+
+import android.support.v4.content.FileProvider;
+
+/**
+ * Providing a custom {@code FileProvider} prevents manifest {@code } name collisions.
+ *
+ * See https://developer.android.com/guide/topics/manifest/provider-element.html for details.
+ */
+public class PushyFileProvider extends FileProvider {
+
+ // This class intentionally left blank.
+
+}
\ No newline at end of file
diff --git a/android/src/main/java/cn/reactnative/modules/update/UpdateContext.java b/android/src/main/java/cn/reactnative/modules/update/UpdateContext.java
index 3582dac..ab782fb 100644
--- a/android/src/main/java/cn/reactnative/modules/update/UpdateContext.java
+++ b/android/src/main/java/cn/reactnative/modules/update/UpdateContext.java
@@ -46,7 +46,7 @@ public class UpdateContext {
editor.putString("packageVersion", packageVersion);
editor.apply();
- this.clearUp();
+ this.cleanUp();
}
}
@@ -86,28 +86,39 @@ public class UpdateContext {
}
public interface DownloadFileListener {
- void onDownloadCompleted();
+ void onDownloadCompleted(DownloadTaskParams params);
void onDownloadFailed(Throwable error);
}
- public void downloadFile(String url, String hash, DownloadFileListener listener) {
+ public void downloadFullUpdate(String url, String hash, DownloadFileListener listener) {
DownloadTaskParams params = new DownloadTaskParams();
- params.type = DownloadTaskParams.TASK_TYPE_FULL_DOWNLOAD;
+ params.type = DownloadTaskParams.TASK_TYPE_PATCH_FULL;
params.url = url;
params.hash = hash;
params.listener = listener;
- params.zipFilePath = new File(rootDir, hash + ".ppk");
+ params.targetFile = new File(rootDir, hash + ".ppk");
params.unzipDirectory = new File(rootDir, hash);
new DownloadTask(context).executeOnExecutor(this.executor, params);
}
+ public void downloadFile(String url, String hash, String fileName, DownloadFileListener listener) {
+ DownloadTaskParams params = new DownloadTaskParams();
+ params.type = DownloadTaskParams.TASK_TYPE_PLAIN_DOWNLOAD;
+ params.url = url;
+ params.hash = hash;
+ params.listener = listener;
+ params.targetFile = new File(rootDir, fileName);
+// params.unzipDirectory = new File(rootDir, hash);
+ new DownloadTask(context).executeOnExecutor(this.executor, params);
+ }
+
public void downloadPatchFromApk(String url, String hash, DownloadFileListener listener) {
DownloadTaskParams params = new DownloadTaskParams();
params.type = DownloadTaskParams.TASK_TYPE_PATCH_FROM_APK;
params.url = url;
params.hash = hash;
params.listener = listener;
- params.zipFilePath = new File(rootDir, hash + ".apk.patch");
+ params.targetFile = new File(rootDir, hash + ".apk.patch");
params.unzipDirectory = new File(rootDir, hash);
new DownloadTask(context).executeOnExecutor(this.executor, params);
}
@@ -119,7 +130,7 @@ public class UpdateContext {
params.hash = hash;
params.originHash = originHash;
params.listener = listener;
- params.zipFilePath = new File(rootDir, originHash + "-" + hash + ".ppk.patch");
+ params.targetFile = new File(rootDir, originHash + "-" + hash + ".ppk.patch");
params.unzipDirectory = new File(rootDir, hash);
params.originDirectory = new File(rootDir, originHash);
new DownloadTask(context).executeOnExecutor(this.executor, params);
@@ -174,7 +185,7 @@ public class UpdateContext {
editor.remove("lastVersion");
editor.apply();
- this.clearUp();
+ this.cleanUp();
}
public void clearFirstTime() {
@@ -182,7 +193,7 @@ public class UpdateContext {
editor.putBoolean("firstTime", false);
editor.apply();
- this.clearUp();
+ this.cleanUp();
}
public void clearRollbackMark() {
@@ -190,7 +201,7 @@ public class UpdateContext {
editor.putBoolean("rolledBack", false);
editor.apply();
- this.clearUp();
+ this.cleanUp();
}
@@ -256,21 +267,12 @@ public class UpdateContext {
return lastVersion;
}
- private void clearUp() {
+ private void cleanUp() {
DownloadTaskParams params = new DownloadTaskParams();
- params.type = DownloadTaskParams.TASK_TYPE_CLEARUP;
+ params.type = DownloadTaskParams.TASK_TYPE_CLEANUP;
params.hash = sp.getString("currentVersion", null);
params.originHash = sp.getString("lastVersion", null);
params.unzipDirectory = rootDir;
- params.listener = new DownloadFileListener() {
- @Override
- public void onDownloadCompleted() {
- }
-
- @Override
- public void onDownloadFailed(Throwable error) {
- }
- };
new DownloadTask(context).executeOnExecutor(this.executor, params);
}
}
diff --git a/android/src/main/java/cn/reactnative/modules/update/UpdateModule.java b/android/src/main/java/cn/reactnative/modules/update/UpdateModule.java
index 8a6051b..0ff24c1 100644
--- a/android/src/main/java/cn/reactnative/modules/update/UpdateModule.java
+++ b/android/src/main/java/cn/reactnative/modules/update/UpdateModule.java
@@ -3,6 +3,8 @@ package cn.reactnative.modules.update;
import android.app.Activity;
import android.app.Application;
import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
import android.util.Log;
import com.facebook.react.ReactApplication;
@@ -18,11 +20,14 @@ import com.facebook.react.bridge.JSBundleLoader;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
+import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
+import static android.support.v4.content.FileProvider.getUriForFile;
+
/**
* Created by tdzl2003 on 3/31/16.
*/
@@ -71,9 +76,9 @@ public class UpdateModule extends ReactContextBaseJavaModule{
public void downloadUpdate(ReadableMap options, final Promise promise){
String url = options.getString("updateUrl");
String hash = options.getString("hash");
- updateContext.downloadFile(url, hash, new UpdateContext.DownloadFileListener() {
+ updateContext.downloadFullUpdate(url, hash, new UpdateContext.DownloadFileListener() {
@Override
- public void onDownloadCompleted() {
+ public void onDownloadCompleted(DownloadTaskParams params) {
promise.resolve(null);
}
@@ -84,13 +89,58 @@ public class UpdateModule extends ReactContextBaseJavaModule{
});
}
+ @ReactMethod
+ public void downloadAndInstallApk(ReadableMap options, final Promise promise){
+ String url = options.getString("url");
+ String hash = options.getString("hash");
+ String target = options.getString("target");
+ updateContext.downloadFile(url, hash, target, new UpdateContext.DownloadFileListener() {
+ @Override
+ public void onDownloadCompleted(DownloadTaskParams params) {
+ installApk(params.targetFile);
+ promise.resolve(null);
+ }
+
+ @Override
+ public void onDownloadFailed(Throwable error) {
+ promise.reject(error);
+ }
+ });
+ }
+
+ // install downloaded apk
+ @ReactMethod
+ public static void installApk(String url) {
+ File toInstall = new File(url);
+ installApk(toInstall);
+ }
+
+ public static void installApk(File toInstall) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ Uri apkUri = getUriForFile(mContext, mContext.getPackageName() + ".pushy.fileprovider", toInstall);
+
+ Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
+ intent.setData(apkUri);
+ intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(intent);
+ } else {
+ Uri apkUri = Uri.fromFile(toInstall);
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(intent);
+ }
+ }
+
+
@ReactMethod
public void downloadPatchFromPackage(ReadableMap options, final Promise promise){
String url = options.getString("updateUrl");
String hash = options.getString("hash");
updateContext.downloadPatchFromApk(url, hash, new UpdateContext.DownloadFileListener() {
@Override
- public void onDownloadCompleted() {
+ public void onDownloadCompleted(DownloadTaskParams params) {
promise.resolve(null);
}
@@ -108,7 +158,7 @@ public class UpdateModule extends ReactContextBaseJavaModule{
String originHash = options.getString("originHash");
updateContext.downloadPatchFromPpk(url, hash, originHash, new UpdateContext.DownloadFileListener() {
@Override
- public void onDownloadCompleted() {
+ public void onDownloadCompleted(DownloadTaskParams params) {
promise.resolve(null);
}
diff --git a/android/src/main/res/xml/file_paths.xml b/android/src/main/res/xml/file_paths.xml
new file mode 100644
index 0000000..a205351
--- /dev/null
+++ b/android/src/main/res/xml/file_paths.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/lib/index.d.ts b/lib/index.d.ts
index 3f5958f..561e241 100644
--- a/lib/index.d.ts
+++ b/lib/index.d.ts
@@ -43,6 +43,14 @@ export function switchVersionLater(hash: string): void;
export function markSuccess(): void;
+export async function downloadAndInstallApk({
+ url,
+ onDownloadProgress,
+}: {
+ url: string;
+ onDownloadProgress?: (data: ProgressData) => void;
+}): void;
+
/**
* @param {string} main - The main api endpoint
* @param {string[]} [backups] - The back up endpoints.
@@ -55,7 +63,7 @@ export function setCustomEndpoints({
backupQueryUrl,
}: {
main: string;
- backUps?: string[];
+ backups?: string[];
backupQueryUrl?: string;
}): void;
diff --git a/lib/index.js b/lib/index.js
index 59d3f39..2f05597 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -143,10 +143,11 @@ export async function downloadUpdate(options, eventListeners) {
if (!options.update) {
return;
}
+ let progressHandler;
if (eventListeners) {
if (eventListeners.onDownloadProgress) {
const downloadCallback = eventListeners.onDownloadProgress;
- eventEmitter.addListener('RCTPushyDownloadProgress', (progressData) => {
+ progressHandler = eventEmitter.addListener('RCTPushyDownloadProgress', (progressData) => {
if (progressData.hash === options.hash) {
downloadCallback(progressData);
}
@@ -167,7 +168,7 @@ export async function downloadUpdate(options, eventListeners) {
hash: options.hash,
});
}
- eventEmitter.removeAllListeners('RCTPushyDownloadProgress');
+ progressHandler && progressHandler.remove();
return options.hash;
}
@@ -188,3 +189,22 @@ export function markSuccess() {
logger('markSuccess');
Pushy.markSuccess();
}
+
+export async function downloadAndInstallApk({ url, onDownloadProgress }) {
+ logger('downloadAndInstallApk');
+ let hash = Date.now().toString();
+ let progressHandler;
+ if (onDownloadProgress) {
+ progressHandler = eventEmitter.addListener('RCTPushyDownloadProgress', (progressData) => {
+ if (progressData.hash === hash) {
+ onDownloadProgress(progressData);
+ }
+ });
+ }
+ await Pushy.downloadAndInstallApk({
+ url,
+ target: 'update.apk',
+ hash,
+ });
+ progressHandler && progressHandler.remove();
+}