Implement download and install apk
This commit is contained in:
parent
3c5792423e
commit
17ff366f51
@ -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);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
@ -1,7 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="cn.reactnative.modules.update">
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
<application>
|
||||
<meta-data android:name="pushy_build_time" android:value="@string/pushy_build_time" />
|
||||
<provider
|
||||
android:name=".PushyFileProvider"
|
||||
android:authorities="${applicationId}.pushy.fileprovider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/file_paths" />
|
||||
</provider>
|
||||
</application>
|
||||
</manifest>
|
||||
|
@ -72,7 +72,7 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
|
||||
|
||||
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<DownloadTaskParams, long[], Void> {
|
||||
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<DownloadTaskParams, long[], Void> {
|
||||
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<DownloadTaskParams, long[], Void> {
|
||||
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<DownloadTaskParams, long[], Void> {
|
||||
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<DownloadTaskParams, long[], Void> {
|
||||
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();
|
||||
|
@ -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;
|
||||
|
@ -0,0 +1,14 @@
|
||||
package cn.reactnative.modules.update;
|
||||
|
||||
import android.support.v4.content.FileProvider;
|
||||
|
||||
/**
|
||||
* Providing a custom {@code FileProvider} prevents manifest {@code <provider>} 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.
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
3
android/src/main/res/xml/file_paths.xml
Normal file
3
android/src/main/res/xml/file_paths.xml
Normal file
@ -0,0 +1,3 @@
|
||||
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<files-path name="." path="."/>
|
||||
</paths>
|
10
lib/index.d.ts
vendored
10
lib/index.d.ts
vendored
@ -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;
|
||||
|
||||
|
24
lib/index.js
24
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();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user