react-native-ota-hot-update 2.1.15 → 2.2.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/README.md +16 -2
- package/android/generated/java/com/otahotupdate/NativeOtaHotUpdateSpec.java +8 -0
- package/android/generated/jni/RNOtaHotUpdateSpec-generated.cpp +12 -0
- package/android/generated/jni/react/renderer/components/RNOtaHotUpdateSpec/RNOtaHotUpdateSpecJSI-generated.cpp +14 -0
- package/android/generated/jni/react/renderer/components/RNOtaHotUpdateSpec/RNOtaHotUpdateSpecJSI.h +18 -0
- package/android/src/main/java/com/otahotupdate/CrashHandler.kt +55 -0
- package/android/src/main/java/com/otahotupdate/OtaHotUpdate.kt +15 -23
- package/android/src/main/java/com/otahotupdate/OtaHotUpdateModule.kt +36 -95
- package/android/src/main/java/com/otahotupdate/SharedPrefs.kt +1 -0
- package/android/src/main/java/com/otahotupdate/Utils.kt +94 -0
- package/android/src/oldarch/OtaHotUpdateSpec.kt +2 -0
- package/ios/OtaHotUpdate.mm +76 -10
- package/ios/generated/RNOtaHotUpdateSpec/RNOtaHotUpdateSpec-generated.mm +14 -0
- package/ios/generated/RNOtaHotUpdateSpec/RNOtaHotUpdateSpec.h +6 -0
- package/ios/generated/RNOtaHotUpdateSpecJSI-generated.cpp +14 -0
- package/ios/generated/RNOtaHotUpdateSpecJSI.h +18 -0
- package/lib/commonjs/NativeOtaHotUpdate.js.map +1 -1
- package/lib/commonjs/index.js +45 -27
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/NativeOtaHotUpdate.js.map +1 -1
- package/lib/module/index.js +45 -27
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/commonjs/src/NativeOtaHotUpdate.d.ts +2 -0
- package/lib/typescript/commonjs/src/NativeOtaHotUpdate.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/index.d.ts +5 -1
- package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/type.d.ts +10 -0
- package/lib/typescript/commonjs/src/type.d.ts.map +1 -1
- package/lib/typescript/module/src/NativeOtaHotUpdate.d.ts +2 -0
- package/lib/typescript/module/src/NativeOtaHotUpdate.d.ts.map +1 -1
- package/lib/typescript/module/src/index.d.ts +5 -1
- package/lib/typescript/module/src/index.d.ts.map +1 -1
- package/lib/typescript/module/src/type.d.ts +10 -0
- package/lib/typescript/module/src/type.d.ts.map +1 -1
- package/package.json +2 -13
- package/plugin/build/index.js +1 -1
- package/plugin/src/index.ts +1 -1
- package/react-native.config.js +1 -1
- package/src/NativeOtaHotUpdate.ts +2 -0
- package/src/index.tsx +52 -30
- package/src/type.ts +12 -0
package/README.md
CHANGED
|
@@ -35,6 +35,17 @@ Auto linking already, need pod install for ios:
|
|
|
35
35
|
cd ios && pod install
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
+
### Expo
|
|
39
|
+
|
|
40
|
+
Modify `app.json`:
|
|
41
|
+
|
|
42
|
+
```angular2html
|
|
43
|
+
"plugins": [
|
|
44
|
+
"react-native-ota-hot-update",
|
|
45
|
+
...
|
|
46
|
+
]
|
|
47
|
+
```
|
|
48
|
+
|
|
38
49
|
### IOS
|
|
39
50
|
Open `AppDelegate.m` and add this:
|
|
40
51
|
|
|
@@ -132,7 +143,7 @@ Open `MainApplication.kt` and add these codes bellow:
|
|
|
132
143
|
import com.otahotupdate.OtaHotUpdate
|
|
133
144
|
...
|
|
134
145
|
override fun getJSBundleFile(): String? {
|
|
135
|
-
return OtaHotUpdate.bundleJS
|
|
146
|
+
return OtaHotUpdate.bundleJS(this@MainApplication)
|
|
136
147
|
}
|
|
137
148
|
|
|
138
149
|
```
|
|
@@ -142,9 +153,12 @@ MainApplication.java:
|
|
|
142
153
|
@Nullable
|
|
143
154
|
@Override
|
|
144
155
|
protected String getJSBundleFile() {
|
|
145
|
-
return OtaHotUpdate.getBundleJS();
|
|
156
|
+
return OtaHotUpdate.getBundleJS(this);
|
|
146
157
|
}
|
|
147
158
|
```
|
|
159
|
+
|
|
160
|
+
For java it maybe can be like: `OtaHotUpdate.Companion.getBundleJS(this)` depend on kotlin / jdk version on your project, you can use android studio to get the correct format coding.
|
|
161
|
+
|
|
148
162
|
Open `AndroidManifest.xml` :
|
|
149
163
|
|
|
150
164
|
`<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />`
|
|
@@ -52,10 +52,18 @@ public abstract class NativeOtaHotUpdateSpec extends ReactContextBaseJavaModule
|
|
|
52
52
|
@DoNotStrip
|
|
53
53
|
public abstract void getCurrentVersion(double a, Promise promise);
|
|
54
54
|
|
|
55
|
+
@ReactMethod
|
|
56
|
+
@DoNotStrip
|
|
57
|
+
public abstract void getUpdateMetadata(double a, Promise promise);
|
|
58
|
+
|
|
55
59
|
@ReactMethod
|
|
56
60
|
@DoNotStrip
|
|
57
61
|
public abstract void setCurrentVersion(String version, Promise promise);
|
|
58
62
|
|
|
63
|
+
@ReactMethod
|
|
64
|
+
@DoNotStrip
|
|
65
|
+
public abstract void setUpdateMetadata(String metadata, Promise promise);
|
|
66
|
+
|
|
59
67
|
@ReactMethod
|
|
60
68
|
@DoNotStrip
|
|
61
69
|
public abstract void rollbackToPreviousBundle(double a, Promise promise);
|
|
@@ -37,11 +37,21 @@ static facebook::jsi::Value __hostFunction_NativeOtaHotUpdateSpecJSI_getCurrentV
|
|
|
37
37
|
return static_cast<JavaTurboModule &>(turboModule).invokeJavaMethod(rt, PromiseKind, "getCurrentVersion", "(DLcom/facebook/react/bridge/Promise;)V", args, count, cachedMethodId);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
static facebook::jsi::Value __hostFunction_NativeOtaHotUpdateSpecJSI_getUpdateMetadata(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
|
|
41
|
+
static jmethodID cachedMethodId = nullptr;
|
|
42
|
+
return static_cast<JavaTurboModule &>(turboModule).invokeJavaMethod(rt, PromiseKind, "getUpdateMetadata", "(DLcom/facebook/react/bridge/Promise;)V", args, count, cachedMethodId);
|
|
43
|
+
}
|
|
44
|
+
|
|
40
45
|
static facebook::jsi::Value __hostFunction_NativeOtaHotUpdateSpecJSI_setCurrentVersion(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
|
|
41
46
|
static jmethodID cachedMethodId = nullptr;
|
|
42
47
|
return static_cast<JavaTurboModule &>(turboModule).invokeJavaMethod(rt, PromiseKind, "setCurrentVersion", "(Ljava/lang/String;Lcom/facebook/react/bridge/Promise;)V", args, count, cachedMethodId);
|
|
43
48
|
}
|
|
44
49
|
|
|
50
|
+
static facebook::jsi::Value __hostFunction_NativeOtaHotUpdateSpecJSI_setUpdateMetadata(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
|
|
51
|
+
static jmethodID cachedMethodId = nullptr;
|
|
52
|
+
return static_cast<JavaTurboModule &>(turboModule).invokeJavaMethod(rt, PromiseKind, "setUpdateMetadata", "(Ljava/lang/String;Lcom/facebook/react/bridge/Promise;)V", args, count, cachedMethodId);
|
|
53
|
+
}
|
|
54
|
+
|
|
45
55
|
static facebook::jsi::Value __hostFunction_NativeOtaHotUpdateSpecJSI_rollbackToPreviousBundle(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
|
|
46
56
|
static jmethodID cachedMethodId = nullptr;
|
|
47
57
|
return static_cast<JavaTurboModule &>(turboModule).invokeJavaMethod(rt, PromiseKind, "rollbackToPreviousBundle", "(DLcom/facebook/react/bridge/Promise;)V", args, count, cachedMethodId);
|
|
@@ -54,7 +64,9 @@ NativeOtaHotUpdateSpecJSI::NativeOtaHotUpdateSpecJSI(const JavaTurboModule::Init
|
|
|
54
64
|
methodMap_["deleteBundle"] = MethodMetadata {1, __hostFunction_NativeOtaHotUpdateSpecJSI_deleteBundle};
|
|
55
65
|
methodMap_["restart"] = MethodMetadata {0, __hostFunction_NativeOtaHotUpdateSpecJSI_restart};
|
|
56
66
|
methodMap_["getCurrentVersion"] = MethodMetadata {1, __hostFunction_NativeOtaHotUpdateSpecJSI_getCurrentVersion};
|
|
67
|
+
methodMap_["getUpdateMetadata"] = MethodMetadata {1, __hostFunction_NativeOtaHotUpdateSpecJSI_getUpdateMetadata};
|
|
57
68
|
methodMap_["setCurrentVersion"] = MethodMetadata {1, __hostFunction_NativeOtaHotUpdateSpecJSI_setCurrentVersion};
|
|
69
|
+
methodMap_["setUpdateMetadata"] = MethodMetadata {1, __hostFunction_NativeOtaHotUpdateSpecJSI_setUpdateMetadata};
|
|
58
70
|
methodMap_["rollbackToPreviousBundle"] = MethodMetadata {1, __hostFunction_NativeOtaHotUpdateSpecJSI_rollbackToPreviousBundle};
|
|
59
71
|
}
|
|
60
72
|
|
|
@@ -42,12 +42,24 @@ static jsi::Value __hostFunction_NativeOtaHotUpdateCxxSpecJSI_getCurrentVersion(
|
|
|
42
42
|
count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asNumber()
|
|
43
43
|
);
|
|
44
44
|
}
|
|
45
|
+
static jsi::Value __hostFunction_NativeOtaHotUpdateCxxSpecJSI_getUpdateMetadata(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
|
|
46
|
+
return static_cast<NativeOtaHotUpdateCxxSpecJSI *>(&turboModule)->getUpdateMetadata(
|
|
47
|
+
rt,
|
|
48
|
+
count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asNumber()
|
|
49
|
+
);
|
|
50
|
+
}
|
|
45
51
|
static jsi::Value __hostFunction_NativeOtaHotUpdateCxxSpecJSI_setCurrentVersion(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
|
|
46
52
|
return static_cast<NativeOtaHotUpdateCxxSpecJSI *>(&turboModule)->setCurrentVersion(
|
|
47
53
|
rt,
|
|
48
54
|
count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asString(rt)
|
|
49
55
|
);
|
|
50
56
|
}
|
|
57
|
+
static jsi::Value __hostFunction_NativeOtaHotUpdateCxxSpecJSI_setUpdateMetadata(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
|
|
58
|
+
return static_cast<NativeOtaHotUpdateCxxSpecJSI *>(&turboModule)->setUpdateMetadata(
|
|
59
|
+
rt,
|
|
60
|
+
count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asString(rt)
|
|
61
|
+
);
|
|
62
|
+
}
|
|
51
63
|
static jsi::Value __hostFunction_NativeOtaHotUpdateCxxSpecJSI_rollbackToPreviousBundle(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
|
|
52
64
|
return static_cast<NativeOtaHotUpdateCxxSpecJSI *>(&turboModule)->rollbackToPreviousBundle(
|
|
53
65
|
rt,
|
|
@@ -62,7 +74,9 @@ NativeOtaHotUpdateCxxSpecJSI::NativeOtaHotUpdateCxxSpecJSI(std::shared_ptr<CallI
|
|
|
62
74
|
methodMap_["deleteBundle"] = MethodMetadata {1, __hostFunction_NativeOtaHotUpdateCxxSpecJSI_deleteBundle};
|
|
63
75
|
methodMap_["restart"] = MethodMetadata {0, __hostFunction_NativeOtaHotUpdateCxxSpecJSI_restart};
|
|
64
76
|
methodMap_["getCurrentVersion"] = MethodMetadata {1, __hostFunction_NativeOtaHotUpdateCxxSpecJSI_getCurrentVersion};
|
|
77
|
+
methodMap_["getUpdateMetadata"] = MethodMetadata {1, __hostFunction_NativeOtaHotUpdateCxxSpecJSI_getUpdateMetadata};
|
|
65
78
|
methodMap_["setCurrentVersion"] = MethodMetadata {1, __hostFunction_NativeOtaHotUpdateCxxSpecJSI_setCurrentVersion};
|
|
79
|
+
methodMap_["setUpdateMetadata"] = MethodMetadata {1, __hostFunction_NativeOtaHotUpdateCxxSpecJSI_setUpdateMetadata};
|
|
66
80
|
methodMap_["rollbackToPreviousBundle"] = MethodMetadata {1, __hostFunction_NativeOtaHotUpdateCxxSpecJSI_rollbackToPreviousBundle};
|
|
67
81
|
}
|
|
68
82
|
|
package/android/generated/jni/react/renderer/components/RNOtaHotUpdateSpec/RNOtaHotUpdateSpecJSI.h
CHANGED
|
@@ -25,7 +25,9 @@ public:
|
|
|
25
25
|
virtual jsi::Value deleteBundle(jsi::Runtime &rt, double i) = 0;
|
|
26
26
|
virtual void restart(jsi::Runtime &rt) = 0;
|
|
27
27
|
virtual jsi::Value getCurrentVersion(jsi::Runtime &rt, double a) = 0;
|
|
28
|
+
virtual jsi::Value getUpdateMetadata(jsi::Runtime &rt, double a) = 0;
|
|
28
29
|
virtual jsi::Value setCurrentVersion(jsi::Runtime &rt, jsi::String version) = 0;
|
|
30
|
+
virtual jsi::Value setUpdateMetadata(jsi::Runtime &rt, jsi::String metadata) = 0;
|
|
29
31
|
virtual jsi::Value rollbackToPreviousBundle(jsi::Runtime &rt, double a) = 0;
|
|
30
32
|
|
|
31
33
|
};
|
|
@@ -93,6 +95,14 @@ private:
|
|
|
93
95
|
return bridging::callFromJs<jsi::Value>(
|
|
94
96
|
rt, &T::getCurrentVersion, jsInvoker_, instance_, std::move(a));
|
|
95
97
|
}
|
|
98
|
+
jsi::Value getUpdateMetadata(jsi::Runtime &rt, double a) override {
|
|
99
|
+
static_assert(
|
|
100
|
+
bridging::getParameterCount(&T::getUpdateMetadata) == 2,
|
|
101
|
+
"Expected getUpdateMetadata(...) to have 2 parameters");
|
|
102
|
+
|
|
103
|
+
return bridging::callFromJs<jsi::Value>(
|
|
104
|
+
rt, &T::getUpdateMetadata, jsInvoker_, instance_, std::move(a));
|
|
105
|
+
}
|
|
96
106
|
jsi::Value setCurrentVersion(jsi::Runtime &rt, jsi::String version) override {
|
|
97
107
|
static_assert(
|
|
98
108
|
bridging::getParameterCount(&T::setCurrentVersion) == 2,
|
|
@@ -101,6 +111,14 @@ private:
|
|
|
101
111
|
return bridging::callFromJs<jsi::Value>(
|
|
102
112
|
rt, &T::setCurrentVersion, jsInvoker_, instance_, std::move(version));
|
|
103
113
|
}
|
|
114
|
+
jsi::Value setUpdateMetadata(jsi::Runtime &rt, jsi::String metadata) override {
|
|
115
|
+
static_assert(
|
|
116
|
+
bridging::getParameterCount(&T::setUpdateMetadata) == 2,
|
|
117
|
+
"Expected setUpdateMetadata(...) to have 2 parameters");
|
|
118
|
+
|
|
119
|
+
return bridging::callFromJs<jsi::Value>(
|
|
120
|
+
rt, &T::setUpdateMetadata, jsInvoker_, instance_, std::move(metadata));
|
|
121
|
+
}
|
|
104
122
|
jsi::Value rollbackToPreviousBundle(jsi::Runtime &rt, double a) override {
|
|
105
123
|
static_assert(
|
|
106
124
|
bridging::getParameterCount(&T::rollbackToPreviousBundle) == 2,
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
package com.otahotupdate
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.os.Handler
|
|
5
|
+
import android.os.Looper
|
|
6
|
+
import android.widget.Toast
|
|
7
|
+
import androidx.appcompat.app.AlertDialog
|
|
8
|
+
import com.jakewharton.processphoenix.ProcessPhoenix
|
|
9
|
+
import com.rnhotupdate.Common.PATH
|
|
10
|
+
import com.rnhotupdate.Common.PREVIOUS_PATH
|
|
11
|
+
import com.rnhotupdate.Common.VERSION
|
|
12
|
+
import com.rnhotupdate.SharedPrefs
|
|
13
|
+
import kotlinx.coroutines.Dispatchers
|
|
14
|
+
import kotlinx.coroutines.GlobalScope
|
|
15
|
+
import kotlinx.coroutines.delay
|
|
16
|
+
import kotlinx.coroutines.launch
|
|
17
|
+
|
|
18
|
+
class CrashHandler(private val context: Context) : Thread.UncaughtExceptionHandler {
|
|
19
|
+
private val defaultHandler = Thread.getDefaultUncaughtExceptionHandler()
|
|
20
|
+
private val utils: Utils = Utils(context)
|
|
21
|
+
private var beginning = true
|
|
22
|
+
init {
|
|
23
|
+
GlobalScope.launch(Dispatchers.IO) {
|
|
24
|
+
delay(2000)
|
|
25
|
+
beginning = false
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
override fun uncaughtException(thread: Thread, throwable: Throwable) {
|
|
29
|
+
if (beginning) {
|
|
30
|
+
//begin remove and using previous bundle
|
|
31
|
+
val sharedPrefs = SharedPrefs(context)
|
|
32
|
+
val oldPath = sharedPrefs.getString(PREVIOUS_PATH)
|
|
33
|
+
if (oldPath != "") {
|
|
34
|
+
val isDeleted = utils.deleteOldBundleIfneeded(PATH)
|
|
35
|
+
if (isDeleted) {
|
|
36
|
+
sharedPrefs.putString(PATH, oldPath)
|
|
37
|
+
sharedPrefs.putString(PREVIOUS_PATH, "")
|
|
38
|
+
} else {
|
|
39
|
+
sharedPrefs.putString(PATH, "")
|
|
40
|
+
}
|
|
41
|
+
} else {
|
|
42
|
+
sharedPrefs.putString(PATH, "")
|
|
43
|
+
}
|
|
44
|
+
sharedPrefs.putString(VERSION, "0")
|
|
45
|
+
Toast.makeText(context, "Failed to load the update. Please try again.", Toast.LENGTH_LONG).show()
|
|
46
|
+
GlobalScope.launch(Dispatchers.IO) {
|
|
47
|
+
delay(1500)
|
|
48
|
+
ProcessPhoenix.triggerRebirth(context)
|
|
49
|
+
}
|
|
50
|
+
} else {
|
|
51
|
+
defaultHandler?.uncaughtException(thread, throwable)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
@@ -4,7 +4,7 @@ import android.content.Context
|
|
|
4
4
|
import android.content.pm.PackageInfo
|
|
5
5
|
import android.content.pm.PackageManager
|
|
6
6
|
import android.os.Build
|
|
7
|
-
import com.facebook.react.
|
|
7
|
+
import com.facebook.react.BaseReactPackage
|
|
8
8
|
import com.facebook.react.bridge.NativeModule
|
|
9
9
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
10
10
|
import com.facebook.react.module.model.ReactModuleInfo
|
|
@@ -16,10 +16,7 @@ import com.rnhotupdate.Common.VERSION
|
|
|
16
16
|
import com.rnhotupdate.SharedPrefs
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
class OtaHotUpdate
|
|
20
|
-
init {
|
|
21
|
-
mContext = context
|
|
22
|
-
}
|
|
19
|
+
class OtaHotUpdate : BaseReactPackage() {
|
|
23
20
|
override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
|
|
24
21
|
return if (name == OtaHotUpdateModule.NAME) {
|
|
25
22
|
OtaHotUpdateModule(reactContext)
|
|
@@ -37,7 +34,6 @@ class OtaHotUpdate(context: Context?) : TurboReactPackage() {
|
|
|
37
34
|
OtaHotUpdateModule.NAME,
|
|
38
35
|
false, // canOverrideExistingModule
|
|
39
36
|
false, // needsEagerInit
|
|
40
|
-
true, // hasConstants
|
|
41
37
|
false, // isCxxModule
|
|
42
38
|
isTurboModule // isTurboModule
|
|
43
39
|
)
|
|
@@ -53,24 +49,20 @@ class OtaHotUpdate(context: Context?) : TurboReactPackage() {
|
|
|
53
49
|
packageManager.getPackageInfo(packageName, 0)
|
|
54
50
|
}
|
|
55
51
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (pathBundle == "" || (currentVersionName != mContext?.getPackageInfo()?.versionName)) {
|
|
67
|
-
if (version != "") {
|
|
68
|
-
// reset version number because bundle is wrong version, need download from new version
|
|
69
|
-
sharedPrefs.putString(VERSION, "")
|
|
70
|
-
}
|
|
71
|
-
return DEFAULT_BUNDLE
|
|
52
|
+
fun bundleJS(context: Context): String {
|
|
53
|
+
Thread.setDefaultUncaughtExceptionHandler(CrashHandler(context))
|
|
54
|
+
val sharedPrefs = SharedPrefs(context)
|
|
55
|
+
val pathBundle = sharedPrefs.getString(PATH)
|
|
56
|
+
val version = sharedPrefs.getString(VERSION)
|
|
57
|
+
val currentVersionName = sharedPrefs.getString(CURRENT_VERSION_NAME)
|
|
58
|
+
if (pathBundle == "" || (currentVersionName != context.getPackageInfo().versionName)) {
|
|
59
|
+
if (version != "") {
|
|
60
|
+
// reset version number because bundle is wrong version, need download from new version
|
|
61
|
+
sharedPrefs.putString(VERSION, "")
|
|
72
62
|
}
|
|
73
|
-
return
|
|
63
|
+
return DEFAULT_BUNDLE
|
|
74
64
|
}
|
|
65
|
+
return pathBundle!!
|
|
66
|
+
}
|
|
75
67
|
}
|
|
76
68
|
}
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
package com.otahotupdate
|
|
2
2
|
|
|
3
3
|
import android.content.Context
|
|
4
|
-
import android.icu.text.SimpleDateFormat
|
|
5
|
-
import android.util.Log
|
|
6
4
|
import com.facebook.react.bridge.Promise
|
|
7
5
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
8
6
|
import com.facebook.react.bridge.ReactMethod
|
|
@@ -12,107 +10,25 @@ import com.rnhotupdate.Common.CURRENT_VERSION_NAME
|
|
|
12
10
|
import com.rnhotupdate.Common.PATH
|
|
13
11
|
import com.rnhotupdate.Common.PREVIOUS_PATH
|
|
14
12
|
import com.rnhotupdate.Common.VERSION
|
|
13
|
+
import com.rnhotupdate.Common.METADATA
|
|
15
14
|
import com.rnhotupdate.SharedPrefs
|
|
16
15
|
import java.io.File
|
|
17
|
-
import java.util.Date
|
|
18
|
-
import java.util.Locale
|
|
19
|
-
import java.util.zip.ZipFile
|
|
20
16
|
|
|
21
17
|
class OtaHotUpdateModule internal constructor(context: ReactApplicationContext) :
|
|
22
18
|
OtaHotUpdateSpec(context) {
|
|
23
|
-
|
|
19
|
+
private val utils: Utils = Utils(context)
|
|
24
20
|
override fun getName(): String {
|
|
25
21
|
return NAME
|
|
26
22
|
}
|
|
27
23
|
|
|
28
|
-
private fun deleteDirectory(directory: File): Boolean {
|
|
29
|
-
if (directory.isDirectory) {
|
|
30
|
-
// List all files and directories in the current directory
|
|
31
|
-
val files = directory.listFiles()
|
|
32
|
-
if (files != null) {
|
|
33
|
-
// Recursively delete all files and directories
|
|
34
|
-
for (file in files) {
|
|
35
|
-
if (!deleteDirectory(file)) {
|
|
36
|
-
return false
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
// Finally, delete the empty directory or file
|
|
42
|
-
return directory.delete()
|
|
43
|
-
}
|
|
44
|
-
private fun deleteOldBundleIfneeded(pathKey: String?): Boolean {
|
|
45
|
-
val pathName = if (pathKey != null) pathKey else PREVIOUS_PATH
|
|
46
|
-
val sharedPrefs = SharedPrefs(reactApplicationContext)
|
|
47
|
-
val path = sharedPrefs.getString(pathName)
|
|
48
|
-
val file = File(path)
|
|
49
|
-
if (file.exists() && file.isFile) {
|
|
50
|
-
val isDeleted = deleteDirectory(file.parentFile)
|
|
51
|
-
sharedPrefs.putString(pathName, "")
|
|
52
|
-
return isDeleted
|
|
53
|
-
} else {
|
|
54
|
-
return false
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
private fun extractZipFile(
|
|
58
|
-
zipFile: File,extension: String
|
|
59
|
-
): String? {
|
|
60
|
-
return try {
|
|
61
|
-
val outputDir = zipFile.parentFile
|
|
62
|
-
val timestamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
|
|
63
|
-
var topLevelFolder: String? = null
|
|
64
|
-
var bundlePath: String? = null
|
|
65
|
-
ZipFile(zipFile).use { zip ->
|
|
66
|
-
zip.entries().asSequence().forEach { entry ->
|
|
67
|
-
zip.getInputStream(entry).use { input ->
|
|
68
|
-
if (topLevelFolder == null) {
|
|
69
|
-
// Get root folder of zip file after unzip
|
|
70
|
-
val parts = entry.name.split("/")
|
|
71
|
-
if (parts.size > 1) {
|
|
72
|
-
topLevelFolder = parts.first()
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
val outputFile = File(outputDir, entry.name)
|
|
76
|
-
if (entry.isDirectory) {
|
|
77
|
-
if (!outputFile.exists()) outputFile.mkdirs()
|
|
78
|
-
} else {
|
|
79
|
-
if (outputFile.parentFile?.exists() != true) outputFile.parentFile?.mkdirs()
|
|
80
|
-
outputFile.outputStream().use { output ->
|
|
81
|
-
input.copyTo(output)
|
|
82
|
-
}
|
|
83
|
-
if (outputFile.absolutePath.endsWith(extension)) {
|
|
84
|
-
bundlePath = outputFile.absolutePath
|
|
85
|
-
return@use // Exit early if found
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
// Rename the detected top-level folder
|
|
92
|
-
if (topLevelFolder != null) {
|
|
93
|
-
val extractedFolder = File(outputDir, topLevelFolder)
|
|
94
|
-
val renamedFolder = File(outputDir, "output_$timestamp")
|
|
95
|
-
if (extractedFolder.exists()) {
|
|
96
|
-
extractedFolder.renameTo(renamedFolder)
|
|
97
|
-
// Update bundlePath if the file was inside the renamed folder
|
|
98
|
-
bundlePath = bundlePath?.replace(extractedFolder.absolutePath, renamedFolder.absolutePath)
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
bundlePath
|
|
102
|
-
} catch (e: Exception) {
|
|
103
|
-
e.printStackTrace()
|
|
104
|
-
null
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
24
|
|
|
109
25
|
@ReactMethod
|
|
110
26
|
override fun setupBundlePath(path: String?, extension: String?, promise: Promise) {
|
|
111
27
|
if (path != null) {
|
|
112
28
|
val file = File(path)
|
|
113
29
|
if (file.exists() && file.isFile) {
|
|
114
|
-
deleteOldBundleIfneeded(null)
|
|
115
|
-
val fileUnzip = extractZipFile(file, extension ?: ".bundle")
|
|
30
|
+
utils.deleteOldBundleIfneeded(null)
|
|
31
|
+
val fileUnzip = utils.extractZipFile(file, extension ?: ".bundle")
|
|
116
32
|
if (fileUnzip != null) {
|
|
117
33
|
file.delete()
|
|
118
34
|
val sharedPrefs = SharedPrefs(reactApplicationContext)
|
|
@@ -121,11 +37,14 @@ class OtaHotUpdateModule internal constructor(context: ReactApplicationContext)
|
|
|
121
37
|
sharedPrefs.putString(PREVIOUS_PATH, oldPath)
|
|
122
38
|
}
|
|
123
39
|
sharedPrefs.putString(PATH, fileUnzip)
|
|
124
|
-
sharedPrefs.putString(
|
|
40
|
+
sharedPrefs.putString(
|
|
41
|
+
CURRENT_VERSION_NAME,
|
|
42
|
+
reactApplicationContext?.getPackageInfo()?.versionName
|
|
43
|
+
)
|
|
125
44
|
promise.resolve(true)
|
|
126
45
|
} else {
|
|
127
46
|
file.delete()
|
|
128
|
-
deleteDirectory(file.parentFile)
|
|
47
|
+
utils.deleteDirectory(file.parentFile)
|
|
129
48
|
val sharedPrefs = SharedPrefs(reactApplicationContext)
|
|
130
49
|
sharedPrefs.putString(PATH, "")
|
|
131
50
|
promise.resolve(false)
|
|
@@ -140,8 +59,8 @@ class OtaHotUpdateModule internal constructor(context: ReactApplicationContext)
|
|
|
140
59
|
|
|
141
60
|
@ReactMethod
|
|
142
61
|
override fun deleteBundle(i: Double, promise: Promise) {
|
|
143
|
-
val isDeleted = deleteOldBundleIfneeded(PATH)
|
|
144
|
-
val isDeletedOldPath = deleteOldBundleIfneeded(PREVIOUS_PATH)
|
|
62
|
+
val isDeleted = utils.deleteOldBundleIfneeded(PATH)
|
|
63
|
+
val isDeletedOldPath = utils.deleteOldBundleIfneeded(PREVIOUS_PATH)
|
|
145
64
|
val sharedPrefs = SharedPrefs(reactApplicationContext)
|
|
146
65
|
sharedPrefs.putString(VERSION, "0")
|
|
147
66
|
promise.resolve(isDeleted && isDeletedOldPath)
|
|
@@ -150,7 +69,7 @@ class OtaHotUpdateModule internal constructor(context: ReactApplicationContext)
|
|
|
150
69
|
@ReactMethod
|
|
151
70
|
override fun restart() {
|
|
152
71
|
val context: Context? = currentActivity
|
|
153
|
-
ProcessPhoenix.triggerRebirth(context)
|
|
72
|
+
ProcessPhoenix.triggerRebirth(context)
|
|
154
73
|
}
|
|
155
74
|
|
|
156
75
|
@ReactMethod
|
|
@@ -171,11 +90,33 @@ class OtaHotUpdateModule internal constructor(context: ReactApplicationContext)
|
|
|
171
90
|
promise.resolve(true)
|
|
172
91
|
}
|
|
173
92
|
|
|
93
|
+
@ReactMethod
|
|
94
|
+
override fun getUpdateMetadata(a: Double, promise: Promise) {
|
|
95
|
+
val sharedPrefs = SharedPrefs(reactApplicationContext)
|
|
96
|
+
val metadata = sharedPrefs.getString(METADATA)
|
|
97
|
+
if (metadata != "") {
|
|
98
|
+
promise.resolve(metadata);
|
|
99
|
+
} else {
|
|
100
|
+
promise.resolve(null);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
@ReactMethod
|
|
105
|
+
override fun setUpdateMetadata(metadata: String?, promise: Promise) {
|
|
106
|
+
val sharedPrefs = SharedPrefs(reactApplicationContext)
|
|
107
|
+
sharedPrefs.putString(METADATA, metadata)
|
|
108
|
+
promise.resolve(true)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
|
|
174
112
|
@ReactMethod
|
|
175
113
|
override fun setExactBundlePath(path: String?, promise: Promise) {
|
|
176
114
|
val sharedPrefs = SharedPrefs(reactApplicationContext)
|
|
177
115
|
sharedPrefs.putString(PATH, path)
|
|
178
|
-
sharedPrefs.putString(
|
|
116
|
+
sharedPrefs.putString(
|
|
117
|
+
CURRENT_VERSION_NAME,
|
|
118
|
+
reactApplicationContext?.getPackageInfo()?.versionName
|
|
119
|
+
)
|
|
179
120
|
promise.resolve(true)
|
|
180
121
|
}
|
|
181
122
|
|
|
@@ -184,7 +125,7 @@ class OtaHotUpdateModule internal constructor(context: ReactApplicationContext)
|
|
|
184
125
|
val sharedPrefs = SharedPrefs(reactApplicationContext)
|
|
185
126
|
val oldPath = sharedPrefs.getString(PREVIOUS_PATH)
|
|
186
127
|
if (oldPath != "") {
|
|
187
|
-
val isDeleted = deleteOldBundleIfneeded(PATH)
|
|
128
|
+
val isDeleted = utils.deleteOldBundleIfneeded(PATH)
|
|
188
129
|
if (isDeleted) {
|
|
189
130
|
sharedPrefs.putString(PATH, oldPath)
|
|
190
131
|
sharedPrefs.putString(PREVIOUS_PATH, "")
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
package com.otahotupdate
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.icu.text.SimpleDateFormat
|
|
5
|
+
import com.rnhotupdate.Common.PREVIOUS_PATH
|
|
6
|
+
import com.rnhotupdate.SharedPrefs
|
|
7
|
+
import java.io.File
|
|
8
|
+
import java.util.Date
|
|
9
|
+
import java.util.Locale
|
|
10
|
+
import java.util.zip.ZipFile
|
|
11
|
+
|
|
12
|
+
class Utils internal constructor(private val context: Context) {
|
|
13
|
+
|
|
14
|
+
fun deleteDirectory(directory: File): Boolean {
|
|
15
|
+
if (directory.isDirectory) {
|
|
16
|
+
// List all files and directories in the current directory
|
|
17
|
+
val files = directory.listFiles()
|
|
18
|
+
if (files != null) {
|
|
19
|
+
// Recursively delete all files and directories
|
|
20
|
+
for (file in files) {
|
|
21
|
+
if (!deleteDirectory(file)) {
|
|
22
|
+
return false
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// Finally, delete the empty directory or file
|
|
28
|
+
return directory.delete()
|
|
29
|
+
}
|
|
30
|
+
fun deleteOldBundleIfneeded(pathKey: String?): Boolean {
|
|
31
|
+
val pathName = if (pathKey != null) pathKey else PREVIOUS_PATH
|
|
32
|
+
val sharedPrefs = SharedPrefs(context)
|
|
33
|
+
val path = sharedPrefs.getString(pathName)
|
|
34
|
+
val file = File(path)
|
|
35
|
+
if (file.exists() && file.isFile) {
|
|
36
|
+
val isDeleted = deleteDirectory(file.parentFile)
|
|
37
|
+
sharedPrefs.putString(pathName, "")
|
|
38
|
+
return isDeleted
|
|
39
|
+
} else {
|
|
40
|
+
return false
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
fun extractZipFile(
|
|
45
|
+
zipFile: File,extension: String
|
|
46
|
+
): String? {
|
|
47
|
+
return try {
|
|
48
|
+
val outputDir = zipFile.parentFile
|
|
49
|
+
val timestamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
|
|
50
|
+
var topLevelFolder: String? = null
|
|
51
|
+
var bundlePath: String? = null
|
|
52
|
+
ZipFile(zipFile).use { zip ->
|
|
53
|
+
zip.entries().asSequence().forEach { entry ->
|
|
54
|
+
zip.getInputStream(entry).use { input ->
|
|
55
|
+
if (topLevelFolder == null) {
|
|
56
|
+
// Get root folder of zip file after unzip
|
|
57
|
+
val parts = entry.name.split("/")
|
|
58
|
+
if (parts.size > 1) {
|
|
59
|
+
topLevelFolder = parts.first()
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
val outputFile = File(outputDir, entry.name)
|
|
63
|
+
if (entry.isDirectory) {
|
|
64
|
+
if (!outputFile.exists()) outputFile.mkdirs()
|
|
65
|
+
} else {
|
|
66
|
+
if (outputFile.parentFile?.exists() != true) outputFile.parentFile?.mkdirs()
|
|
67
|
+
outputFile.outputStream().use { output ->
|
|
68
|
+
input.copyTo(output)
|
|
69
|
+
}
|
|
70
|
+
if (outputFile.absolutePath.endsWith(extension)) {
|
|
71
|
+
bundlePath = outputFile.absolutePath
|
|
72
|
+
return@use // Exit early if found
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// Rename the detected top-level folder
|
|
79
|
+
if (topLevelFolder != null) {
|
|
80
|
+
val extractedFolder = File(outputDir, topLevelFolder)
|
|
81
|
+
val renamedFolder = File(outputDir, "output_$timestamp")
|
|
82
|
+
if (extractedFolder.exists()) {
|
|
83
|
+
extractedFolder.renameTo(renamedFolder)
|
|
84
|
+
// Update bundlePath if the file was inside the renamed folder
|
|
85
|
+
bundlePath = bundlePath?.replace(extractedFolder.absolutePath, renamedFolder.absolutePath)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
bundlePath
|
|
89
|
+
} catch (e: Exception) {
|
|
90
|
+
e.printStackTrace()
|
|
91
|
+
null
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -14,4 +14,6 @@ abstract class OtaHotUpdateSpec internal constructor(context: ReactApplicationCo
|
|
|
14
14
|
abstract fun setCurrentVersion(version: String?, promise: Promise)
|
|
15
15
|
abstract fun setExactBundlePath(path: String?, promise: Promise)
|
|
16
16
|
abstract fun rollbackToPreviousBundle(a: Double, promise: Promise)
|
|
17
|
+
abstract fun getUpdateMetadata(a: Double, promise: Promise)
|
|
18
|
+
abstract fun setUpdateMetadata(metadata: String?, promise: Promise)
|
|
17
19
|
}
|