react-native-update 8.1.0 → 9.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/e2e_android.yml +49 -0
- package/.github/workflows/e2e_ios.yml +182 -0
- package/.github/workflows/scripts/adb_all_emulators.sh +10 -0
- package/.github/workflows/scripts/database.rules +13 -0
- package/.github/workflows/scripts/firebase.json +39 -0
- package/.github/workflows/scripts/firestore.indexes.json +72 -0
- package/.github/workflows/scripts/firestore.rules +17 -0
- package/.github/workflows/scripts/functions/package.json +24 -0
- package/.github/workflows/scripts/functions/src/exports.ts +13 -0
- package/.github/workflows/scripts/functions/src/index.ts +12 -0
- package/.github/workflows/scripts/functions/src/sample-data.ts +80 -0
- package/.github/workflows/scripts/functions/src/testFunctionCustomRegion.ts +14 -0
- package/.github/workflows/scripts/functions/src/testFunctionDefaultRegion.ts +70 -0
- package/.github/workflows/scripts/functions/tsconfig.json +16 -0
- package/.github/workflows/scripts/start-firebase-emulator.bat +6 -0
- package/.github/workflows/scripts/start-firebase-emulator.sh +44 -0
- package/.github/workflows/scripts/storage.rules +21 -0
- package/README.md +8 -6
- package/android/build.gradle +22 -0
- package/android/jni/hpatch.c +4 -4
- package/android/jni/hpatch.h +3 -3
- package/android/src/main/java/cn/reactnative/modules/update/DownloadTaskParams.java +0 -2
- package/android/src/main/java/cn/reactnative/modules/update/UpdateModuleImpl.java +265 -0
- package/android/src/main/java/cn/reactnative/modules/update/UpdatePackage.java +31 -20
- package/android/src/newarch/cn/reactnative/modules/update/UpdateModule.java +147 -0
- package/android/src/{main/java → oldarch}/cn/reactnative/modules/update/UpdateModule.java +6 -2
- package/e2e/jest.config.js +12 -0
- package/e2e/starter.test.js +23 -0
- package/ios/RCTPushy/{RCTPushy.m → RCTPushy.mm} +116 -44
- package/ios/pushy_build_time.txt +1 -1
- package/lib/NativeUpdate.js +51 -0
- package/lib/main.js +29 -23
- package/package.json +36 -2
- package/react-native-update.podspec +20 -2
- /package/ios/RCTPushy/HDiffPatch/{HDiffPatch.m → HDiffPatch.mm} +0 -0
- /package/ios/RCTPushy/{RCTPushyDownloader.m → RCTPushyDownloader.mm} +0 -0
- /package/ios/RCTPushy/{RCTPushyManager.m → RCTPushyManager.mm} +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
rules_version = '2';
|
|
2
|
+
service firebase.storage {
|
|
3
|
+
match /b/{bucket}/o {
|
|
4
|
+
match /{document=**} {
|
|
5
|
+
allow read, write: if false;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
match /writeOnly.jpeg {
|
|
9
|
+
allow read: if false;
|
|
10
|
+
allow write: if true;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
match /playground/{document=**} {
|
|
14
|
+
allow read, write: if true;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
match /react-native-firebase-testing/{document=**} {
|
|
18
|
+
allow read, write: if true;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
package/README.md
CHANGED
|
@@ -9,12 +9,13 @@
|
|
|
9
9
|
### 优势
|
|
10
10
|
|
|
11
11
|
1. 基于阿里云高速 CDN 分发,对比其他服务器在国外的热更新服务,分发更稳定,更新成功率极高。
|
|
12
|
-
2. 基于 bsdiff/hdiff
|
|
13
|
-
3.
|
|
14
|
-
4.
|
|
15
|
-
5.
|
|
16
|
-
6.
|
|
17
|
-
7.
|
|
12
|
+
2. 基于 bsdiff/hdiff 算法创建的**超小更新包**,通常版本迭代后在几十 KB 级别(其他全量热更新服务所需流量通常在几十 MB 级别)。
|
|
13
|
+
3. 始终跟进 RN 最新正式版本,第一时间提供支持。支持 hermes 字节码格式。(暂不支持新架构,会待其相对稳定后跟进)
|
|
14
|
+
4. 跨越多个版本进行更新时,只需要下载**一个更新包**,不需要逐版本依次更新。
|
|
15
|
+
5. 命令行工具 & 网页双端管理,版本发布过程简单便捷,完全可以集成 CI。
|
|
16
|
+
6. 支持崩溃回滚,安全可靠。
|
|
17
|
+
7. meta 信息及开放 API,提供更高扩展性。
|
|
18
|
+
8. 提供付费的专人技术支持。
|
|
18
19
|
|
|
19
20
|
### 本地开发
|
|
20
21
|
|
|
@@ -32,3 +33,4 @@ $ yarn start
|
|
|
32
33
|
本组件由[React Native 中文网](https://reactnative.cn/)独家发布,如有定制需求可以[联系我们](https://reactnative.cn/about.html#content)。
|
|
33
34
|
|
|
34
35
|
关于此插件发现任何问题,可以前往[Issues](https://github.com/reactnativecn/react-native-pushy/issues)发帖提问。
|
|
36
|
+
|
package/android/build.gradle
CHANGED
|
@@ -5,7 +5,15 @@ def safeExtGet(prop, fallback) {
|
|
|
5
5
|
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
+
def isNewArchitectureEnabled() {
|
|
9
|
+
return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true"
|
|
10
|
+
}
|
|
11
|
+
|
|
8
12
|
apply plugin: 'com.android.library'
|
|
13
|
+
if (isNewArchitectureEnabled()) {
|
|
14
|
+
apply plugin: 'com.facebook.react'
|
|
15
|
+
}
|
|
16
|
+
|
|
9
17
|
|
|
10
18
|
android {
|
|
11
19
|
compileSdkVersion safeExtGet('compileSdkVersion', 28)
|
|
@@ -17,11 +25,17 @@ android {
|
|
|
17
25
|
versionCode 1
|
|
18
26
|
versionName "1.0"
|
|
19
27
|
consumerProguardFiles "proguard.pro"
|
|
28
|
+
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
|
|
20
29
|
}
|
|
21
30
|
sourceSets {
|
|
22
31
|
main {
|
|
23
32
|
// let gradle pack the shared library into apk
|
|
24
33
|
jniLibs.srcDirs = ['./lib']
|
|
34
|
+
if (isNewArchitectureEnabled()) {
|
|
35
|
+
java.srcDirs += ['src/newarch']
|
|
36
|
+
} else {
|
|
37
|
+
java.srcDirs += ['src/oldarch']
|
|
38
|
+
}
|
|
25
39
|
}
|
|
26
40
|
}
|
|
27
41
|
|
|
@@ -44,4 +58,12 @@ repositories {
|
|
|
44
58
|
|
|
45
59
|
dependencies {
|
|
46
60
|
implementation 'com.facebook.react:react-native:+'
|
|
61
|
+
implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.0'
|
|
62
|
+
}
|
|
63
|
+
if (isNewArchitectureEnabled()) {
|
|
64
|
+
react {
|
|
65
|
+
jsRootDir = file("../lib/")
|
|
66
|
+
libraryName = "update"
|
|
67
|
+
codegenJavaPackageName = "cn.reactnative.modules.update"
|
|
68
|
+
}
|
|
47
69
|
}
|
package/android/jni/hpatch.c
CHANGED
|
@@ -24,7 +24,7 @@ int hpatch_getInfo_by_mem(hpatch_singleCompressedDiffInfo* out_patinfo,
|
|
|
24
24
|
mem_as_hStreamInput(&patStream,pat,pat+patsize);
|
|
25
25
|
if (!getSingleCompressedDiffInfo(out_patinfo,&patStream,0))
|
|
26
26
|
return kHPatch_error_info;//data error;
|
|
27
|
-
return kHPatch_ok; //ok
|
|
27
|
+
return kHPatch_ok; //ok
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
static hpatch_TDecompress* getDecompressPlugin(const char* compressType){
|
|
@@ -68,7 +68,7 @@ static int hpatch_by_stream(const hpatch_TStreamInput* old,hpatch_BOOL isLoadOld
|
|
|
68
68
|
_check(decompressPlugin,kHPatch_error_compressType);
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
|
-
{// mem
|
|
71
|
+
{// mem
|
|
72
72
|
size_t mem_size;
|
|
73
73
|
size_t oldSize=(size_t)old->streamSize;
|
|
74
74
|
isLoadOldAllToMem=isLoadOldAllToMem&&(old->streamSize<=kMaxLoadMemOldSize);
|
|
@@ -76,7 +76,7 @@ static int hpatch_by_stream(const hpatch_TStreamInput* old,hpatch_BOOL isLoadOld
|
|
|
76
76
|
mem_size=temp_cache_size+(isLoadOldAllToMem?oldSize:0);
|
|
77
77
|
temp_cache=malloc(mem_size);
|
|
78
78
|
_check(temp_cache,kHPatch_error_malloc);
|
|
79
|
-
if (isLoadOldAllToMem){//load old to mem
|
|
79
|
+
if (isLoadOldAllToMem){//load old to mem
|
|
80
80
|
uint8_t* oldMem=temp_cache+temp_cache_size;
|
|
81
81
|
_check(old->read(old,0,oldMem,oldMem+oldSize),kHPatch_error_old_fread);
|
|
82
82
|
mem_as_hStreamInput(&_old,oldMem,oldMem+oldSize);
|
|
@@ -95,7 +95,7 @@ _clear:
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
int hpatch_by_mem(const uint8_t* old,size_t oldsize,uint8_t* newBuf,size_t newsize,
|
|
98
|
-
const uint8_t* pat,size_t patsize,const hpatch_singleCompressedDiffInfo* patInfo){
|
|
98
|
+
const uint8_t* pat,size_t patsize,const hpatch_singleCompressedDiffInfo* patInfo){
|
|
99
99
|
hpatch_TStreamInput oldStream;
|
|
100
100
|
hpatch_TStreamInput patStream;
|
|
101
101
|
hpatch_TStreamOutput newStream;
|
package/android/jni/hpatch.h
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
// hpatch.h
|
|
2
|
-
// import HDiffPatch, support patchData created by "hdiffz -SD -c-lzma2 oldfile newfile patchfile"
|
|
1
|
+
// hpatch.h
|
|
2
|
+
// import HDiffPatch, support patchData created by "hdiffz -SD -c-lzma2 oldfile newfile patchfile"
|
|
3
3
|
// Copyright 2021 housisong, All rights reserved
|
|
4
4
|
|
|
5
5
|
#ifndef HDIFFPATCH_PATCH_H
|
|
@@ -41,4 +41,4 @@ int hpatch_by_file(const char* oldfile, const char* newfile, const char* patchfi
|
|
|
41
41
|
#ifdef __cplusplus
|
|
42
42
|
}
|
|
43
43
|
#endif
|
|
44
|
-
#endif //HDIFFPATCH_PATCH_H
|
|
44
|
+
#endif //HDIFFPATCH_PATCH_H
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
package cn.reactnative.modules.update;
|
|
2
|
+
|
|
3
|
+
import android.app.Activity;
|
|
4
|
+
import android.app.Application;
|
|
5
|
+
import android.util.Log;
|
|
6
|
+
import com.facebook.react.ReactApplication;
|
|
7
|
+
import com.facebook.react.ReactInstanceManager;
|
|
8
|
+
import com.facebook.react.bridge.JSBundleLoader;
|
|
9
|
+
import com.facebook.react.bridge.Promise;
|
|
10
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
11
|
+
import com.facebook.react.bridge.ReadableMap;
|
|
12
|
+
import com.facebook.react.bridge.UiThreadUtil;
|
|
13
|
+
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
14
|
+
|
|
15
|
+
import org.json.JSONObject;
|
|
16
|
+
|
|
17
|
+
import java.io.File;
|
|
18
|
+
import java.io.IOException;
|
|
19
|
+
import java.lang.reflect.Field;
|
|
20
|
+
import java.util.HashMap;
|
|
21
|
+
import java.util.Map;
|
|
22
|
+
import java.util.Optional;
|
|
23
|
+
import java.util.regex.Matcher;
|
|
24
|
+
import java.util.regex.Pattern;
|
|
25
|
+
|
|
26
|
+
public class UpdateModuleImpl {
|
|
27
|
+
|
|
28
|
+
public static final String NAME = "Pushy";
|
|
29
|
+
|
|
30
|
+
public static void downloadFullUpdate(UpdateContext updateContext, ReadableMap options, Promise promise) {
|
|
31
|
+
String url = options.getString("updateUrl");
|
|
32
|
+
String hash = options.getString("hash");
|
|
33
|
+
updateContext.downloadFullUpdate(url, hash, new UpdateContext.DownloadFileListener() {
|
|
34
|
+
@Override
|
|
35
|
+
public void onDownloadCompleted(DownloadTaskParams params) {
|
|
36
|
+
promise.resolve(null);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@Override
|
|
40
|
+
public void onDownloadFailed(Throwable error) {
|
|
41
|
+
promise.reject(error);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public static void downloadAndInstallApk(UpdateContext updateContext, ReadableMap options, Promise promise) {
|
|
47
|
+
String url = options.getString("url");
|
|
48
|
+
String hash = options.getString("hash");
|
|
49
|
+
String target = options.getString("target");
|
|
50
|
+
updateContext.downloadFile(url, hash, target, new UpdateContext.DownloadFileListener() {
|
|
51
|
+
@Override
|
|
52
|
+
public void onDownloadCompleted(DownloadTaskParams params) {
|
|
53
|
+
UpdateModule.installApk(params.targetFile);
|
|
54
|
+
promise.resolve(null);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@Override
|
|
58
|
+
public void onDownloadFailed(Throwable error) {
|
|
59
|
+
promise.reject(error);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
public static void installApk(String url) {
|
|
65
|
+
File toInstall = new File(url);
|
|
66
|
+
UpdateModule.installApk(toInstall);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
public static void downloadPatchFromPackage(UpdateContext updateContext, ReadableMap options, Promise promise) {
|
|
70
|
+
String url = options.getString("updateUrl");
|
|
71
|
+
String hash = options.getString("hash");
|
|
72
|
+
updateContext.downloadPatchFromApk(url, hash, new UpdateContext.DownloadFileListener() {
|
|
73
|
+
@Override
|
|
74
|
+
public void onDownloadCompleted(DownloadTaskParams params) {
|
|
75
|
+
promise.resolve(null);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
@Override
|
|
79
|
+
public void onDownloadFailed(Throwable error) {
|
|
80
|
+
promise.reject(error);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
public static void downloadPatchFromPpk(UpdateContext updateContext, ReadableMap options, Promise promise) {
|
|
86
|
+
try {
|
|
87
|
+
String url = options.getString("updateUrl");
|
|
88
|
+
String hash = options.getString("hash");
|
|
89
|
+
|
|
90
|
+
String originHash = options.getString("originHash");
|
|
91
|
+
|
|
92
|
+
updateContext.downloadPatchFromPpk(url, hash, originHash, new UpdateContext.DownloadFileListener() {
|
|
93
|
+
@Override
|
|
94
|
+
public void onDownloadCompleted(DownloadTaskParams params) {
|
|
95
|
+
promise.resolve(null);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
@Override
|
|
99
|
+
public void onDownloadFailed(Throwable error) {
|
|
100
|
+
promise.reject(error);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}catch (Exception e){
|
|
104
|
+
promise.reject("执行报错:"+e.getMessage());
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
public static void reloadUpdate(UpdateContext updateContext, ReactApplicationContext mContext, ReadableMap options,Promise promise) {
|
|
109
|
+
final String hash = options.getString("hash");
|
|
110
|
+
|
|
111
|
+
if(hash==null || hash.isEmpty()){
|
|
112
|
+
promise.reject("hash不能为空");
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
UiThreadUtil.runOnUiThread(new Runnable() {
|
|
116
|
+
@Override
|
|
117
|
+
public void run() {
|
|
118
|
+
try {
|
|
119
|
+
updateContext.switchVersion(hash);
|
|
120
|
+
Activity activity = mContext.getCurrentActivity();
|
|
121
|
+
Application application = activity.getApplication();
|
|
122
|
+
ReactInstanceManager instanceManager = updateContext.getCustomReactInstanceManager();
|
|
123
|
+
|
|
124
|
+
if (instanceManager == null) {
|
|
125
|
+
instanceManager = ((ReactApplication) application).getReactNativeHost().getReactInstanceManager();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
JSBundleLoader loader = JSBundleLoader.createFileLoader(UpdateContext.getBundleUrl(application));
|
|
130
|
+
Field loadField = instanceManager.getClass().getDeclaredField("mBundleLoader");
|
|
131
|
+
loadField.setAccessible(true);
|
|
132
|
+
loadField.set(instanceManager, loader);
|
|
133
|
+
} catch (Throwable err) {
|
|
134
|
+
promise.reject("pushy:"+err.getMessage());
|
|
135
|
+
Field jsBundleField = instanceManager.getClass().getDeclaredField("mJSBundleFile");
|
|
136
|
+
jsBundleField.setAccessible(true);
|
|
137
|
+
jsBundleField.set(instanceManager, UpdateContext.getBundleUrl(application));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
try {
|
|
141
|
+
instanceManager.recreateReactContextInBackground();
|
|
142
|
+
promise.resolve(true);
|
|
143
|
+
} catch (Throwable err) {
|
|
144
|
+
promise.reject("pushy:"+err.getMessage());
|
|
145
|
+
activity.recreate();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
} catch (Throwable err) {
|
|
149
|
+
promise.reject("pushy:switchVersion failed"+err.getMessage());
|
|
150
|
+
Log.e("pushy", "switchVersion failed", err);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
public static void setNeedUpdate(UpdateContext updateContext, ReadableMap options,Promise promise) {
|
|
158
|
+
try {
|
|
159
|
+
final String hash = options.getString("hash");
|
|
160
|
+
if(hash==null || hash.isEmpty()){
|
|
161
|
+
promise.reject("hash不能为空");
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
UiThreadUtil.runOnUiThread(new Runnable() {
|
|
165
|
+
@Override
|
|
166
|
+
public void run() {
|
|
167
|
+
try {
|
|
168
|
+
updateContext.switchVersion(hash);
|
|
169
|
+
promise.resolve(true);
|
|
170
|
+
} catch (Throwable err) {
|
|
171
|
+
promise.reject("switchVersionLater failed:"+err.getMessage());
|
|
172
|
+
Log.e("pushy", "switchVersionLater failed", err);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
}catch (Exception e){
|
|
177
|
+
promise.reject("执行报错:"+e.getMessage());
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
public static void markSuccess(UpdateContext updateContext,Promise promise) {
|
|
182
|
+
try {
|
|
183
|
+
UiThreadUtil.runOnUiThread(new Runnable() {
|
|
184
|
+
@Override
|
|
185
|
+
public void run() {
|
|
186
|
+
updateContext.markSuccess();
|
|
187
|
+
promise.resolve(true);
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
}catch (Exception e){
|
|
191
|
+
promise.reject("执行报错:"+e.getMessage());
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
public static void setBlockUpdate(UpdateContext updateContext, ReadableMap options,Promise promise) {
|
|
196
|
+
try {
|
|
197
|
+
final int until = options.getInt("until");
|
|
198
|
+
final String reason = options.getString("reason");
|
|
199
|
+
UiThreadUtil.runOnUiThread(new Runnable() {
|
|
200
|
+
@Override
|
|
201
|
+
public void run() {
|
|
202
|
+
updateContext.setBlockUpdate(until, reason);
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
promise.resolve(true);
|
|
206
|
+
}catch (Exception e){
|
|
207
|
+
promise.reject("执行报错:"+e.getMessage());
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
public static void setUuid(UpdateContext updateContext, String uuid, Promise promise) {
|
|
213
|
+
try {
|
|
214
|
+
UiThreadUtil.runOnUiThread(new Runnable() {
|
|
215
|
+
@Override
|
|
216
|
+
public void run() {
|
|
217
|
+
updateContext.setKv("uuid", uuid);
|
|
218
|
+
promise.resolve(true);
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
}catch (Exception e){
|
|
222
|
+
promise.reject("执行报错:"+e.getMessage());
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
public static boolean check(String json) {
|
|
228
|
+
ObjectMapper mapper = new ObjectMapper();
|
|
229
|
+
try {
|
|
230
|
+
mapper.readValue(json, Map.class);
|
|
231
|
+
System.out.println("String can be converted to Map");
|
|
232
|
+
return true;
|
|
233
|
+
} catch (IOException e) {
|
|
234
|
+
System.out.println("String cannot be converted to Map");
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
public static void setLocalHashInfo(UpdateContext updateContext, final String hash, final String info, Promise promise) {
|
|
241
|
+
UiThreadUtil.runOnUiThread(new Runnable() {
|
|
242
|
+
@Override
|
|
243
|
+
public void run() {
|
|
244
|
+
if(!check(info)){
|
|
245
|
+
updateContext.setKv("hash_" + hash, info);
|
|
246
|
+
promise.reject("校验报错:json字符串格式错误");
|
|
247
|
+
}else {
|
|
248
|
+
updateContext.setKv("hash_" + hash, info);
|
|
249
|
+
promise.resolve(true);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
public static void getLocalHashInfo(UpdateContext updateContext, final String hash, Promise promise) {
|
|
256
|
+
String value = updateContext.getKv("hash_" + hash);
|
|
257
|
+
if(check(value)){
|
|
258
|
+
promise.resolve(value);
|
|
259
|
+
}else {
|
|
260
|
+
promise.reject("校验报错:json字符串格式错误");
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
}
|
|
@@ -1,34 +1,45 @@
|
|
|
1
1
|
package cn.reactnative.modules.update;
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import com.facebook.react.
|
|
3
|
+
import androidx.annotation.Nullable;
|
|
4
|
+
import com.facebook.react.TurboReactPackage;
|
|
5
5
|
import com.facebook.react.bridge.NativeModule;
|
|
6
6
|
import com.facebook.react.bridge.ReactApplicationContext;
|
|
7
|
-
import com.facebook.react.
|
|
8
|
-
|
|
9
|
-
import java.util.
|
|
10
|
-
import java.util.
|
|
11
|
-
import java.util.List;
|
|
7
|
+
import com.facebook.react.module.model.ReactModuleInfo;
|
|
8
|
+
import com.facebook.react.module.model.ReactModuleInfoProvider;
|
|
9
|
+
import java.util.HashMap;
|
|
10
|
+
import java.util.Map;
|
|
12
11
|
|
|
13
12
|
/**
|
|
14
13
|
* Created by tdzl2003 on 3/31/16.
|
|
15
14
|
*/
|
|
16
|
-
public class UpdatePackage
|
|
17
|
-
|
|
15
|
+
public class UpdatePackage extends TurboReactPackage {
|
|
16
|
+
@Nullable
|
|
18
17
|
@Override
|
|
19
|
-
public
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
public List<Class<? extends JavaScriptModule>> createJSModules() {
|
|
27
|
-
return Collections.emptyList();
|
|
18
|
+
public NativeModule getModule(String name, ReactApplicationContext reactContext) {
|
|
19
|
+
if (name.equals(UpdateModuleImpl.NAME)) {
|
|
20
|
+
return new UpdateModule(reactContext);
|
|
21
|
+
} else {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
28
24
|
}
|
|
29
25
|
|
|
30
26
|
@Override
|
|
31
|
-
public
|
|
32
|
-
return
|
|
27
|
+
public ReactModuleInfoProvider getReactModuleInfoProvider() {
|
|
28
|
+
return () -> {
|
|
29
|
+
final Map<String, ReactModuleInfo> moduleInfos = new HashMap<>();
|
|
30
|
+
boolean isTurboModule = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
|
|
31
|
+
moduleInfos.put(
|
|
32
|
+
UpdateModuleImpl.NAME,
|
|
33
|
+
new ReactModuleInfo(
|
|
34
|
+
UpdateModuleImpl.NAME,
|
|
35
|
+
UpdateModuleImpl.NAME,
|
|
36
|
+
false, // canOverrideExistingModule
|
|
37
|
+
false, // needsEagerInit
|
|
38
|
+
true, // hasConstants
|
|
39
|
+
false, // isCxxModule
|
|
40
|
+
isTurboModule // isTurboModule
|
|
41
|
+
));
|
|
42
|
+
return moduleInfos;
|
|
43
|
+
};
|
|
33
44
|
}
|
|
34
45
|
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
package cn.reactnative.modules.update;
|
|
2
|
+
|
|
3
|
+
import static androidx.core.content.FileProvider.getUriForFile;
|
|
4
|
+
import android.content.Intent;
|
|
5
|
+
import android.net.Uri;
|
|
6
|
+
import android.os.Build;
|
|
7
|
+
import com.facebook.react.bridge.Promise;
|
|
8
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
9
|
+
import com.facebook.react.bridge.ReactContext;
|
|
10
|
+
import com.facebook.react.bridge.ReadableMap;
|
|
11
|
+
import com.facebook.react.bridge.WritableMap;
|
|
12
|
+
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
|
13
|
+
import java.io.File;
|
|
14
|
+
import java.util.HashMap;
|
|
15
|
+
import java.util.Map;
|
|
16
|
+
|
|
17
|
+
public class UpdateModule extends NativeUpdateSpec {
|
|
18
|
+
UpdateContext updateContext;
|
|
19
|
+
public static ReactApplicationContext mContext;
|
|
20
|
+
public UpdateModule(ReactApplicationContext reactContext, UpdateContext updateContext) {
|
|
21
|
+
super(reactContext);
|
|
22
|
+
this.updateContext = updateContext;
|
|
23
|
+
mContext = reactContext;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public UpdateModule(ReactApplicationContext reactContext) {
|
|
27
|
+
this(reactContext, new UpdateContext(reactContext.getApplicationContext()));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@Override
|
|
31
|
+
protected Map<String, Object> getTypedExportedConstants() {
|
|
32
|
+
final Map<String, Object> constants = new HashMap<>();
|
|
33
|
+
constants.put("downloadRootDir", updateContext.getRootDir());
|
|
34
|
+
constants.put("packageVersion", updateContext.getPackageVersion());
|
|
35
|
+
constants.put("currentVersion", updateContext.getCurrentVersion());
|
|
36
|
+
constants.put("buildTime", updateContext.getBuildTime());
|
|
37
|
+
constants.put("isUsingBundleUrl", updateContext.getIsUsingBundleUrl());
|
|
38
|
+
boolean isFirstTime = updateContext.isFirstTime();
|
|
39
|
+
constants.put("isFirstTime", isFirstTime);
|
|
40
|
+
if (isFirstTime) {
|
|
41
|
+
updateContext.clearFirstTime();
|
|
42
|
+
}
|
|
43
|
+
String rolledBackVersion = updateContext.rolledBackVersion();
|
|
44
|
+
constants.put("rolledBackVersion", rolledBackVersion);
|
|
45
|
+
if (rolledBackVersion != null) {
|
|
46
|
+
updateContext.clearRollbackMark();
|
|
47
|
+
}
|
|
48
|
+
constants.put("blockUpdate", updateContext.getBlockUpdate());
|
|
49
|
+
constants.put("uuid", updateContext.getKv("uuid"));
|
|
50
|
+
return constants;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@Override
|
|
54
|
+
public String getName() {
|
|
55
|
+
return UpdateModuleImpl.NAME;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@Override
|
|
59
|
+
public void downloadFullUpdate(ReadableMap options, final Promise promise) {
|
|
60
|
+
UpdateModuleImpl.downloadFullUpdate(this.updateContext,options,promise);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@Override
|
|
64
|
+
public void downloadAndInstallApk(ReadableMap options, final Promise promise) {
|
|
65
|
+
UpdateModuleImpl.downloadAndInstallApk(this.updateContext,options,promise);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
public static void installApk(File toInstall) {
|
|
69
|
+
Uri apkUri;
|
|
70
|
+
Intent intent;
|
|
71
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
72
|
+
apkUri = getUriForFile(mContext, mContext.getPackageName() + ".pushy.fileprovider", toInstall);
|
|
73
|
+
intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
|
|
74
|
+
intent.setData(apkUri);
|
|
75
|
+
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
|
76
|
+
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
77
|
+
} else {
|
|
78
|
+
apkUri = Uri.fromFile(toInstall);
|
|
79
|
+
intent = new Intent(Intent.ACTION_VIEW);
|
|
80
|
+
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
|
|
81
|
+
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
82
|
+
}
|
|
83
|
+
mContext.startActivity(intent);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
@Override
|
|
87
|
+
public void downloadPatchFromPackage(ReadableMap options, final Promise promise) {
|
|
88
|
+
UpdateModuleImpl.downloadPatchFromPackage(updateContext,options,promise);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@Override
|
|
92
|
+
public void downloadPatchFromPpk(ReadableMap options, final Promise promise) {
|
|
93
|
+
UpdateModuleImpl.downloadPatchFromPpk(updateContext,options,promise);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
@Override
|
|
97
|
+
public void reloadUpdate(ReadableMap options,Promise promise) {
|
|
98
|
+
UpdateModuleImpl.reloadUpdate(updateContext, mContext, options,promise);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
@Override
|
|
102
|
+
public void setNeedUpdate(ReadableMap options,Promise promise) {
|
|
103
|
+
UpdateModuleImpl.setNeedUpdate(updateContext, options,promise);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
@Override
|
|
107
|
+
public void markSuccess(Promise promise) {
|
|
108
|
+
UpdateModuleImpl.markSuccess(updateContext,promise);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
@Override
|
|
112
|
+
public void setBlockUpdate(ReadableMap options,Promise promise) {
|
|
113
|
+
UpdateModuleImpl.setBlockUpdate(updateContext,options,promise);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
@Override
|
|
117
|
+
public void setUuid(final String uuid, Promise promise) {
|
|
118
|
+
UpdateModuleImpl.setUuid(updateContext,uuid,promise);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
@Override
|
|
122
|
+
public void setLocalHashInfo(final String hash, final String info, final Promise promise) {
|
|
123
|
+
UpdateModuleImpl.setLocalHashInfo(updateContext,hash,info,promise);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
@Override
|
|
127
|
+
public void getLocalHashInfo(final String hash, final Promise promise) {
|
|
128
|
+
UpdateModuleImpl.getLocalHashInfo(updateContext,hash,promise);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
@Override
|
|
132
|
+
public void addListener(String eventName) {
|
|
133
|
+
// Set up any upstream listeners or background tasks as necessary
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
@Override
|
|
137
|
+
public void removeListeners(double count) {
|
|
138
|
+
// Remove upstream listeners, stop unnecessary background tasks
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
/* 发送事件*/
|
|
143
|
+
public static void sendEvent(String eventName, WritableMap params) {
|
|
144
|
+
((ReactContext) mContext).getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(eventName,
|
|
145
|
+
params);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -66,7 +66,7 @@ public class UpdateModule extends ReactContextBaseJavaModule {
|
|
|
66
66
|
|
|
67
67
|
@Override
|
|
68
68
|
public String getName() {
|
|
69
|
-
return
|
|
69
|
+
return UpdateModuleImpl.NAME;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
@ReactMethod
|
|
@@ -169,7 +169,7 @@ public class UpdateModule extends ReactContextBaseJavaModule {
|
|
|
169
169
|
}
|
|
170
170
|
|
|
171
171
|
@ReactMethod
|
|
172
|
-
public void reloadUpdate(ReadableMap options) {
|
|
172
|
+
public void reloadUpdate(ReadableMap options, final Promise promise) {
|
|
173
173
|
final String hash = options.getString("hash");
|
|
174
174
|
|
|
175
175
|
UiThreadUtil.runOnUiThread(new Runnable() {
|
|
@@ -191,6 +191,7 @@ public class UpdateModule extends ReactContextBaseJavaModule {
|
|
|
191
191
|
loadField.setAccessible(true);
|
|
192
192
|
loadField.set(instanceManager, loader);
|
|
193
193
|
} catch (Throwable err) {
|
|
194
|
+
promise.reject(err);
|
|
194
195
|
Field jsBundleField = instanceManager.getClass().getDeclaredField("mJSBundleFile");
|
|
195
196
|
jsBundleField.setAccessible(true);
|
|
196
197
|
jsBundleField.set(instanceManager, UpdateContext.getBundleUrl(application));
|
|
@@ -198,11 +199,14 @@ public class UpdateModule extends ReactContextBaseJavaModule {
|
|
|
198
199
|
|
|
199
200
|
try {
|
|
200
201
|
instanceManager.recreateReactContextInBackground();
|
|
202
|
+
promise.resolve(null);
|
|
201
203
|
} catch (Throwable err) {
|
|
202
204
|
activity.recreate();
|
|
205
|
+
promise.reject(err);
|
|
203
206
|
}
|
|
204
207
|
|
|
205
208
|
} catch (Throwable err) {
|
|
209
|
+
promise.reject(err);
|
|
206
210
|
Log.e("pushy", "switchVersion failed", err);
|
|
207
211
|
}
|
|
208
212
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** @type {import('@jest/types').Config.InitialOptions} */
|
|
2
|
+
module.exports = {
|
|
3
|
+
rootDir: '..',
|
|
4
|
+
testMatch: ['<rootDir>/e2e/**/*.test.js'],
|
|
5
|
+
testTimeout: 120000,
|
|
6
|
+
maxWorkers: 1,
|
|
7
|
+
globalSetup: 'detox/runners/jest/globalSetup',
|
|
8
|
+
globalTeardown: 'detox/runners/jest/globalTeardown',
|
|
9
|
+
reporters: ['detox/runners/jest/reporter'],
|
|
10
|
+
testEnvironment: 'detox/runners/jest/testEnvironment',
|
|
11
|
+
verbose: true,
|
|
12
|
+
};
|