├── harmony └── pushy │ ├── README.md │ ├── index.ets │ ├── hvigorfile.ts │ ├── ts.ts │ ├── build-profile.json5 │ ├── src │ └── main │ │ ├── module.json5 │ │ ├── resources │ │ ├── base │ │ │ └── element │ │ │ │ └── string.json │ │ ├── en_US │ │ │ └── element │ │ │ │ └── string.json │ │ └── zh_CN │ │ │ └── element │ │ │ └── string.json │ │ ├── cpp │ │ ├── pushy.h │ │ ├── CMakeLists.txt │ │ ├── PushyTurboModule.h │ │ └── PushyPackage.h │ │ └── ets │ │ ├── PushyPackage.ts │ │ ├── DownloadTaskParams.ts │ │ ├── Logger.ts │ │ ├── EventHub.ts │ │ └── PushyFileJSBundleProvider.ets │ ├── oh-package.json5 │ ├── BuildProfile.ets │ ├── oh-package-lock.json5 │ ├── hvigor-plugin.ts │ └── OAT.xml ├── Example ├── testHotUpdate │ ├── .watchmanconfig │ ├── app.json │ ├── .eslintrc.js │ ├── .prettierrc.js │ ├── android │ │ ├── app │ │ │ ├── src │ │ │ │ ├── main │ │ │ │ │ ├── res │ │ │ │ │ │ ├── values │ │ │ │ │ │ │ ├── strings.xml │ │ │ │ │ │ │ └── styles.xml │ │ │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ │ ├── xml │ │ │ │ │ │ │ └── network_security_config.xml │ │ │ │ │ │ └── drawable │ │ │ │ │ │ │ └── rn_edit_text_material.xml │ │ │ │ │ ├── java │ │ │ │ │ │ └── com │ │ │ │ │ │ │ └── awesomeproject │ │ │ │ │ │ │ ├── MainActivity.kt │ │ │ │ │ │ │ └── MainApplication.kt │ │ │ │ │ └── AndroidManifest.xml │ │ │ │ ├── debug │ │ │ │ │ └── AndroidManifest.xml │ │ │ │ └── androidTest │ │ │ │ │ └── java │ │ │ │ │ └── com │ │ │ │ │ └── awesomeproject │ │ │ │ │ └── DetoxTest.java │ │ │ ├── debug.keystore │ │ │ ├── proguard-rules.pro │ │ │ ├── build_defs.bzl │ │ │ └── _BUCK │ │ ├── gradle │ │ │ └── wrapper │ │ │ │ ├── gradle-wrapper.jar │ │ │ │ └── gradle-wrapper.properties │ │ ├── settings.gradle │ │ ├── build.gradle │ │ └── gradle.properties │ ├── ios │ │ ├── AwesomeProject │ │ │ ├── Images.xcassets │ │ │ │ ├── Contents.json │ │ │ │ └── AppIcon.appiconset │ │ │ │ │ └── Contents.json │ │ │ ├── AppDelegate.h │ │ │ ├── main.m │ │ │ ├── AppDelegate.mm │ │ │ ├── PrivacyInfo.xcprivacy │ │ │ └── Info.plist │ │ ├── AwesomeProject.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ └── IDEWorkspaceChecks.plist │ │ ├── .xcode.env │ │ └── Podfile │ ├── src │ │ └── assets │ │ │ ├── shezhi.png │ │ │ ├── shezhi@2x.png │ │ │ ├── shezhi@3x.png │ │ │ ├── shoucang.png │ │ │ ├── shoucang@2x.png │ │ │ ├── shoucang@3x.png │ │ │ └── react-logo.svg │ ├── tsconfig.json │ ├── babel.config.js │ ├── update.json │ ├── index.js │ ├── scripts │ │ └── start-rn.sh │ ├── __tests__ │ │ └── App-test.js │ ├── metro.config.js │ ├── e2e │ │ └── jest.config.js │ ├── .gitignore │ ├── README.md │ ├── package.json │ └── .detoxrc.js ├── harmony_use_pushy │ ├── .watchmanconfig │ ├── harmony │ │ ├── entry │ │ │ ├── src │ │ │ │ ├── main │ │ │ │ │ ├── cpp │ │ │ │ │ │ ├── .gitignore │ │ │ │ │ │ ├── PackageProvider.cpp │ │ │ │ │ │ └── CMakeLists.txt │ │ │ │ │ ├── resources │ │ │ │ │ │ ├── base │ │ │ │ │ │ │ ├── profile │ │ │ │ │ │ │ │ ├── backup_config.json │ │ │ │ │ │ │ │ └── main_pages.json │ │ │ │ │ │ │ ├── element │ │ │ │ │ │ │ │ ├── color.json │ │ │ │ │ │ │ │ └── string.json │ │ │ │ │ │ │ └── media │ │ │ │ │ │ │ │ ├── layered_image.json │ │ │ │ │ │ │ │ ├── background.png │ │ │ │ │ │ │ │ ├── foreground.png │ │ │ │ │ │ │ │ └── startIcon.png │ │ │ │ │ │ ├── rawfile │ │ │ │ │ │ │ ├── assets │ │ │ │ │ │ │ │ └── assets │ │ │ │ │ │ │ │ │ └── shezhi.png │ │ │ │ │ │ │ └── update.json │ │ │ │ │ │ ├── zh_CN │ │ │ │ │ │ │ └── element │ │ │ │ │ │ │ │ └── string.json │ │ │ │ │ │ └── en_US │ │ │ │ │ │ │ └── element │ │ │ │ │ │ │ └── string.json │ │ │ │ │ ├── ets │ │ │ │ │ │ ├── RNPackagesFactory.ts │ │ │ │ │ │ ├── entryability │ │ │ │ │ │ │ └── EntryAbility.ets │ │ │ │ │ │ ├── entrybackupability │ │ │ │ │ │ │ └── EntryBackupAbility.ets │ │ │ │ │ │ └── pages │ │ │ │ │ │ │ └── Index.ets │ │ │ │ │ └── module.json5 │ │ │ │ ├── mock │ │ │ │ │ └── mock-config.json5 │ │ │ │ ├── test │ │ │ │ │ ├── List.test.ets │ │ │ │ │ └── LocalUnit.test.ets │ │ │ │ └── ohosTest │ │ │ │ │ ├── ets │ │ │ │ │ └── test │ │ │ │ │ │ ├── List.test.ets │ │ │ │ │ │ └── Ability.test.ets │ │ │ │ │ └── module.json5 │ │ │ ├── .gitignore │ │ │ ├── oh-package.json5 │ │ │ ├── hvigorfile.ts │ │ │ ├── build-profile.json5 │ │ │ ├── obfuscation-rules.txt │ │ │ └── oh-package-lock.json5 │ │ ├── react_native_openharmony │ │ │ └── generated │ │ │ │ ├── components │ │ │ │ └── ts.ts │ │ │ │ ├── turboModules │ │ │ │ └── ts.ts │ │ │ │ ├── index.ets │ │ │ │ └── ts.ts │ │ ├── AppScope │ │ │ ├── resources │ │ │ │ └── base │ │ │ │ │ ├── element │ │ │ │ │ └── string.json │ │ │ │ │ └── media │ │ │ │ │ └── app_icon.png │ │ │ └── app.json5 │ │ ├── .gitignore │ │ ├── hvigorfile.ts │ │ ├── code-linter.json5 │ │ ├── oh-package.json5 │ │ ├── oh-package-lock.json5 │ │ ├── build-profile.json5 │ │ └── hvigor │ │ │ └── hvigor-config.json5 │ ├── jest.config.js │ ├── .bundle │ │ └── config │ ├── tsconfig.json │ ├── .eslintrc.js │ ├── app.json │ ├── babel.config.js │ ├── demo.png │ ├── sync.png │ ├── debug.png │ ├── assets │ │ ├── shezhi.png │ │ ├── shezhi@2x.png │ │ ├── shezhi@3x.png │ │ ├── shoucang.png │ │ ├── shoucang@2x.png │ │ └── shoucang@3x.png │ ├── android │ │ ├── app │ │ │ ├── src │ │ │ │ ├── main │ │ │ │ │ ├── res │ │ │ │ │ │ ├── values │ │ │ │ │ │ │ ├── strings.xml │ │ │ │ │ │ │ └── styles.xml │ │ │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ │ └── drawable │ │ │ │ │ │ │ └── rn_edit_text_material.xml │ │ │ │ │ ├── AndroidManifest.xml │ │ │ │ │ └── java │ │ │ │ │ │ └── com │ │ │ │ │ │ └── harmony_use_pushy │ │ │ │ │ │ ├── MainActivity.java │ │ │ │ │ │ └── MainApplication.java │ │ │ │ ├── debug │ │ │ │ │ └── AndroidManifest.xml │ │ │ │ └── release │ │ │ │ │ └── java │ │ │ │ │ └── com │ │ │ │ │ └── harmony_use_pushy │ │ │ │ │ └── ReactNativeFlipper.java │ │ │ ├── debug.keystore │ │ │ └── proguard-rules.pro │ │ ├── gradle │ │ │ └── wrapper │ │ │ │ ├── gradle-wrapper.jar │ │ │ │ └── gradle-wrapper.properties │ │ ├── settings.gradle │ │ ├── build.gradle │ │ ├── gradle.properties │ │ └── gradlew.bat │ ├── ios │ │ ├── harmony_use_pushy │ │ │ ├── Images.xcassets │ │ │ │ ├── Contents.json │ │ │ │ └── AppIcon.appiconset │ │ │ │ │ └── Contents.json │ │ │ ├── AppDelegate.h │ │ │ ├── main.m │ │ │ ├── AppDelegate.mm │ │ │ └── Info.plist │ │ ├── .xcode.env │ │ ├── harmony_use_pushyTests │ │ │ ├── Info.plist │ │ │ └── harmony_use_pushyTests.m │ │ └── Podfile │ ├── .prettierrc.js │ ├── index.js │ ├── update.json │ ├── __tests__ │ │ └── App.test.tsx │ ├── README.md │ ├── metro.config.js │ └── package.json └── expoUsePushy │ ├── index.js │ ├── assets │ ├── icon.png │ ├── favicon.png │ ├── shezhi.png │ ├── shezhi@2x.png │ ├── shezhi@3x.png │ ├── splash-icon.png │ └── adaptive-icon.png │ ├── tsconfig.json │ ├── update.json │ ├── .gitignore │ ├── app.json │ └── package.json ├── ios ├── ImportReact.h ├── pushy_build_time.txt ├── Expo │ ├── ExpoPushyModule.swift │ └── ExpoPushyReactDelegateHandler.swift └── RCTPushy │ ├── RCTPushy.h │ ├── RCTPushyDownloader.h │ ├── HDiffPatch │ ├── HDiffPatch.h │ └── HDiffPatch.mm │ ├── RCTPushyManager.h │ └── RCTPushyDownloader.mm ├── react-native.config.js ├── endpoints.json ├── src ├── permissions.native.ts ├── permissions.ts ├── index.ts ├── NativePushy.ts ├── context.ts ├── isInRollout.ts ├── locales │ └── zh.ts ├── type.ts └── core.ts ├── domains.json ├── endpoints_cresc.json ├── .eslintrc.js ├── babel.config.js ├── android ├── lib │ ├── x86 │ │ └── librnupdate.so │ ├── x86_64 │ │ └── librnupdate.so │ ├── arm64-v8a │ │ └── librnupdate.so │ └── armeabi-v7a │ │ └── librnupdate.so ├── src │ └── main │ │ ├── res │ │ └── xml │ │ │ └── pushy_file_paths.xml │ │ ├── java │ │ ├── expo │ │ │ └── modules │ │ │ │ └── pushy │ │ │ │ ├── ExpoPushyModule.kt │ │ │ │ └── ExpoPushyPackage.java │ │ └── cn │ │ │ └── reactnative │ │ │ └── modules │ │ │ └── update │ │ │ ├── ReactNativeHostHandler.java │ │ │ ├── PushyFileProvider.java │ │ │ ├── DownloadTaskParams.java │ │ │ ├── UpdatePackage.java │ │ │ └── SafeZipFile.java │ │ ├── AndroidManifestNew.xml │ │ └── AndroidManifest.xml ├── jni │ ├── Application.mk │ ├── Android.mk │ ├── cn_reactnative_modules_update_DownloadTask.h │ ├── hpatch.h │ └── DownloadTask.c └── proguard.pro ├── .prettierrc.js ├── tea.yaml ├── tsconfig.json ├── .github └── workflows │ ├── scripts │ ├── functions │ │ ├── .gitignore │ │ ├── src │ │ │ ├── exports.ts │ │ │ ├── testFunctionCustomRegion.ts │ │ │ ├── index.ts │ │ │ ├── sample-data.ts │ │ │ └── testFunctionDefaultRegion.ts │ │ ├── tsconfig.json │ │ └── package.json │ ├── adb_all_emulators.sh │ ├── database.rules │ ├── start-firebase-emulator.bat │ ├── firestore.rules │ ├── storage.rules │ ├── firebase.json │ ├── start-firebase-emulator.sh │ └── firestore.indexes.json │ ├── publish.yml │ ├── lint.yml │ └── e2e_android.yml ├── .gitmodules ├── expo-module.config.json ├── e2e ├── jest.config.js └── starter.test.js ├── .npmignore ├── .gitignore ├── LICENSE ├── README.md └── scripts └── check-expo-version.js /harmony/pushy/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Example/testHotUpdate/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /ios/ImportReact.h: -------------------------------------------------------------------------------- 1 | @import React; 2 | 3 | -------------------------------------------------------------------------------- /ios/pushy_build_time.txt: -------------------------------------------------------------------------------- 1 | 1680488830 2 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /harmony/pushy/index.ets: -------------------------------------------------------------------------------- 1 | 2 | export * from './ts' -------------------------------------------------------------------------------- /react-native.config.js: -------------------------------------------------------------------------------- 1 | module.exports = {}; 2 | -------------------------------------------------------------------------------- /endpoints.json: -------------------------------------------------------------------------------- 1 | ["https://p.reactnative.cn/api"] 2 | -------------------------------------------------------------------------------- /Example/expoUsePushy/index.js: -------------------------------------------------------------------------------- 1 | import 'expo-router/entry'; 2 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/entry/src/main/cpp/.gitignore: -------------------------------------------------------------------------------- 1 | jsbundle.h -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/entry/src/mock/mock-config.json5: -------------------------------------------------------------------------------- 1 | { 2 | } -------------------------------------------------------------------------------- /src/permissions.native.ts: -------------------------------------------------------------------------------- 1 | export { PermissionsAndroid } from 'react-native'; 2 | -------------------------------------------------------------------------------- /domains.json: -------------------------------------------------------------------------------- 1 | ["update.react-native.cn", "update.reactnative.cn", "p.reactnative.cn"] 2 | -------------------------------------------------------------------------------- /endpoints_cresc.json: -------------------------------------------------------------------------------- 1 | ["https://cresc-server-pthxtmvcnf.ap-southeast-1.fcapp.run"] 2 | -------------------------------------------------------------------------------- /harmony/pushy/hvigorfile.ts: -------------------------------------------------------------------------------- 1 | export { harTasks } from '@ohos/hvigor-ohos-plugin'; 2 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: '@react-native', 4 | }; 5 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'react-native', 3 | }; 4 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:@react-native/babel-preset'], 3 | }; 4 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/.bundle/config: -------------------------------------------------------------------------------- 1 | BUNDLE_PATH: "vendor/bundle" 2 | BUNDLE_FORCE_RUBY_PLATFORM: 1 3 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/react-native/tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /Example/testHotUpdate/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "AwesomeProject", 3 | "displayName": "AwesomeProject" 4 | } -------------------------------------------------------------------------------- /Example/harmony_use_pushy/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: '@react-native', 4 | }; 5 | -------------------------------------------------------------------------------- /Example/testHotUpdate/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: '@react-native', 4 | }; 5 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "harmony_use_pushy", 3 | "displayName": "harmony_use_pushy" 4 | } 5 | -------------------------------------------------------------------------------- /harmony/pushy/ts.ts: -------------------------------------------------------------------------------- 1 | 2 | export * from './src/main/ets/PushyPackage' 3 | export * from './src/main/ets/PushyTurboModule' -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/entry/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /oh_modules 3 | /.preview 4 | /build 5 | /.cxx 6 | /.test -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/react_native_openharmony/generated/components/ts.ts: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | */ 4 | 5 | export {} 6 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/react_native_openharmony/generated/turboModules/ts.ts: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | */ 4 | 5 | export {} 6 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:metro-react-native-babel-preset'], 3 | }; 4 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/entry/src/main/resources/base/profile/backup_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "allowToBackupRestore": true 3 | } -------------------------------------------------------------------------------- /android/lib/x86/librnupdate.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/android/lib/x86/librnupdate.so -------------------------------------------------------------------------------- /Example/harmony_use_pushy/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/harmony_use_pushy/demo.png -------------------------------------------------------------------------------- /Example/harmony_use_pushy/sync.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/harmony_use_pushy/sync.png -------------------------------------------------------------------------------- /android/lib/x86_64/librnupdate.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/android/lib/x86_64/librnupdate.so -------------------------------------------------------------------------------- /Example/expoUsePushy/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/expoUsePushy/assets/icon.png -------------------------------------------------------------------------------- /Example/expoUsePushy/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "expo/tsconfig.base", 3 | "compilerOptions": { 4 | "strict": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/debug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/harmony_use_pushy/debug.png -------------------------------------------------------------------------------- /android/lib/arm64-v8a/librnupdate.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/android/lib/arm64-v8a/librnupdate.so -------------------------------------------------------------------------------- /Example/expoUsePushy/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/expoUsePushy/assets/favicon.png -------------------------------------------------------------------------------- /Example/expoUsePushy/assets/shezhi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/expoUsePushy/assets/shezhi.png -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/entry/src/main/resources/base/profile/main_pages.json: -------------------------------------------------------------------------------- 1 | { 2 | "src": [ 3 | "pages/Index" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /Example/testHotUpdate/.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | arrowParens: 'avoid', 3 | singleQuote: true, 4 | trailingComma: 'all', 5 | }; 6 | -------------------------------------------------------------------------------- /android/lib/armeabi-v7a/librnupdate.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/android/lib/armeabi-v7a/librnupdate.so -------------------------------------------------------------------------------- /harmony/pushy/build-profile.json5: -------------------------------------------------------------------------------- 1 | { 2 | "apiType": "stageMode", 3 | "targets": [ 4 | { 5 | "name": "default", 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /Example/expoUsePushy/assets/shezhi@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/expoUsePushy/assets/shezhi@2x.png -------------------------------------------------------------------------------- /Example/expoUsePushy/assets/shezhi@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/expoUsePushy/assets/shezhi@3x.png -------------------------------------------------------------------------------- /harmony/pushy/src/main/module.json5: -------------------------------------------------------------------------------- 1 | { 2 | "module": { 3 | "name": "pushy", 4 | "type": "har", 5 | "deviceTypes": ['default'], 6 | }, 7 | } -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | arrowParens: 'avoid', 3 | bracketSameLine: true, 4 | singleQuote: true, 5 | trailingComma: 'all', 6 | }; 7 | -------------------------------------------------------------------------------- /Example/expoUsePushy/assets/splash-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/expoUsePushy/assets/splash-icon.png -------------------------------------------------------------------------------- /Example/harmony_use_pushy/assets/shezhi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/harmony_use_pushy/assets/shezhi.png -------------------------------------------------------------------------------- /Example/testHotUpdate/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | AwesomeProject 3 | 4 | -------------------------------------------------------------------------------- /Example/testHotUpdate/ios/AwesomeProject/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Example/testHotUpdate/src/assets/shezhi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/testHotUpdate/src/assets/shezhi.png -------------------------------------------------------------------------------- /tea.yaml: -------------------------------------------------------------------------------- 1 | # https://tea.xyz/what-is-this-file 2 | --- 3 | version: 1.0.0 4 | codeOwners: 5 | - '0x10D90dC0034E2e82F0AC55954B3ed4EC0550ECe7' 6 | quorum: 1 7 | -------------------------------------------------------------------------------- /Example/expoUsePushy/assets/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/expoUsePushy/assets/adaptive-icon.png -------------------------------------------------------------------------------- /Example/harmony_use_pushy/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | harmony_use_pushy 3 | 4 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/assets/shezhi@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/harmony_use_pushy/assets/shezhi@2x.png -------------------------------------------------------------------------------- /Example/harmony_use_pushy/assets/shezhi@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/harmony_use_pushy/assets/shezhi@3x.png -------------------------------------------------------------------------------- /Example/harmony_use_pushy/assets/shoucang.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/harmony_use_pushy/assets/shoucang.png -------------------------------------------------------------------------------- /Example/testHotUpdate/src/assets/shezhi@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/testHotUpdate/src/assets/shezhi@2x.png -------------------------------------------------------------------------------- /Example/testHotUpdate/src/assets/shezhi@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/testHotUpdate/src/assets/shezhi@3x.png -------------------------------------------------------------------------------- /Example/testHotUpdate/src/assets/shoucang.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/testHotUpdate/src/assets/shoucang.png -------------------------------------------------------------------------------- /Example/harmony_use_pushy/assets/shoucang@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/harmony_use_pushy/assets/shoucang@2x.png -------------------------------------------------------------------------------- /Example/harmony_use_pushy/assets/shoucang@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/harmony_use_pushy/assets/shoucang@3x.png -------------------------------------------------------------------------------- /Example/harmony_use_pushy/ios/harmony_use_pushy/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Example/testHotUpdate/android/app/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/testHotUpdate/android/app/debug.keystore -------------------------------------------------------------------------------- /Example/testHotUpdate/src/assets/shoucang@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/testHotUpdate/src/assets/shoucang@2x.png -------------------------------------------------------------------------------- /Example/testHotUpdate/src/assets/shoucang@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/testHotUpdate/src/assets/shoucang@3x.png -------------------------------------------------------------------------------- /Example/harmony_use_pushy/android/app/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/harmony_use_pushy/android/app/debug.keystore -------------------------------------------------------------------------------- /Example/testHotUpdate/ios/AwesomeProject/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : RCTAppDelegate 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/entry/src/test/List.test.ets: -------------------------------------------------------------------------------- 1 | import localUnitTest from './LocalUnit.test'; 2 | 3 | export default function testsuite() { 4 | localUnitTest(); 5 | } -------------------------------------------------------------------------------- /Example/harmony_use_pushy/ios/harmony_use_pushy/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : RCTAppDelegate 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /harmony/pushy/src/main/resources/base/element/string.json: -------------------------------------------------------------------------------- 1 | { 2 | "string": [ 3 | { 4 | "name": "page_show", 5 | "value": "page from npm package" 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /harmony/pushy/src/main/resources/en_US/element/string.json: -------------------------------------------------------------------------------- 1 | { 2 | "string": [ 3 | { 4 | "name": "page_show", 5 | "value": "page from npm package" 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /harmony/pushy/src/main/resources/zh_CN/element/string.json: -------------------------------------------------------------------------------- 1 | { 2 | "string": [ 3 | { 4 | "name": "page_show", 5 | "value": "page from npm package" 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/entry/src/ohosTest/ets/test/List.test.ets: -------------------------------------------------------------------------------- 1 | import abilityTest from './Ability.test'; 2 | 3 | export default function testsuite() { 4 | abilityTest(); 5 | } -------------------------------------------------------------------------------- /Example/testHotUpdate/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@react-native/typescript-config", 3 | "include": ["**/*.ts", "**/*.tsx"], 4 | "exclude": ["**/node_modules", "**/Pods"] 5 | } 6 | -------------------------------------------------------------------------------- /android/src/main/res/xml/pushy_file_paths.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | -------------------------------------------------------------------------------- /Example/testHotUpdate/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/testHotUpdate/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /ios/Expo/ExpoPushyModule.swift: -------------------------------------------------------------------------------- 1 | import ExpoModulesCore 2 | 3 | public class ExpoPushyModule: Module { 4 | public func definition() -> ModuleDefinition { 5 | Name("ExpoPushy") 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@react-native/typescript-config", 3 | "include": ["**/*.ts", "**/*.tsx"], 4 | "exclude": ["**/node_modules", "**/Pods", "**/harmony", "**/Example"] 5 | } 6 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/harmony_use_pushy/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/AppScope/resources/base/element/string.json: -------------------------------------------------------------------------------- 1 | { 2 | "string": [ 3 | { 4 | "name": "app_name", 5 | "value": "harmony_use_pushy" 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/entry/src/main/resources/base/element/color.json: -------------------------------------------------------------------------------- 1 | { 2 | "color": [ 3 | { 4 | "name": "start_window_background", 5 | "value": "#FFFFFF" 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /Example/harmony_use_pushy/.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | arrowParens: 'avoid', 3 | bracketSameLine: true, 4 | bracketSpacing: false, 5 | singleQuote: true, 6 | trailingComma: 'all', 7 | }; 8 | -------------------------------------------------------------------------------- /src/permissions.ts: -------------------------------------------------------------------------------- 1 | import type { PermissionsAndroidStatic } from 'react-native'; 2 | import { emptyModule } from './utils'; 3 | 4 | export const PermissionsAndroid = emptyModule as PermissionsAndroidStatic; 5 | -------------------------------------------------------------------------------- /Example/testHotUpdate/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/testHotUpdate/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /Example/testHotUpdate/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/testHotUpdate/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/AppScope/resources/base/media/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/harmony_use_pushy/harmony/AppScope/resources/base/media/app_icon.png -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/entry/src/main/resources/base/media/layered_image.json: -------------------------------------------------------------------------------- 1 | { 2 | "layered-image": 3 | { 4 | "background" : "$media:background", 5 | "foreground" : "$media:foreground" 6 | } 7 | } -------------------------------------------------------------------------------- /Example/testHotUpdate/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/testHotUpdate/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Example/testHotUpdate/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/testHotUpdate/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Example/testHotUpdate/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/testHotUpdate/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /ios/RCTPushy/RCTPushy.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | 5 | @interface RCTPushy : RCTEventEmitter 6 | 7 | + (NSURL *)bundleURL; 8 | 9 | @end 10 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/harmony_use_pushy/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /Example/harmony_use_pushy/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/harmony_use_pushy/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /Example/harmony_use_pushy/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/harmony_use_pushy/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Example/testHotUpdate/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:@react-native/babel-preset'], 3 | env: { 4 | production: { 5 | plugins: ['react-native-paper/babel'], 6 | }, 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/harmony_use_pushy/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Example/harmony_use_pushy/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/harmony_use_pushy/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /oh_modules 3 | /local.properties 4 | /.idea 5 | **/build 6 | /.hvigor 7 | .cxx 8 | /.clangd 9 | /.clang-format 10 | /.clang-tidy 11 | **/.test 12 | /.appanalyzer -------------------------------------------------------------------------------- /Example/testHotUpdate/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/testHotUpdate/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Example/testHotUpdate/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/testHotUpdate/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Example/testHotUpdate/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/testHotUpdate/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Example/testHotUpdate/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/testHotUpdate/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Example/harmony_use_pushy/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/harmony_use_pushy/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Example/harmony_use_pushy/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/harmony_use_pushy/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/entry/src/main/resources/base/media/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/harmony_use_pushy/harmony/entry/src/main/resources/base/media/background.png -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/entry/src/main/resources/base/media/foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/harmony_use_pushy/harmony/entry/src/main/resources/base/media/foreground.png -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/entry/src/main/resources/base/media/startIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/harmony_use_pushy/harmony/entry/src/main/resources/base/media/startIcon.png -------------------------------------------------------------------------------- /Example/testHotUpdate/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/testHotUpdate/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /harmony/pushy/src/main/cpp/pushy.h: -------------------------------------------------------------------------------- 1 | #ifndef _DOWNLOAD_TASK_H_ 2 | #define _DOWNLOAD_TASK_H_ 3 | 4 | #include 5 | 6 | napi_value HdiffPatch(napi_env env, napi_callback_info info); 7 | 8 | #endif // _DOWNLOAD_TASK_H_ -------------------------------------------------------------------------------- /Example/harmony_use_pushy/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/harmony_use_pushy/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Example/harmony_use_pushy/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/harmony_use_pushy/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Example/harmony_use_pushy/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/harmony_use_pushy/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Example/testHotUpdate/android/app/src/main/res/xml/network_security_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Example/testHotUpdate/update.json: -------------------------------------------------------------------------------- 1 | { 2 | "ios": { 3 | "appId": 28943, 4 | "appKey": "d-OmPxIBivPrDfKhLHjxN-HS" 5 | }, 6 | "android": { 7 | "appId": 27509, 8 | "appKey": "aQz3Uc2pA7gt_prDaQ4rbWRY" 9 | } 10 | } -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { Pushy, Cresc } from './client'; 2 | export { UpdateContext, usePushy, useUpdate } from './context'; 3 | export { PushyProvider, UpdateProvider } from './provider'; 4 | export { PushyModule, UpdateModule } from './core'; 5 | -------------------------------------------------------------------------------- /.github/workflows/scripts/functions/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled JavaScript files 2 | lib/**/*.js 3 | lib/**/*.js.map 4 | 5 | # TypeScript v1 declaration files 6 | typings/ 7 | 8 | # Node.js dependency directory 9 | node_modules/ 10 | yarn.lock 11 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "android/jni/lzma"] 2 | path = android/jni/lzma 3 | url = https://github.com/sisong/lzma.git 4 | [submodule "android/jni/HDiffPatch"] 5 | path = android/jni/HDiffPatch 6 | url = https://github.com/sisong/HDiffPatch.git 7 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/entry/src/main/resources/rawfile/assets/assets/shezhi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactnativecn/react-native-update/HEAD/Example/harmony_use_pushy/harmony/entry/src/main/resources/rawfile/assets/assets/shezhi.png -------------------------------------------------------------------------------- /Example/harmony_use_pushy/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | */ 4 | 5 | import {AppRegistry} from 'react-native'; 6 | import App from './App'; 7 | import {name as appName} from './app.json'; 8 | 9 | AppRegistry.registerComponent(appName, () => App); 10 | -------------------------------------------------------------------------------- /Example/testHotUpdate/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | */ 4 | 5 | import {AppRegistry} from 'react-native'; 6 | import App from './src'; 7 | import {name as appName} from './app.json'; 8 | 9 | AppRegistry.registerComponent(appName, () => App); 10 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/hvigorfile.ts: -------------------------------------------------------------------------------- 1 | import {appTasks} from '@ohos/hvigor-ohos-plugin'; 2 | 3 | export default { 4 | system: appTasks /* Built-in plugin of Hvigor. It cannot be modified. */, 5 | plugins: [] /* Custom plugin to extend the functionality of Hvigor. */, 6 | }; 7 | -------------------------------------------------------------------------------- /Example/testHotUpdate/ios/AwesomeProject/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | @autoreleasepool { 8 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.github/workflows/scripts/adb_all_emulators.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "Running $1 on all running emulators..." 3 | devices=`adb devices` 4 | 5 | for device in $devices; do 6 | if [[ "$device" =~ "emulator-" ]]; then 7 | adb -s $device $1 8 | fi 9 | done 10 | echo "All Done." -------------------------------------------------------------------------------- /Example/harmony_use_pushy/ios/harmony_use_pushy/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | @autoreleasepool { 8 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Example/testHotUpdate/scripts/start-rn.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | trap 'kill $RN_PID' EXIT 4 | 5 | PLATFORM=$1 6 | 7 | kill -9 $(lsof -i :8081 | awk '{print $2}' | tail -n +2) & npm start & 8 | RN_PID=$! 9 | sleep 2 && curl>/dev/null http://localhost:8081/index.bundle 10 | wait $RN_PID 11 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/AppScope/app.json5: -------------------------------------------------------------------------------- 1 | { 2 | "app": { 3 | "bundleName": "com.example.myapplication", 4 | "vendor": "example", 5 | "versionCode": 1000000, 6 | "versionName": "1.0.0", 7 | "icon": "$media:app_icon", 8 | "label": "$string:app_name" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.1-all.zip 4 | networkTimeout=10000 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/react_native_openharmony/generated/index.ets: -------------------------------------------------------------------------------- 1 | /** 2 | * This code was generated by "react-native codegen-harmony" 3 | * 4 | * Do not edit this file as changes may cause incorrect behavior and will be 5 | * lost once the code is regenerated. 6 | */ 7 | 8 | export * from "./ts" 9 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'harmony_use_pushy' 2 | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) 3 | include ':app' 4 | includeBuild('../node_modules/@react-native/gradle-plugin') 5 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/entry/src/main/ets/RNPackagesFactory.ts: -------------------------------------------------------------------------------- 1 | import type {RNPackageContext, RNPackage} from '@rnoh/react-native-openharmony/ts'; 2 | import {PushyPackage} from 'pushy/ts'; 3 | 4 | export function createRNPackages(ctx: RNPackageContext): RNPackage[] { 5 | return [new PushyPackage(ctx)]; 6 | } 7 | -------------------------------------------------------------------------------- /harmony/pushy/oh-package.json5: -------------------------------------------------------------------------------- 1 | { 2 | license: 'MIT', 3 | types: '', 4 | devDependencies: {}, 5 | name: 'pushy', 6 | description: '', 7 | main: 'index.ets', 8 | version: '10.35.1', 9 | dependencies: { 10 | '@rnoh/react-native-openharmony': '^0.72.96', 11 | }, 12 | modelVersion: '5.0.0', 13 | } 14 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/entry/src/ohosTest/module.json5: -------------------------------------------------------------------------------- 1 | { 2 | "module": { 3 | "name": "entry_test", 4 | "type": "feature", 5 | "deviceTypes": [ 6 | "phone", 7 | "tablet", 8 | "2in1" 9 | ], 10 | "deliveryWithInstall": true, 11 | "installationFree": false 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /android/jni/Application.mk: -------------------------------------------------------------------------------- 1 | APP_PLATFORM := android-16 2 | APP_CFLAGS += -Wno-error=format-security 3 | APP_CFLAGS += -fvisibility=hidden -fvisibility-inlines-hidden 4 | APP_CFLAGS += -ffunction-sections -fdata-sections 5 | APP_LDFLAGS += -Wl,--gc-sections 6 | APP_BUILD_SCRIPT := Android.mk 7 | APP_ABI := armeabi-v7a arm64-v8a x86 x86_64 8 | -------------------------------------------------------------------------------- /Example/expoUsePushy/update.json: -------------------------------------------------------------------------------- 1 | { 2 | "ios": { 3 | "appId": 29439, 4 | "appKey": "jNA71vpFHTDpEqeZd9yx87zj" 5 | }, 6 | "android": { 7 | "appId": 29413, 8 | "appKey": "vdZWPXU6eyaPE6Avk96-YvwK" 9 | }, 10 | "harmony": { 11 | "appId": 29140, 12 | "appKey": "JLklGflGIRbY-cMebjQwm1J1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/entry/src/main/cpp/PackageProvider.cpp: -------------------------------------------------------------------------------- 1 | #include "RNOH/PackageProvider.h" 2 | #include "PushyPackage.h" 3 | using namespace rnoh; 4 | 5 | std::vector> PackageProvider::getPackages(Package::Context ctx) { 6 | return { 7 | std::make_shared(ctx) 8 | }; 9 | } -------------------------------------------------------------------------------- /android/src/main/java/expo/modules/pushy/ExpoPushyModule.kt: -------------------------------------------------------------------------------- 1 | package expo.modules.pushy 2 | 3 | import expo.modules.kotlin.modules.Module 4 | import expo.modules.kotlin.modules.ModuleDefinition 5 | 6 | class ExpoPushyModule : Module() { 7 | override fun definition() = ModuleDefinition { 8 | Name("ExpoPushy") 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Example/testHotUpdate/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/update.json: -------------------------------------------------------------------------------- 1 | { 2 | "ios": { 3 | "appId": 24794, 4 | "appKey": "SqShg4Klnj2hG6LAFMW2PdcgSSuniz0T" 5 | }, 6 | "android": { 7 | "appId": 27509, 8 | "appKey": "aQz3Uc2pA7gt_prDaQ4rbWRY" 9 | }, 10 | "harmony": { 11 | "appId": 29140, 12 | "appKey": "JLklGflGIRbY-cMebjQwm1J1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.github/workflows/scripts/database.rules: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | // Database in general is closed. Read/Write to anything but "tests/" will fail. 4 | ".read": false, 5 | ".write": false, 6 | 7 | // ..."tests" node will succeed 8 | "tests": { 9 | ".read": true, 10 | ".write": true, 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Example/testHotUpdate/ios/AwesomeProject.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/testHotUpdate/ios/AwesomeProject.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/testHotUpdate/__tests__/App-test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | */ 4 | 5 | import 'react-native'; 6 | import React from 'react'; 7 | import App from '../App'; 8 | 9 | // Note: test renderer must be required after react-native. 10 | import renderer from 'react-test-renderer'; 11 | 12 | it('renders correctly', () => { 13 | renderer.create(); 14 | }); 15 | -------------------------------------------------------------------------------- /expo-module.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "platforms": ["apple", "android"], 3 | "apple": { 4 | "modules": ["ExpoPushyModule"], 5 | "reactDelegateHandlers": ["ExpoPushyReactDelegateHandler"], 6 | "podspecPath":"react-native-update.podspec" 7 | }, 8 | "android": { 9 | "modules": [ 10 | "expo.modules.pushy.ExpoPushyModule" 11 | ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ios/RCTPushy/RCTPushyDownloader.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface RCTPushyDownloader : NSObject 4 | 5 | + (void)download:(NSString *)downloadPath savePath:(NSString *)savePath 6 | progressHandler:(void (^)(long long, long long))progressHandler 7 | completionHandler:(void (^)(NSString *path, NSError *error))completionHandler; 8 | 9 | @end 10 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/react_native_openharmony/generated/ts.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This code was generated by "react-native codegen-harmony" 3 | * 4 | * Do not edit this file as changes may cause incorrect behavior and will be 5 | * lost once the code is regenerated. 6 | */ 7 | 8 | export * as RNC from "./components/ts" 9 | export * as TM from "./turboModules/ts" 10 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/entry/src/main/resources/zh_CN/element/string.json: -------------------------------------------------------------------------------- 1 | { 2 | "string": [ 3 | { 4 | "name": "module_desc", 5 | "value": "模块描述" 6 | }, 7 | { 8 | "name": "EntryAbility_desc", 9 | "value": "description" 10 | }, 11 | { 12 | "name": "EntryAbility_label", 13 | "value": "label" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /Example/testHotUpdate/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Example/testHotUpdate/metro.config.js: -------------------------------------------------------------------------------- 1 | const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config'); 2 | 3 | /** 4 | * Metro configuration 5 | * https://reactnative.dev/docs/metro 6 | * 7 | * @type {import('@react-native/metro-config').MetroConfig} 8 | */ 9 | const config = {}; 10 | 11 | module.exports = mergeConfig(getDefaultConfig(__dirname), config); 12 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /ios/RCTPushy/HDiffPatch/HDiffPatch.h: -------------------------------------------------------------------------------- 1 | // 2 | // HDiffPatch.h 3 | // RCTPushy 4 | // 5 | // Created by HouSisong, All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @interface HDiffPatch : NSObject 11 | 12 | + (BOOL)hdiffPatch:(NSString *)path 13 | origin:(NSString *)origin 14 | toDestination:(NSString *)destination; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/entry/oh-package.json5: -------------------------------------------------------------------------------- 1 | { 2 | name: 'entry', 3 | version: '1.0.0', 4 | description: 'Please describe the basic information.', 5 | main: '', 6 | author: '', 7 | license: '', 8 | dependencies: { 9 | '@rnoh/react-native-openharmony': '0.72.96', 10 | pushy: 'file:../../node_modules/react-native-update/harmony/pushy', 11 | }, 12 | } 13 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/entry/src/main/resources/rawfile/update.json: -------------------------------------------------------------------------------- 1 | { 2 | "ios": { 3 | "appId": 24794, 4 | "appKey": "SqShg4Klnj2hG6LAFMW2PdcgSSuniz0T" 5 | }, 6 | "android": { 7 | "appId": 27509, 8 | "appKey": "aQz3Uc2pA7gt_prDaQ4rbWRY" 9 | }, 10 | "harmony": { 11 | "appId": 29140, 12 | "appKey": "JLklGflGIRbY-cMebjQwm1J1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/entry/src/main/resources/en_US/element/string.json: -------------------------------------------------------------------------------- 1 | { 2 | "string": [ 3 | { 4 | "name": "module_desc", 5 | "value": "module description" 6 | }, 7 | { 8 | "name": "EntryAbility_desc", 9 | "value": "description" 10 | }, 11 | { 12 | "name": "EntryAbility_label", 13 | "value": "label" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /.github/workflows/scripts/functions/src/exports.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Testing tools for invertase/react-native-firebase use only. 4 | * 5 | * Copyright (C) 2018-present Invertase Limited 6 | * 7 | * See License file for more information. 8 | */ 9 | 10 | /* eslint-disable global-require */ 11 | module.exports = { 12 | SAMPLE_DATA: require('./functions/sample-data'), 13 | }; 14 | -------------------------------------------------------------------------------- /.github/workflows/scripts/functions/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "noImplicitReturns": true, 5 | "noUnusedLocals": true, 6 | "outDir": "lib", 7 | "sourceMap": true, 8 | "skipLibCheck": true, 9 | "strict": true, 10 | "target": "es2017" 11 | }, 12 | "compileOnSave": true, 13 | "include": [ 14 | "src" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/entry/hvigorfile.ts: -------------------------------------------------------------------------------- 1 | import {hapTasks} from '@ohos/hvigor-ohos-plugin'; 2 | import {reactNativeUpdatePlugin} from 'pushy/hvigor-plugin'; 3 | 4 | export default { 5 | system: hapTasks /* Built-in plugin of Hvigor. It cannot be modified. */, 6 | plugins: [ 7 | reactNativeUpdatePlugin(), 8 | ] /* Custom plugin to extend the functionality of Hvigor. */, 9 | }; 10 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/entry/src/main/ets/entryability/EntryAbility.ets: -------------------------------------------------------------------------------- 1 | import {RNAbility} from '@rnoh/react-native-openharmony'; 2 | 3 | export default class EntryAbility extends RNAbility { 4 | getPagePath() { 5 | return 'pages/Index'; 6 | } 7 | 8 | shouldCleanUpRNInstance__hack() { 9 | return true // Used by RNOH devs. If set to true, the app may crash. 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Example/testHotUpdate/android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { includeBuild("../node_modules/@react-native/gradle-plugin") } 2 | plugins { id("com.facebook.react.settings") } 3 | extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> ex.autolinkLibrariesFromCommand() } 4 | rootProject.name = 'AwesomeProject' 5 | include ':app' 6 | includeBuild('../node_modules/@react-native/gradle-plugin') 7 | -------------------------------------------------------------------------------- /Example/testHotUpdate/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 10 | -------------------------------------------------------------------------------- /android/jni/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | include $(CLEAR_VARS) 4 | 5 | LOCAL_MODULE := rnupdate 6 | 7 | Hdp_Files := \ 8 | hpatch.c \ 9 | HDiffPatch/libHDiffPatch/HPatch/patch.c \ 10 | HDiffPatch/file_for_patch.c \ 11 | lzma/C/LzmaDec.c \ 12 | lzma/C/Lzma2Dec.c 13 | 14 | LOCAL_SRC_FILES := \ 15 | DownloadTask.c \ 16 | $(Hdp_Files) 17 | 18 | include $(BUILD_SHARED_LIBRARY) -------------------------------------------------------------------------------- /Example/testHotUpdate/src/assets/react-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | React Logo 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/src/main/java/cn/reactnative/modules/update/ReactNativeHostHandler.java: -------------------------------------------------------------------------------- 1 | package cn.reactnative.modules.update; 2 | 3 | import androidx.annotation.Nullable; 4 | 5 | public interface ReactNativeHostHandler { 6 | @Nullable 7 | String getJSBundleFile(boolean useDeveloperSupport); 8 | 9 | @Nullable 10 | String getBundleAssetName(boolean useDeveloperSupport); 11 | 12 | void onWillCreateReactInstance(boolean useDeveloperSupport); 13 | } -------------------------------------------------------------------------------- /e2e/jest.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('@jest/types').Config.InitialOptions} */ 2 | module.exports = { 3 | rootDir: '..', 4 | testMatch: ['/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 | }; 13 | -------------------------------------------------------------------------------- /.github/workflows/scripts/functions/src/testFunctionCustomRegion.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Testing tools for invertase/react-native-firebase use only. 4 | * 5 | * Copyright (C) 2018-present Invertase Limited 6 | * 7 | * See License file for more information. 8 | */ 9 | 10 | import * as functions from 'firebase-functions'; 11 | 12 | export const testFunctionCustomRegion = functions 13 | .region('europe-west1') 14 | .https.onCall(() => 'europe-west1'); 15 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/__tests__/App.test.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | */ 4 | 5 | import 'react-native'; 6 | import React from 'react'; 7 | import App from '../App'; 8 | 9 | // Note: import explicitly to use the types shiped with jest. 10 | import {it} from '@jest/globals'; 11 | 12 | // Note: test renderer must be required after react-native. 13 | import renderer from 'react-test-renderer'; 14 | 15 | it('renders correctly', () => { 16 | renderer.create(); 17 | }); 18 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/code-linter.json5: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "**/*.ets" 4 | ], 5 | "ignore": [ 6 | "**/src/ohosTest/**/*", 7 | "**/src/test/**/*", 8 | "**/src/mock/**/*", 9 | "**/node_modules/**/*", 10 | "**/oh_modules/**/*", 11 | "**/build/**/*", 12 | "**/.preview/**/*" 13 | ], 14 | "ruleSet": [ 15 | "plugin:@performance/recommended", 16 | "plugin:@typescript-eslint/recommended" 17 | ], 18 | "rules": { 19 | } 20 | } -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/oh-package.json5: -------------------------------------------------------------------------------- 1 | { 2 | modelVersion: '5.0.0', 3 | description: 'Please describe the basic information.', 4 | dependencies: {}, 5 | devDependencies: { 6 | '@ohos/hypium': '1.0.19' 7 | }, 8 | arkTs: { 9 | compilerOptions: { 10 | noImplicitAny: false, 11 | suppressImplicitAnyIndexErrors: true, 12 | strict: false, 13 | }, 14 | }, 15 | overrides: { 16 | '@rnoh/react-native-openharmony': '0.72.96', 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /.github/workflows/scripts/start-firebase-emulator.bat: -------------------------------------------------------------------------------- 1 | @REM this pushd is likely not needed, but just in case 2 | pushd "%~dp0" 3 | @REM this is just to see what our current directory is. Should be .github/workflow/scripts 4 | echo %cd% 5 | @REM strangely, unless you specify the config file as being right in the current directory, it won't find it, and everything fails 6 | yarn firebase emulators:start --config %cd%\firebase.json --only auth,database,firestore,functions,storage --project react-native-firebase-testing -------------------------------------------------------------------------------- /android/src/main/java/cn/reactnative/modules/update/PushyFileProvider.java: -------------------------------------------------------------------------------- 1 | package cn.reactnative.modules.update; 2 | 3 | import androidx.core.content.FileProvider; 4 | 5 | /** 6 | * Providing a custom {@code FileProvider} prevents manifest {@code } name collisions. 7 | * 8 | * See https://developer.android.com/guide/topics/manifest/provider-element.html for details. 9 | */ 10 | public class PushyFileProvider extends FileProvider { 11 | 12 | // This class intentionally left blank. 13 | 14 | } -------------------------------------------------------------------------------- /.github/workflows/scripts/firestore.rules: -------------------------------------------------------------------------------- 1 | rules_version = '2'; 2 | service cloud.firestore { 3 | match /databases/{database}/documents { 4 | match /{document=**} { 5 | allow read, write: if false; 6 | } 7 | match /firestore-bundle-tests/{document=**} { 8 | allow read, write: if true; 9 | } 10 | match /firestore/{document=**} { 11 | allow read, write: if true; 12 | } 13 | match /{path=**}/collectionGroup/{documentId} { 14 | allow read, write: if true; 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /Example/testHotUpdate/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | -------------------------------------------------------------------------------- /.github/workflows/scripts/storage.rules: -------------------------------------------------------------------------------- 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 | } -------------------------------------------------------------------------------- /Example/testHotUpdate/e2e/jest.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('@jest/types').Config.InitialOptions} */ 2 | module.exports = { 3 | rootDir: '..', 4 | testMatch: ['/e2e/**/*.test.ts'], 5 | testTimeout: 240000, 6 | maxWorkers: 2, 7 | transform: { 8 | '\\.tsx?$': 'ts-jest', 9 | }, 10 | globalSetup: 'detox/runners/jest/globalSetup', 11 | globalTeardown: 'detox/runners/jest/globalTeardown', 12 | reporters: ['detox/runners/jest/reporter'], 13 | testEnvironment: 'detox/runners/jest/testEnvironment', 14 | verbose: true, 15 | }; 16 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets: -------------------------------------------------------------------------------- 1 | import { hilog } from '@kit.PerformanceAnalysisKit'; 2 | import { BackupExtensionAbility, BundleVersion } from '@kit.CoreFileKit'; 3 | 4 | export default class EntryBackupAbility extends BackupExtensionAbility { 5 | async onBackup() { 6 | hilog.info(0x0000, 'testTag', 'onBackup ok'); 7 | } 8 | 9 | async onRestore(bundleVersion: BundleVersion) { 10 | hilog.info(0x0000, 'testTag', 'onRestore ok %{public}s', JSON.stringify(bundleVersion)); 11 | } 12 | } -------------------------------------------------------------------------------- /Example/harmony_use_pushy/ios/.xcode.env: -------------------------------------------------------------------------------- 1 | # This `.xcode.env` file is versioned and is used to source the environment 2 | # used when running script phases inside Xcode. 3 | # To customize your local environment, you can create an `.xcode.env.local` 4 | # file that is not versioned. 5 | 6 | # NODE_BINARY variable contains the PATH to the node executable. 7 | # 8 | # Customize the NODE_BINARY variable here. 9 | # For example, to use nvm with brew, add the following line 10 | # . "$(brew --prefix nvm)/nvm.sh" --no-use 11 | export NODE_BINARY=$(command -v node) 12 | -------------------------------------------------------------------------------- /Example/testHotUpdate/ios/.xcode.env: -------------------------------------------------------------------------------- 1 | # This `.xcode.env` file is versioned and is used to source the environment 2 | # used when running script phases inside Xcode. 3 | # To customize your local environment, you can create an `.xcode.env.local` 4 | # file that is not versioned. 5 | 6 | # NODE_BINARY variable contains the PATH to the node executable. 7 | # 8 | # Customize the NODE_BINARY variable here. 9 | # For example, to use nvm with brew, add the following line 10 | # . "$(brew --prefix nvm)/nvm.sh" --no-use 11 | export NODE_BINARY=$(command -v node) 12 | -------------------------------------------------------------------------------- /Example/expoUsePushy/.gitignore: -------------------------------------------------------------------------------- 1 | # Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files 2 | 3 | # dependencies 4 | node_modules/ 5 | 6 | # Expo 7 | .expo/ 8 | dist/ 9 | web-build/ 10 | expo-env.d.ts 11 | 12 | # Native 13 | *.orig.* 14 | *.jks 15 | *.p8 16 | *.p12 17 | *.key 18 | *.mobileprovision 19 | 20 | # Metro 21 | .metro-health-check* 22 | 23 | # debug 24 | npm-debug.* 25 | yarn-debug.* 26 | yarn-error.* 27 | 28 | # macOS 29 | .DS_Store 30 | *.pem 31 | 32 | # local env files 33 | .env*.local 34 | 35 | # typescript 36 | *.tsbuildinfo 37 | -------------------------------------------------------------------------------- /.github/workflows/scripts/functions/src/index.ts: -------------------------------------------------------------------------------- 1 | import * as functions from 'firebase-functions'; 2 | 3 | // // Start writing Firebase Functions 4 | // // https://firebase.google.com/docs/functions/typescript 5 | // 6 | export const helloWorld = functions.https.onRequest((request, response) => { 7 | functions.logger.info('Hello logs!', { structuredData: true }); 8 | response.send('{ "data": "Hello from Firebase!" }'); 9 | }); 10 | 11 | export { testFunctionCustomRegion } from './testFunctionCustomRegion'; 12 | export { testFunctionDefaultRegion } from './testFunctionDefaultRegion'; 13 | -------------------------------------------------------------------------------- /harmony/pushy/BuildProfile.ets: -------------------------------------------------------------------------------- 1 | /** 2 | * Use these variables when you tailor your ArkTS code. They must be of the const type. 3 | */ 4 | export const HAR_VERSION = '3.1.0-0.0.7'; 5 | export const BUILD_MODE_NAME = 'debug'; 6 | export const DEBUG = true; 7 | export const TARGET_NAME = 'default'; 8 | 9 | /** 10 | * BuildProfile Class is used only for compatibility purposes. 11 | */ 12 | export default class BuildProfile { 13 | static readonly HAR_VERSION = HAR_VERSION; 14 | static readonly BUILD_MODE_NAME = BUILD_MODE_NAME; 15 | static readonly DEBUG = DEBUG; 16 | static readonly TARGET_NAME = TARGET_NAME; 17 | } -------------------------------------------------------------------------------- /Example/harmony_use_pushy/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Example/testHotUpdate/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext { 3 | buildToolsVersion = "36.0.0" 4 | minSdkVersion = 24 5 | compileSdkVersion = 36 6 | targetSdkVersion = 36 7 | ndkVersion = "27.1.12297006" 8 | kotlinVersion = "2.1.20" 9 | } 10 | repositories { 11 | google() 12 | mavenCentral() 13 | } 14 | dependencies { 15 | classpath("com.android.tools.build:gradle") 16 | classpath("com.facebook.react:react-native-gradle-plugin") 17 | classpath("org.jetbrains.kotlin:kotlin-gradle-plugin") 18 | } 19 | } 20 | 21 | apply plugin: "com.facebook.react.rootproject" 22 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/README.md: -------------------------------------------------------------------------------- 1 | ## 运行harmony_use_pushy项目步骤 2 | 3 | ### 1. 在项目根目录执行下面命令安装第三方依赖。 4 | ``` 5 | bun install 6 | ``` 7 | 8 | ### 2. 本地debug 模式 9 | ``` 10 | bun run start 11 | ``` 12 | ![image](./debug.png) 13 | 14 | ### 3. release 模式: 在项目根目录执行下面命令生成bundle包文件。 15 | ``` 16 | bun run build 17 | ``` 18 | 说明:这个命令会在harmony/entry/src/main/resources/rawfile目录生成Hbundle.harmony.js和assets文件,同时会基于该内容在.pushy/output目录生成ppk包。 19 | 20 | **注意⚠️**:在使用pushy bundle --platform harmony命令进行打包的默认bundle包名是Hbundle.harmony.js,不要随意修改包名,因为diff是匹配该包名进行生成的。 21 | 22 | ### 4. 使用DevEco Studio IDE打开harmony目录然后执行sync运行项目 23 | ![image](./sync.png) 24 | 25 | ### 5 运行效果图 26 | ![image](./demo.png) 27 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifestNew.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/entry/src/main/resources/base/element/string.json: -------------------------------------------------------------------------------- 1 | { 2 | "string": [ 3 | { 4 | "name": "Access_location", 5 | "value": "access location" 6 | }, 7 | { 8 | "name": "module_desc", 9 | "value": "module description" 10 | }, 11 | { 12 | "name": "EntryAbility_desc", 13 | "value": "description" 14 | }, 15 | { 16 | "name": "EntryAbility_label", 17 | "value": "label" 18 | }, 19 | { 20 | "name": "reason_write_media", 21 | "value": "Write files to internal storage" 22 | }, 23 | { 24 | "name": "reason_read_media", 25 | "value": "Read files to internal storage" 26 | } 27 | ] 28 | } -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/oh-package-lock.json5: -------------------------------------------------------------------------------- 1 | { 2 | "meta": { 3 | "stableOrder": true, 4 | "enableUnifiedLockfile": false 5 | }, 6 | "lockfileVersion": 3, 7 | "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.", 8 | "specifiers": { 9 | "@ohos/hypium@1.0.19": "@ohos/hypium@1.0.19" 10 | }, 11 | "packages": { 12 | "@ohos/hypium@1.0.19": { 13 | "name": "", 14 | "version": "1.0.19", 15 | "integrity": "sha512-cEjDgLFCm3cWZDeRXk7agBUkPqjWxUo6AQeiu0gEkb3J8ESqlduQLSIXeo3cCsm8U/asL7iKjF85ZyOuufAGSQ==", 16 | "resolved": "https://ohpm.openharmony.cn/ohpm/@ohos/hypium/-/hypium-1.0.19.har", 17 | "registryType": "ohpm" 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /android/jni/cn_reactnative_modules_update_DownloadTask.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT THIS FILE - it is machine generated */ 2 | #include 3 | /* Header for class cn_reactnative_modules_update_DownloadTask */ 4 | 5 | #ifndef _Included_cn_reactnative_modules_update_DownloadTask 6 | #define _Included_cn_reactnative_modules_update_DownloadTask 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | /* 11 | * Class: cn_reactnative_modules_update_DownloadTask 12 | * Method: hdiffPatch 13 | * Signature: ([B[B)[B 14 | */ 15 | JNIEXPORT jbyteArray JNICALL Java_cn_reactnative_modules_update_DownloadTask_hdiffPatch 16 | (JNIEnv *, jclass, jbyteArray, jbyteArray); 17 | 18 | #ifdef __cplusplus 19 | } 20 | #endif 21 | #endif 22 | -------------------------------------------------------------------------------- /Example/testHotUpdate/android/app/build_defs.bzl: -------------------------------------------------------------------------------- 1 | """Helper definitions to glob .aar and .jar targets""" 2 | 3 | def create_aar_targets(aarfiles): 4 | for aarfile in aarfiles: 5 | name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")] 6 | lib_deps.append(":" + name) 7 | android_prebuilt_aar( 8 | name = name, 9 | aar = aarfile, 10 | ) 11 | 12 | def create_jar_targets(jarfiles): 13 | for jarfile in jarfiles: 14 | name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")] 15 | lib_deps.append(":" + name) 16 | prebuilt_jar( 17 | name = name, 18 | binary_jar = jarfile, 19 | ) 20 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext { 5 | buildToolsVersion = "33.0.0" 6 | minSdkVersion = 21 7 | compileSdkVersion = 33 8 | targetSdkVersion = 33 9 | 10 | // We use NDK 23 which has both M1 support and is the side-by-side NDK version from AGP. 11 | ndkVersion = "23.1.7779620" 12 | } 13 | repositories { 14 | google() 15 | mavenCentral() 16 | } 17 | dependencies { 18 | classpath("com.android.tools.build:gradle") 19 | classpath("com.facebook.react:react-native-gradle-plugin") 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 10 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /.github/workflows/scripts/functions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "functions", 3 | "scripts": { 4 | "build": "tsc", 5 | "serve": "npm run build && firebase emulators:start --only functions", 6 | "shell": "npm run build && firebase functions:shell", 7 | "start": "npm run shell", 8 | "deploy": "firebase deploy --only functions", 9 | "logs": "firebase functions:log" 10 | }, 11 | "engines": { 12 | "node": "16" 13 | }, 14 | "main": "lib/index.js", 15 | "dependencies": { 16 | "firebase-admin": "^11.3.0", 17 | "firebase-functions": "^4.2.1" 18 | }, 19 | "devDependencies": { 20 | "firebase-functions-test": "^3.0.0", 21 | "typescript": "^4.9.5" 22 | }, 23 | "private": true 24 | } 25 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/metro.config.js: -------------------------------------------------------------------------------- 1 | const {mergeConfig, getDefaultConfig} = require('@react-native/metro-config'); 2 | const {createHarmonyMetroConfig} = require('@react-native-oh/react-native-harmony/metro.config'); 3 | 4 | /** 5 | * @type {import("metro-config").ConfigT} 6 | */ 7 | const config = { 8 | transformer: { 9 | getTransformOptions: async () => ({ 10 | transform: { 11 | experimentalImportSupport: false, 12 | inlineRequires: true, 13 | }, 14 | }), 15 | }, 16 | }; 17 | 18 | module.exports = mergeConfig( 19 | getDefaultConfig(__dirname), 20 | createHarmonyMetroConfig({ 21 | reactNativeHarmonyPackageName: '@react-native-oh/react-native-harmony', 22 | }), 23 | config, 24 | ); 25 | -------------------------------------------------------------------------------- /e2e/starter.test.js: -------------------------------------------------------------------------------- 1 | describe('Example', () => { 2 | beforeAll(async () => { 3 | await device.launchApp(); 4 | }); 5 | 6 | beforeEach(async () => { 7 | await device.reloadReactNative(); 8 | }); 9 | 10 | it('should have welcome screen', async () => { 11 | await expect(element(by.id('welcome'))).toBeVisible(); 12 | }); 13 | 14 | it('should show hello screen after tap', async () => { 15 | await element(by.id('hello_button')).tap(); 16 | await expect(element(by.text('Hello!!!'))).toBeVisible(); 17 | }); 18 | 19 | it('should show world screen after tap', async () => { 20 | await element(by.id('world_button')).tap(); 21 | await expect(element(by.text('World!!!'))).toBeVisible(); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/entry/build-profile.json5: -------------------------------------------------------------------------------- 1 | { 2 | "apiType": "stageMode", 3 | "buildOption": { 4 | "externalNativeOptions": { 5 | "path": "./src/main/cpp/CMakeLists.txt", 6 | "arguments": "", 7 | "cppFlags": "", 8 | }, 9 | }, 10 | "buildOptionSet": [ 11 | { 12 | "name": "release", 13 | "arkOptions": { 14 | "obfuscation": { 15 | "ruleOptions": { 16 | "enable": false, 17 | "files": [ 18 | "./obfuscation-rules.txt" 19 | ] 20 | } 21 | } 22 | } 23 | }, 24 | ], 25 | "targets": [ 26 | { 27 | "name": "default" 28 | }, 29 | { 30 | "name": "ohosTest", 31 | } 32 | ] 33 | } -------------------------------------------------------------------------------- /harmony/pushy/oh-package-lock.json5: -------------------------------------------------------------------------------- 1 | { 2 | "meta": { 3 | "stableOrder": true, 4 | "enableUnifiedLockfile": false 5 | }, 6 | "lockfileVersion": 3, 7 | "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.", 8 | "specifiers": { 9 | "@rnoh/react-native-openharmony@^0.72.96": "@rnoh/react-native-openharmony@0.72.96" 10 | }, 11 | "packages": { 12 | "@rnoh/react-native-openharmony@0.72.96": { 13 | "name": "", 14 | "version": "0.72.96", 15 | "integrity": "sha512-gBbm8LLyqi5UE7qHWdZYeQnjyncfEpCczKZUP/9M2U1Z7exR0Kya8PMKMwr1ta5ujy7w/hZVC2LomEV4QvBeqA==", 16 | "resolved": "https://ohpm.openharmony.cn/ohpm/@rnoh/react-native-openharmony/-/react-native-openharmony-0.72.96.har", 17 | "registryType": "ohpm" 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .babelrc 2 | .npmignore 3 | .eslintrc 4 | .nvmrc 5 | .travis.yml 6 | Example 7 | android/build 8 | .vscode 9 | .github/ 10 | 11 | # OSX 12 | # 13 | .DS_Store 14 | 15 | # Xcode 16 | # 17 | build/ 18 | *.pbxuser 19 | !default.pbxuser 20 | *.mode1v3 21 | !default.mode1v3 22 | *.mode2v3 23 | !default.mode2v3 24 | *.perspectivev3 25 | !default.perspectivev3 26 | xcuserdata 27 | *.xccheckout 28 | *.moved-aside 29 | DerivedData 30 | *.hmap 31 | *.ipa 32 | *.xcuserstate 33 | project.xcworkspace 34 | 35 | # Android/IJ 36 | # 37 | .idea 38 | .gradle 39 | local.properties 40 | android/build 41 | android/obj 42 | 43 | # node.js 44 | # 45 | node_modules/ 46 | npm-debug.log 47 | Example 48 | yarn.lock 49 | bun.lock 50 | 51 | domains.json 52 | endpoints.json 53 | endpoints_cresc.json 54 | 55 | tea.yaml 56 | 57 | e2e/ -------------------------------------------------------------------------------- /.github/workflows/scripts/firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "firestore": { 3 | "rules": "firestore.rules", 4 | "indexes": "firestore.indexes.json" 5 | }, 6 | "functions": { 7 | "predeploy": [ 8 | "yarn", 9 | "yarn --prefix \"$RESOURCE_DIR\" build" 10 | ], 11 | "source": "functions" 12 | }, 13 | "database": { 14 | "rules": "database.rules" 15 | }, 16 | "storage": { 17 | "rules": "storage.rules" 18 | }, 19 | "emulators": { 20 | "auth": { 21 | "port": 9099 22 | }, 23 | "database": { 24 | "port": 9000 25 | }, 26 | "firestore": { 27 | "port": 8080 28 | }, 29 | "functions": { 30 | "port": 5001 31 | }, 32 | "storage": { 33 | "port": 9199 34 | }, 35 | "ui": { 36 | "enabled": true 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /harmony/pushy/src/main/ets/PushyPackage.ts: -------------------------------------------------------------------------------- 1 | import { RNPackage, TurboModulesFactory } from '@rnoh/react-native-openharmony/ts'; 2 | import type { TurboModule, TurboModuleContext } from '@rnoh/react-native-openharmony/ts'; 3 | import { PushyTurboModule } from './PushyTurboModule'; 4 | 5 | class PushyTurboModulesFactory extends TurboModulesFactory { 6 | createTurboModule(name: string): TurboModule | null { 7 | if (name === 'Pushy') { 8 | return new PushyTurboModule(this.ctx); 9 | } 10 | return null; 11 | } 12 | 13 | hasTurboModule(name: string): boolean { 14 | return name === 'Pushy'; 15 | } 16 | } 17 | 18 | export class PushyPackage extends RNPackage { 19 | createTurboModulesFactory(ctx: TurboModuleContext): TurboModulesFactory { 20 | return new PushyTurboModulesFactory(ctx); 21 | } 22 | } -------------------------------------------------------------------------------- /android/src/main/java/cn/reactnative/modules/update/DownloadTaskParams.java: -------------------------------------------------------------------------------- 1 | package cn.reactnative.modules.update; 2 | 3 | import java.io.File; 4 | 5 | /** 6 | * Created by tdzl2003 on 3/31/16. 7 | */ 8 | class DownloadTaskParams { 9 | static final int TASK_TYPE_CLEANUP = 0; //Keep hash & originHash 10 | 11 | static final int TASK_TYPE_PATCH_FULL = 1; 12 | static final int TASK_TYPE_PATCH_FROM_APK = 2; 13 | static final int TASK_TYPE_PATCH_FROM_PPK = 3; 14 | static final int TASK_TYPE_PLAIN_DOWNLOAD = 4; 15 | 16 | 17 | int type; 18 | String url; 19 | String hash; 20 | String originHash; 21 | File targetFile; 22 | File unzipDirectory; 23 | File originDirectory; 24 | UpdateContext.DownloadFileListener listener; 25 | } 26 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish Package to npmjs 2 | on: 3 | release: 4 | types: [published] 5 | 6 | permissions: 7 | id-token: write # Required for OIDC 8 | contents: read 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | permissions: 14 | contents: read 15 | id-token: write 16 | steps: 17 | - uses: actions/checkout@v5 18 | - uses: oven-sh/setup-bun@v2 19 | # Setup .npmrc file to publish to npm 20 | - uses: actions/setup-node@v6 21 | with: 22 | node-version: 24 23 | registry-url: 'https://registry.npmjs.org' 24 | - run: bun install --frozen-lockfile 25 | # Ensure npm 11.5.1 or later is installed 26 | - name: Update npm 27 | run: npm install -g npm@latest 28 | - run: npm publish --provenance --access public 29 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/android/app/src/release/java/com/harmony_use_pushy/ReactNativeFlipper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | *

This source code is licensed under the MIT license found in the LICENSE file in the root 5 | * directory of this source tree. 6 | */ 7 | package com.harmony_use_pushy; 8 | 9 | import android.content.Context; 10 | import com.facebook.react.ReactInstanceManager; 11 | 12 | /** 13 | * Class responsible of loading Flipper inside your React Native application. This is the release 14 | * flavor of it so it's empty as we don't want to load Flipper. 15 | */ 16 | public class ReactNativeFlipper { 17 | public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) { 18 | // Do nothing as we don't want to initialize Flipper on Release. 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Example/expoUsePushy/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "expoUsePushy", 4 | "slug": "expoUsePushy", 5 | "version": "1.0.0", 6 | "orientation": "portrait", 7 | "icon": "./assets/icon.png", 8 | "userInterfaceStyle": "light", 9 | "newArchEnabled": true, 10 | "splash": { 11 | "image": "./assets/splash-icon.png", 12 | "resizeMode": "contain", 13 | "backgroundColor": "#ffffff" 14 | }, 15 | "ios": { 16 | "supportsTablet": true, 17 | "bundleIdentifier": "com.anonymous.expoUsePushy" 18 | }, 19 | "android": { 20 | "adaptiveIcon": { 21 | "foregroundImage": "./assets/adaptive-icon.png", 22 | "backgroundColor": "#ffffff" 23 | }, 24 | "package": "com.anonymous.expoUsePushy" 25 | }, 26 | "web": { 27 | "favicon": "./assets/favicon.png" 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/ios/harmony_use_pushyTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/ios/harmony_use_pushy/AppDelegate.mm: -------------------------------------------------------------------------------- 1 | #import "AppDelegate.h" 2 | 3 | #import 4 | 5 | @implementation AppDelegate 6 | 7 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 8 | { 9 | self.moduleName = @"harmony_use_pushy"; 10 | // You can add your custom initial props in the dictionary below. 11 | // They will be passed down to the ViewController used by React Native. 12 | self.initialProps = @{}; 13 | 14 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 15 | } 16 | 17 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge 18 | { 19 | #if DEBUG 20 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; 21 | #else 22 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; 23 | #endif 24 | } 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /android/proguard.pro: -------------------------------------------------------------------------------- 1 | # Keep our update module classes 2 | -keepnames class cn.reactnative.modules.update.DownloadTask { *; } 3 | -keepnames class cn.reactnative.modules.update.UpdateModuleImpl { *; } 4 | -keepnames class cn.reactnative.modules.update.** { *; } 5 | 6 | # Keep React Native classes 7 | -keepnames class com.facebook.react.ReactInstanceManager { *; } 8 | -keepnames class com.facebook.react.** { *; } 9 | -keepnames class com.facebook.react.bridge.** { *; } 10 | -keepnames class com.facebook.react.devsupport.** { *; } 11 | 12 | # Keep fields used in reflection 13 | -keepclassmembers class com.facebook.react.ReactActivity { *; } 14 | -keepclassmembers class com.facebook.react.ReactInstanceManager { *; } 15 | -keepclassmembers class com.facebook.react.ReactDelegate { *; } 16 | -keepclassmembers class com.facebook.react.ReactHost { *; } 17 | 18 | -keepnames class expo.modules.ExpoReactHostFactory$ExpoReactHostDelegate { *; } 19 | -------------------------------------------------------------------------------- /ios/RCTPushy/HDiffPatch/HDiffPatch.mm: -------------------------------------------------------------------------------- 1 | // 2 | // HDiffPatch.m 3 | // RCTPushy 4 | // 5 | // Created by HouSisong, All rights reserved. 6 | // 7 | 8 | #import "HDiffPatch.h" 9 | #include "../../../android/jni/hpatch.h" 10 | 11 | @implementation HDiffPatch 12 | 13 | + (BOOL)hdiffPatch:(NSString *)patch 14 | origin:(NSString *)origin 15 | toDestination:(NSString *)destination 16 | { 17 | if (![[NSFileManager defaultManager] fileExistsAtPath:patch]) { 18 | return NO; 19 | } 20 | if (![[NSFileManager defaultManager] fileExistsAtPath:origin]) { 21 | return NO; 22 | } 23 | 24 | if ([[NSFileManager defaultManager] fileExistsAtPath:destination]) { 25 | [[NSFileManager defaultManager] removeItemAtPath:destination error:nil]; 26 | } 27 | 28 | int err = hpatch_by_file([origin UTF8String], [destination UTF8String], [patch UTF8String]); 29 | if (err) { 30 | return NO; 31 | } 32 | return YES; 33 | } 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /Example/testHotUpdate/android/app/src/main/java/com/awesomeproject/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.awesomeproject 2 | 3 | import com.facebook.react.ReactActivity 4 | import com.facebook.react.ReactActivityDelegate 5 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled 6 | import com.facebook.react.defaults.DefaultReactActivityDelegate 7 | 8 | class MainActivity : ReactActivity() { 9 | 10 | /** 11 | * Returns the name of the main component registered from JavaScript. This is used to schedule 12 | * rendering of the component. 13 | */ 14 | override fun getMainComponentName(): String = "AwesomeProject" 15 | 16 | /** 17 | * Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate] 18 | * which allows you to enable New Architecture with a single boolean flags [fabricEnabled] 19 | */ 20 | override fun createReactActivityDelegate(): ReactActivityDelegate = 21 | DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled) 22 | } 23 | -------------------------------------------------------------------------------- /harmony/pushy/src/main/ets/DownloadTaskParams.ts: -------------------------------------------------------------------------------- 1 | export interface DownloadTaskListener { 2 | onDownloadCompleted(params: DownloadTaskParams): void; 3 | onDownloadFailed(error: Error): void; 4 | } 5 | 6 | /** 7 | * 下载任务参数类 8 | */ 9 | export class DownloadTaskParams { 10 | // 任务类型常量 11 | static readonly TASK_TYPE_CLEANUP: number = 0; // 保留hash和originHash 12 | static readonly TASK_TYPE_PATCH_FULL: number = 1; // 全量补丁 13 | static readonly TASK_TYPE_PATCH_FROM_APP: number = 2; // 从APP补丁 14 | static readonly TASK_TYPE_PATCH_FROM_PPK: number = 3; // 从PPK补丁 15 | static readonly TASK_TYPE_PLAIN_DOWNLOAD: number = 4; // 普通下载 16 | 17 | type: number; // 任务类型 18 | url: string; // 下载URL 19 | hash: string; // 文件哈希值 20 | originHash: string; // 原始文件哈希值 21 | targetFile: string; // 目标文件路径 22 | unzipDirectory: string; // 解压目录路径 23 | originDirectory: string; // 原始文件目录路径 24 | listener: DownloadTaskListener; // 下载监听器 25 | } -------------------------------------------------------------------------------- /harmony/pushy/src/main/ets/Logger.ts: -------------------------------------------------------------------------------- 1 | 2 | import hilog from '@ohos.hilog'; 3 | 4 | class Logger { 5 | private domain: number; 6 | private prefix: string; 7 | private format: string = '%{public}s,%{public}s'; 8 | private isDebug: boolean; 9 | 10 | constructor(prefix: string = 'MyApp', domain: number = 0xFF00, isDebug = false) { 11 | this.prefix = prefix; 12 | this.domain = domain; 13 | this.isDebug = isDebug; 14 | } 15 | 16 | debug(...args: string[]): void { 17 | if (this.isDebug) { 18 | hilog.debug(this.domain, this.prefix, this.format, args); 19 | } 20 | } 21 | 22 | info(...args: string[]): void { 23 | hilog.info(this.domain, this.prefix, this.format, args); 24 | } 25 | 26 | warn(...args: string[]): void { 27 | hilog.warn(this.domain, this.prefix, this.format, args); 28 | } 29 | 30 | error(...args: string[]): void { 31 | hilog.error(this.domain, this.prefix, this.format, args); 32 | } 33 | } 34 | 35 | export default new Logger('geolocation', 0xFF00, false) -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/build-profile.json5: -------------------------------------------------------------------------------- 1 | { 2 | app: { 3 | signingConfigs: [], 4 | products: [ 5 | { 6 | name: 'default', 7 | signingConfig: 'default', 8 | compatibleSdkVersion: '5.0.0(12)', 9 | runtimeOS: 'HarmonyOS', 10 | buildOption: { 11 | strictMode: { 12 | caseSensitiveCheck: true, 13 | useNormalizedOHMUrl: true 14 | }, 15 | "nativeCompiler": "BiSheng" 16 | }, 17 | }, 18 | ], 19 | buildModeSet: [ 20 | { 21 | name: 'debug', 22 | }, 23 | { 24 | name: 'release', 25 | }, 26 | ], 27 | }, 28 | modules: [ 29 | { 30 | name: 'entry', 31 | srcPath: './entry', 32 | targets: [ 33 | { 34 | name: 'default', 35 | applyToProducts: ['default'], 36 | }, 37 | ], 38 | }, 39 | { 40 | name: 'pushy', 41 | srcPath: '../node_modules/react-native-update/harmony/pushy', 42 | }, 43 | ], 44 | } 45 | -------------------------------------------------------------------------------- /android/src/main/java/expo/modules/pushy/ExpoPushyPackage.java: -------------------------------------------------------------------------------- 1 | package expo.modules.pushy; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | import androidx.annotation.Nullable; 6 | import java.util.ArrayList; 7 | import java.util.HashMap; 8 | import java.util.List; 9 | import java.util.Map; 10 | import cn.reactnative.modules.update.UpdateContext; 11 | import expo.modules.core.interfaces.Package; 12 | import expo.modules.core.interfaces.ReactNativeHostHandler; 13 | 14 | public class ExpoPushyPackage implements Package { 15 | @Override 16 | public List createReactNativeHostHandlers(Context context) { 17 | List handlers = new ArrayList<>(); 18 | handlers.add(new ReactNativeHostHandler() { 19 | @Nullable 20 | @Override 21 | public String getJSBundleFile(boolean useDeveloperSupport) { 22 | return UpdateContext.getBundleUrl(context); 23 | } 24 | }); 25 | return handlers; 26 | } 27 | } -------------------------------------------------------------------------------- /Example/testHotUpdate/android/app/src/androidTest/java/com/awesomeproject/DetoxTest.java: -------------------------------------------------------------------------------- 1 | package com.awesomeproject; 2 | 3 | import com.wix.detox.Detox; 4 | import com.wix.detox.config.DetoxConfig; 5 | 6 | import org.junit.Rule; 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import androidx.test.ext.junit.runners.AndroidJUnit4; 11 | import androidx.test.filters.LargeTest; 12 | import androidx.test.rule.ActivityTestRule; 13 | 14 | @RunWith(AndroidJUnit4.class) 15 | @LargeTest 16 | public class DetoxTest { 17 | @Rule 18 | public ActivityTestRule mActivityRule = new ActivityTestRule<>(MainActivity.class, false, false); 19 | 20 | @Test 21 | public void runDetoxTests() { 22 | DetoxConfig detoxConfig = new DetoxConfig(); 23 | detoxConfig.idlePolicyConfig.masterTimeoutSec = 90; 24 | detoxConfig.idlePolicyConfig.idleResourceTimeoutSec = 60; 25 | detoxConfig.rnContextLoadTimeoutSec = (BuildConfig.DEBUG ? 180 : 60); 26 | 27 | Detox.runTests(mActivityRule, detoxConfig); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Example/testHotUpdate/ios/AwesomeProject/AppDelegate.mm: -------------------------------------------------------------------------------- 1 | #import "AppDelegate.h" 2 | #import "RCTPushy.h" 3 | 4 | #import 5 | #import 6 | 7 | @implementation AppDelegate 8 | 9 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 10 | { 11 | self.moduleName = @"AwesomeProject"; 12 | self.dependencyProvider = [RCTAppDependencyProvider new]; 13 | // You can add your custom initial props in the dictionary below. 14 | // They will be passed down to the ViewController used by React Native. 15 | self.initialProps = @{}; 16 | 17 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 18 | } 19 | 20 | - (NSURL *)bundleURL 21 | { 22 | #if DEBUG 23 | // 原先DEBUG这里的写法不作修改(所以DEBUG模式下不可热更新) 24 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; 25 | #else 26 | return [RCTPushy bundleURL]; // <-- 把这里非DEBUG的情况替换为热更新bundle 27 | #endif 28 | } 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /ios/RCTPushy/RCTPushyManager.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface RCTPushyManager : NSObject 4 | 5 | 6 | - (BOOL)createDir:(NSString *)dir; 7 | 8 | - (void)unzipFileAtPath:(NSString *)path 9 | toDestination:(NSString *)destination 10 | progressHandler:(void (^)(NSString *entry, long entryNumber, long total))progressHandler 11 | completionHandler:(void (^)(NSString *path, BOOL succeeded, NSError *error))completionHandler; 12 | 13 | - (void)hdiffFileAtPath:(NSString *)path 14 | fromOrigin:(NSString *)origin 15 | toDestination:(NSString *)destination 16 | completionHandler:(void (^)(BOOL success))completionHandler; 17 | 18 | - (void)copyFiles:(NSDictionary *)filesDic 19 | fromDir:(NSString *)fromDir 20 | toDir:(NSString *)toDir 21 | deletes:(NSDictionary *)deletes 22 | completionHandler:(void (^)(NSError *error))completionHandler; 23 | 24 | - (void)removeFile:(NSString *)filePath 25 | completionHandler:(void (^)(NSError *error))completionHandler; 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /Example/testHotUpdate/android/app/src/main/java/com/awesomeproject/MainApplication.kt: -------------------------------------------------------------------------------- 1 | package com.awesomeproject 2 | 3 | import android.app.Application 4 | import com.facebook.react.PackageList 5 | import com.facebook.react.ReactApplication 6 | import com.facebook.react.ReactHost 7 | import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative 8 | import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost 9 | import cn.reactnative.modules.update.UpdateContext 10 | 11 | class MainApplication : Application(), ReactApplication { 12 | 13 | override val reactHost: ReactHost by lazy { 14 | getDefaultReactHost( 15 | context = applicationContext, 16 | packageList = 17 | PackageList(this).packages.apply { 18 | // Packages that cannot be auto-linked yet can be added manually here, for example: 19 | // add(MyReactNativePackage()) 20 | }, 21 | jsBundleFilePath = UpdateContext.getBundleUrl(this), 22 | ) 23 | } 24 | 25 | override fun onCreate() { 26 | super.onCreate() 27 | loadReactNative(this) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /harmony/pushy/src/main/ets/EventHub.ts: -------------------------------------------------------------------------------- 1 | type EventCallback = (data: any) => void; 2 | 3 | export class EventHub { 4 | private static instance: EventHub; 5 | private listeners: Map>; 6 | private rnInstance: any; 7 | 8 | private constructor() { 9 | this.listeners = new Map(); 10 | } 11 | 12 | public static getInstance(): EventHub { 13 | if (!EventHub.instance) { 14 | EventHub.instance = new EventHub(); 15 | } 16 | return EventHub.instance; 17 | } 18 | 19 | public on(event: string, callback: EventCallback): void { 20 | if (!this.listeners.has(event)) { 21 | this.listeners.set(event, new Set()); 22 | } 23 | this.listeners.get(event)?.add(callback); 24 | } 25 | 26 | public off(event: string, callback: EventCallback): void { 27 | this.listeners.get(event)?.delete(callback); 28 | } 29 | 30 | public emit(event: string, data: any): void { 31 | if (this.rnInstance) { 32 | this.rnInstance.emitDeviceEvent(event, data); 33 | } 34 | } 35 | 36 | setRNInstance(instance: any) { 37 | this.rnInstance = instance; 38 | } 39 | } -------------------------------------------------------------------------------- /Example/harmony_use_pushy/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/entry/obfuscation-rules.txt: -------------------------------------------------------------------------------- 1 | # Define project specific obfuscation rules here. 2 | # You can include the obfuscation configuration files in the current module's build-profile.json5. 3 | # 4 | # For more details, see 5 | # https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/source-obfuscation-V5 6 | 7 | # Obfuscation options: 8 | # -disable-obfuscation: disable all obfuscations 9 | # -enable-property-obfuscation: obfuscate the property names 10 | # -enable-toplevel-obfuscation: obfuscate the names in the global scope 11 | # -compact: remove unnecessary blank spaces and all line feeds 12 | # -remove-log: remove all console.* statements 13 | # -print-namecache: print the name cache that contains the mapping from the old names to new names 14 | # -apply-namecache: reuse the given cache file 15 | 16 | # Keep options: 17 | # -keep-property-name: specifies property names that you want to keep 18 | # -keep-global-name: specifies names that you want to keep in the global scope 19 | 20 | -enable-property-obfuscation 21 | -enable-toplevel-obfuscation 22 | -enable-filename-obfuscation 23 | -enable-export-obfuscation -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | android/build 3 | android/obj 4 | *.iml 5 | 6 | # OSX 7 | # 8 | .DS_Store 9 | 10 | # Xcode 11 | # 12 | build/ 13 | *.pbxuser 14 | !default.pbxuser 15 | *.mode1v3 16 | !default.mode1v3 17 | *.mode2v3 18 | !default.mode2v3 19 | *.perspectivev3 20 | !default.perspectivev3 21 | xcuserdata 22 | *.xccheckout 23 | *.moved-aside 24 | DerivedData 25 | *.hmap 26 | *.ipa 27 | *.xcuserstate 28 | project.xcworkspace 29 | 30 | # Android/IJ 31 | # 32 | .idea 33 | .gradle 34 | local.properties 35 | **/.project 36 | **/.settings 37 | **/.classpath 38 | 39 | # node.js 40 | # 41 | node_modules/ 42 | npm-debug.log 43 | 44 | Example/**/.update 45 | Example/**/.pushy 46 | Example/testHotUpdate/artifacts 47 | 48 | yarn-error.log 49 | Example/testHotUpdate/.yarn 50 | android/bin 51 | Example/testHotUpdate/harmony 52 | Example/testHotUpdate/android/app/.cxx 53 | Example/harmony_use_pushy/libs 54 | **/mcp.json 55 | 56 | 57 | harmony/package 58 | **/oh_modules 59 | harmony/pushy/.preview 60 | Example/harmony_use_pushy/harmony/entry/src/main/resources/rawfile/meta.json 61 | **/.hvigor 62 | Example/harmony_use_pushy/harmony/entry/src/main/cpp/generated 63 | -------------------------------------------------------------------------------- /Example/testHotUpdate/ios/AwesomeProject/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ios-marketing", 45 | "scale" : "1x", 46 | "size" : "1024x1024" 47 | } 48 | ], 49 | "info" : { 50 | "author" : "xcode", 51 | "version" : 1 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/ios/harmony_use_pushy/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ios-marketing", 45 | "scale" : "1x", 46 | "size" : "1024x1024" 47 | } 48 | ], 49 | "info" : { 50 | "author" : "xcode", 51 | "version" : 1 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Example/testHotUpdate/ios/AwesomeProject/PrivacyInfo.xcprivacy: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSPrivacyAccessedAPITypes 6 | 7 | 8 | NSPrivacyAccessedAPIType 9 | NSPrivacyAccessedAPICategoryFileTimestamp 10 | NSPrivacyAccessedAPITypeReasons 11 | 12 | C617.1 13 | 14 | 15 | 16 | NSPrivacyAccessedAPIType 17 | NSPrivacyAccessedAPICategoryUserDefaults 18 | NSPrivacyAccessedAPITypeReasons 19 | 20 | CA92.1 21 | 22 | 23 | 24 | NSPrivacyAccessedAPIType 25 | NSPrivacyAccessedAPICategorySystemBootTime 26 | NSPrivacyAccessedAPITypeReasons 27 | 28 | 35F9.1 29 | 30 | 31 | 32 | NSPrivacyCollectedDataTypes 33 | 34 | NSPrivacyTracking 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /Example/testHotUpdate/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Resolve react_native_pods.rb with node to allow for hoisting 2 | require Pod::Executable.execute_command('node', ['-p', 3 | 'require.resolve( 4 | "react-native/scripts/react_native_pods.rb", 5 | {paths: [process.argv[1]]}, 6 | )', __dir__]).strip 7 | 8 | platform :ios, min_ios_version_supported 9 | prepare_react_native_project! 10 | 11 | 12 | linkage = ENV['USE_FRAMEWORKS'] 13 | if linkage != nil 14 | Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green 15 | use_frameworks! :linkage => linkage.to_sym 16 | end 17 | 18 | target 'AwesomeProject' do 19 | config = use_native_modules! 20 | 21 | use_react_native!( 22 | :path => config[:reactNativePath], 23 | # An absolute path to your application root. 24 | :app_path => "#{Pod::Config.instance.installation_root}/.." 25 | ) 26 | 27 | post_install do |installer| 28 | # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202 29 | react_native_post_install( 30 | installer, 31 | config[:reactNativePath], 32 | :mac_catalyst_enabled => false, 33 | # :ccache_enabled => true 34 | ) 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Update Plugin for React Native 2 | 3 | Powered by ReactNative.cn 4 | 5 | Copyright (c) Wuhan Charmlot Network Technology Co., Ltd. 6 | 7 | All rights reserved. 8 | 9 | MIT License 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/entry/oh-package-lock.json5: -------------------------------------------------------------------------------- 1 | { 2 | "meta": { 3 | "stableOrder": true, 4 | "enableUnifiedLockfile": false 5 | }, 6 | "lockfileVersion": 3, 7 | "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.", 8 | "specifiers": { 9 | "@rnoh/react-native-openharmony@0.72.96": "@rnoh/react-native-openharmony@0.72.96", 10 | "pushy@../../node_modules/react-native-update/harmony/pushy": "pushy@../../node_modules/react-native-update/harmony/pushy" 11 | }, 12 | "packages": { 13 | "@rnoh/react-native-openharmony@0.72.96": { 14 | "name": "", 15 | "version": "0.72.96", 16 | "integrity": "sha512-gBbm8LLyqi5UE7qHWdZYeQnjyncfEpCczKZUP/9M2U1Z7exR0Kya8PMKMwr1ta5ujy7w/hZVC2LomEV4QvBeqA==", 17 | "resolved": "https://ohpm.openharmony.cn/ohpm/@rnoh/react-native-openharmony/-/react-native-openharmony-0.72.96.har", 18 | "registryType": "ohpm" 19 | }, 20 | "pushy@../../node_modules/react-native-update/harmony/pushy": { 21 | "name": "pushy", 22 | "version": "3.1.0-0.0.7", 23 | "resolved": "", 24 | "registryType": "local", 25 | "dependencies": { 26 | "@rnoh/react-native-openharmony": "^0.72.38" 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /Example/testHotUpdate/.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | ios/.xcode.env.local 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | *.hprof 33 | .kotlin/ 34 | 35 | # node.js 36 | # 37 | node_modules/ 38 | npm-debug.log 39 | yarn-error.log 40 | 41 | # BUCK 42 | buck-out/ 43 | \.buckd/ 44 | *.keystore 45 | !debug.keystore 46 | 47 | # fastlane 48 | # 49 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 50 | # screenshots whenever they are needed. 51 | # For more information about the recommended setup visit: 52 | # https://docs.fastlane.tools/best-practices/source-control/ 53 | 54 | **/fastlane/report.xml 55 | **/fastlane/Preview.html 56 | **/fastlane/screenshots 57 | **/fastlane/test_output 58 | 59 | # Bundle artifact 60 | *.jsbundle 61 | 62 | # Ruby / CocoaPods 63 | /ios/Pods/ 64 | /vendor/bundle/ 65 | 66 | # react-native-update 67 | .update 68 | .pushy -------------------------------------------------------------------------------- /harmony/pushy/src/main/cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | project(rnupdate) 3 | 4 | # Point to android/jni directory for shared source code 5 | set(ANDROID_JNI_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../../node_modules/react-native-update/android/jni) 6 | set(HDIFFPATCH_DIR ${ANDROID_JNI_DIR}/HDiffPatch) 7 | set(LZMA_DIR ${ANDROID_JNI_DIR}/lzma) 8 | set(HDP_SOURCES 9 | ${CMAKE_CURRENT_SOURCE_DIR}/pushy.c 10 | ${ANDROID_JNI_DIR}/hpatch.c 11 | ${HDIFFPATCH_DIR}/libHDiffPatch/HPatch/patch.c 12 | ${HDIFFPATCH_DIR}/file_for_patch.c 13 | ${LZMA_DIR}/C/LzmaDec.c 14 | ${LZMA_DIR}/C/Lzma2Dec.c 15 | ) 16 | set(CMAKE_VERBOSE_MAKEFILE on) 17 | 18 | 19 | add_library(rnupdate SHARED 20 | ${HDP_SOURCES} 21 | ) 22 | 23 | target_include_directories(rnupdate PRIVATE 24 | ${CMAKE_CURRENT_SOURCE_DIR} 25 | ${ANDROID_JNI_DIR} 26 | ${HDIFFPATCH_DIR} 27 | ${HDIFFPATCH_DIR}/libHDiffPatch/HPatch 28 | ${LZMA_DIR}/C 29 | ) 30 | 31 | target_link_libraries(rnupdate PUBLIC 32 | libace_napi.z.so 33 | ) 34 | 35 | file(GLOB rnoh_pushy_SRC CONFIGURE_DEPENDS *.cpp) 36 | add_library(rnoh_pushy SHARED ${rnoh_pushy_SRC}) 37 | target_include_directories(rnoh_pushy PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) 38 | target_link_libraries(rnoh_pushy PUBLIC rnoh) 39 | 40 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/android/app/src/main/java/com/harmony_use_pushy/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.harmony_use_pushy; 2 | 3 | import com.facebook.react.ReactActivity; 4 | import com.facebook.react.ReactActivityDelegate; 5 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint; 6 | import com.facebook.react.defaults.DefaultReactActivityDelegate; 7 | 8 | public class MainActivity extends ReactActivity { 9 | 10 | /** 11 | * Returns the name of the main component registered from JavaScript. This is used to schedule 12 | * rendering of the component. 13 | */ 14 | @Override 15 | protected String getMainComponentName() { 16 | return "harmony_use_pushy"; 17 | } 18 | 19 | /** 20 | * Returns the instance of the {@link ReactActivityDelegate}. Here we use a util class {@link 21 | * DefaultReactActivityDelegate} which allows you to easily enable Fabric and Concurrent React 22 | * (aka React 18) with two boolean flags. 23 | */ 24 | @Override 25 | protected ReactActivityDelegate createReactActivityDelegate() { 26 | return new DefaultReactActivityDelegate( 27 | this, 28 | getMainComponentName(), 29 | // If you opted-in for the New Architecture, we enable the Fabric Renderer. 30 | DefaultNewArchitectureEntryPoint.getFabricEnabled()); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: lint 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | push: 8 | branches: 9 | - master 10 | 11 | # Cancel a currently running workflow from the same PR/branch/tag 12 | # when a new workflow is triggered 13 | concurrency: 14 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} 15 | cancel-in-progress: true 16 | 17 | jobs: 18 | lint: 19 | runs-on: ubuntu-latest 20 | 21 | strategy: 22 | matrix: 23 | node-version: [20.x] 24 | 25 | steps: 26 | - uses: actions/checkout@v4 27 | - uses: oven-sh/setup-bun@v2 28 | - uses: actions/setup-node@v4 29 | with: 30 | node-version: '20.x' 31 | registry-url: 'https://registry.npmjs.org' 32 | 33 | - name: Get yarn cache directory path 34 | id: yarn-cache-dir-path 35 | run: echo "dir=$(yarn config get cacheFolder)" >> $GITHUB_OUTPUT 36 | 37 | - name: Install Dependency 38 | env: 39 | NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 40 | NODE_OPTIONS: '--max_old_space_size=4096' 41 | run: bun install --frozen-lockfile 42 | 43 | - name: Run lint 44 | env: 45 | NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 46 | NODE_OPTIONS: '--max_old_space_size=4096' 47 | run: bun lint 48 | -------------------------------------------------------------------------------- /harmony/pushy/hvigor-plugin.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import path from 'path'; 3 | 4 | export function reactNativeUpdatePlugin() { 5 | return { 6 | pluginId: 'reactNativeUpdatePlugin', 7 | apply(node) { 8 | node.registerTask({ 9 | name: 'reactNativeUpdatePlugin', 10 | run: () => { 11 | const cwd = process.cwd(); 12 | const metaFilePath = path.resolve( 13 | cwd, 14 | 'entry/src/main/resources/rawfile/meta.json', 15 | ); 16 | fs.mkdirSync(path.dirname(metaFilePath), { recursive: true }); 17 | 18 | const moduleJsonPath = path.resolve(cwd, 'AppScope/app.json5'); 19 | let versionName = ''; 20 | if (fs.existsSync(moduleJsonPath)) { 21 | const content = fs.readFileSync(moduleJsonPath, 'utf-8'); 22 | const match = content.match( 23 | /(?:"versionName"|versionName):\s*["']([^"']+)["']/, 24 | ); 25 | versionName = match?.[1] || ''; 26 | } 27 | 28 | const metaContent = { 29 | pushy_build_time: new Date().toISOString(), 30 | versionName, 31 | }; 32 | 33 | fs.writeFileSync(metaFilePath, JSON.stringify(metaContent, null, 2)); 34 | console.log(`Build time written to ${metaFilePath}`); 35 | }, 36 | }); 37 | }, 38 | }; 39 | } 40 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/entry/src/main/cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | project(rnapp) 3 | set(RNOH_APP_DIR "${CMAKE_CURRENT_SOURCE_DIR}") 4 | set(NODE_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../node_modules") 5 | set(OH_MODULE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules") 6 | set(RNOH_CPP_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules/@rnoh/react-native-openharmony/src/main/cpp") 7 | set(OH_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules") 8 | set(LOG_VERBOSITY_LEVEL 1) 9 | set(CMAKE_ASM_FLAGS "-Wno-error=unused-command-line-argument -Qunused-arguments") 10 | set(CMAKE_CXX_FLAGS "-fstack-protector-strong -Wl,-z,relro,-z,now,-z,noexecstack -s -fPIE -pie") 11 | set(OH_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules") 12 | set(WITH_HITRACE_SYSTRACE 1) # for other CMakeLists.txt files to use 13 | add_compile_definitions(WITH_HITRACE_SYSTRACE) 14 | 15 | add_subdirectory("${OH_MODULES}/pushy/src/main/cpp" ./pushy) 16 | add_subdirectory("${RNOH_CPP_DIR}" ./rn) 17 | 18 | file(GLOB GENERATED_CPP_FILES "${CMAKE_CURRENT_SOURCE_DIR}/generated/*.cpp") # this line is needed by codegen v1 19 | 20 | add_library(rnoh_app SHARED 21 | ${GENERATED_CPP_FILES} 22 | "./PackageProvider.cpp" 23 | "${RNOH_CPP_DIR}/RNOHAppNapiBridge.cpp" 24 | ) 25 | target_link_libraries(rnoh_app PUBLIC rnoh) 26 | target_link_libraries(rnoh_app PUBLIC rnoh_pushy) -------------------------------------------------------------------------------- /src/NativePushy.ts: -------------------------------------------------------------------------------- 1 | import { TurboModule, TurboModuleRegistry } from 'react-native'; 2 | 3 | export interface Spec extends TurboModule { 4 | getConstants: () => { 5 | downloadRootDir: string; 6 | packageVersion: string; 7 | currentVersion: string; 8 | isFirstTime: boolean; 9 | rolledBackVersion: string; 10 | buildTime: string; 11 | uuid: string; 12 | isUsingBundleUrl: boolean; 13 | currentVersionInfo: string; 14 | }; 15 | setLocalHashInfo(hash: string, info: string): Promise; 16 | getLocalHashInfo(hash: string): Promise; 17 | setUuid(uuid: string): Promise; 18 | reloadUpdate(options: { hash: string }): Promise; 19 | restartApp(): Promise; 20 | setNeedUpdate(options: { hash: string }): Promise; 21 | markSuccess(): Promise; 22 | downloadPatchFromPpk(options: { 23 | updateUrl: string; 24 | hash: string; 25 | originHash: string; 26 | }): Promise; 27 | downloadPatchFromPackage(options: { 28 | updateUrl: string; 29 | hash: string; 30 | }): Promise; 31 | downloadFullUpdate(options: { 32 | updateUrl: string; 33 | hash: string; 34 | }): Promise; 35 | downloadAndInstallApk(options: { 36 | url: string; 37 | target: string; 38 | hash: string; 39 | }): Promise; 40 | addListener(eventName: string): void; 41 | removeListeners(count: number): void; 42 | } 43 | 44 | export default TurboModuleRegistry.get('Pushy') as Spec | null; 45 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/hvigor/hvigor-config.json5: -------------------------------------------------------------------------------- 1 | { 2 | "modelVersion": "5.0.0", 3 | "dependencies": { 4 | pushy: 'file:../../node_modules/react-native-update/harmony/pushy' 5 | }, 6 | "execution": { 7 | // "analyze": "normal", /* Define the build analyze mode. Value: [ "normal" | "advanced" | false ]. Default: "normal" */ 8 | // "daemon": true, /* Enable daemon compilation. Value: [ true | false ]. Default: true */ 9 | // "incremental": true, /* Enable incremental compilation. Value: [ true | false ]. Default: true */ 10 | // "parallel": true, /* Enable parallel compilation. Value: [ true | false ]. Default: true */ 11 | // "typeCheck": false, /* Enable typeCheck. Value: [ true | false ]. Default: false */ 12 | }, 13 | "logging": { 14 | // "level": "info" /* Define the log level. Value: [ "debug" | "info" | "warn" | "error" ]. Default: "info" */ 15 | }, 16 | "debugging": { 17 | // "stacktrace": false /* Disable stacktrace compilation. Value: [ true | false ]. Default: false */ 18 | }, 19 | "nodeOptions": { 20 | // "maxOldSpaceSize": 8192 /* Enable nodeOptions maxOldSpaceSize compilation. Unit M. Used for the daemon process. Default: 8192*/ 21 | // "exposeGC": true /* Enable to trigger garbage collection explicitly. Default: true*/ 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /harmony/pushy/src/main/ets/PushyFileJSBundleProvider.ets: -------------------------------------------------------------------------------- 1 | import { 2 | FileJSBundle, 3 | HotReloadConfig, 4 | JSBundleProvider, 5 | JSBundleProviderError 6 | } from '@rnoh/react-native-openharmony'; 7 | import common from '@ohos.app.ability.common'; 8 | import fs from '@ohos.file.fs'; 9 | import { UpdateContext } from './UpdateContext'; 10 | 11 | export class PushyFileJSBundleProvider extends JSBundleProvider { 12 | private updateContext: UpdateContext; 13 | private path: string = '' 14 | 15 | constructor(context: common.UIAbilityContext) { 16 | super(); 17 | this.updateContext = new UpdateContext(context); 18 | this.path = this.updateContext.getBundleUrl(); 19 | } 20 | 21 | getURL(): string { 22 | return this.path; 23 | } 24 | 25 | async getBundle(): Promise { 26 | if (!this.path) { 27 | throw new JSBundleProviderError({ 28 | whatHappened: 'No pushy bundle found. using default bundle', 29 | howCanItBeFixed: [''] 30 | }) 31 | } 32 | try { 33 | await fs.access(this.path, fs.OpenMode.READ_ONLY); 34 | return { 35 | filePath: this.path 36 | } 37 | } catch (error) { 38 | throw new JSBundleProviderError({ 39 | whatHappened: `Couldn't load JSBundle from ${this.path}`, 40 | extraData: error, 41 | howCanItBeFixed: [`Check if a bundle exists at "${this.path}" on your device.`] 42 | }) 43 | } 44 | } 45 | 46 | getAppKeys(): string[] { 47 | return []; 48 | } 49 | } -------------------------------------------------------------------------------- /.github/workflows/e2e_android.yml: -------------------------------------------------------------------------------- 1 | name: e2e-android 2 | on: push 3 | 4 | jobs: 5 | e2e-android: 6 | runs-on: macos-latest 7 | steps: 8 | - name: Checkout repository 9 | uses: actions/checkout@v3 10 | 11 | - name: Setup Node.js 12 | uses: actions/setup-node@v3 13 | with: 14 | cache: yarn 15 | node-version-file: .nvmrc 16 | 17 | - name: Install Yarn dependencies 18 | run: yarn --frozen-lockfile --prefer-offline 19 | 20 | - name: Setup Java 21 | uses: actions/setup-java@v3 22 | with: 23 | cache: gradle 24 | distribution: temurin 25 | java-version: 17 26 | 27 | - name: Cache Detox build 28 | id: cache-detox-build 29 | uses: actions/cache@v3 30 | with: 31 | path: android/app/build 32 | key: ${{ runner.os }}-detox-build 33 | restore-keys: | 34 | ${{ runner.os }}-detox-build 35 | 36 | - name: Detox build 37 | run: yarn build:android-debug 38 | 39 | - name: Get device name 40 | id: device 41 | run: node -e "console.log('AVD_NAME=' + require('./Example/testHotUpdate/.detoxrc').devices.emulator.device.avdName)" >> $GITHUB_OUTPUT 42 | 43 | - name: Detox test 44 | uses: reactivecircus/android-emulator-runner@v2 45 | with: 46 | api-level: 31 47 | arch: x86_64 48 | avd-name: ${{ steps.device.outputs.AVD_NAME }} 49 | script: yarn test:android-debug 50 | -------------------------------------------------------------------------------- /Example/testHotUpdate/README.md: -------------------------------------------------------------------------------- 1 | # react-native-android-detox 2 | 3 | [![e2e-android](https://github.com/remarkablemark/react-native-android-detox/actions/workflows/e2e-android.yml/badge.svg)](https://github.com/remarkablemark/react-native-android-detox/actions/workflows/e2e-android.yml) 4 | 5 | React Native Android Detox. The project has already been patched with the [additional Android configuration](https://wix.github.io/Detox/docs/introduction/project-setup/). 6 | 7 | ## Prerequisites 8 | 9 | Follow the [environment setup](https://wix.github.io/Detox/docs/introduction/getting-started). 10 | 11 | ## Install 12 | 13 | Clone the repository: 14 | 15 | ```sh 16 | git clone https://github.com/remarkablemark/react-native-android-detox.git 17 | cd react-native-android-detox 18 | ``` 19 | 20 | Install the dependencies: 21 | 22 | ```sh 23 | yarn 24 | ``` 25 | 26 | ## Build 27 | 28 | ### Android (Debug) 29 | 30 | Build the Android debug app: 31 | 32 | ```sh 33 | yarn detox build --configuration android.emu.debug 34 | ``` 35 | 36 | ### Android (Release) 37 | 38 | Build the Android release app: 39 | 40 | ```sh 41 | yarn detox build --configuration android.emu.release 42 | ``` 43 | 44 | ## Test 45 | 46 | ### Android (Debug) 47 | 48 | Start the app: 49 | 50 | ```sh 51 | yarn start 52 | ``` 53 | 54 | Run the test: 55 | 56 | ```sh 57 | yarn detox test --configuration android.emu.debug 58 | ``` 59 | 60 | ### Android (Release) 61 | 62 | Run the test: 63 | 64 | ```sh 65 | yarn detox test --configuration android.emu.release 66 | ``` 67 | -------------------------------------------------------------------------------- /Example/testHotUpdate/android/app/_BUCK: -------------------------------------------------------------------------------- 1 | # To learn about Buck see [Docs](https://buckbuild.com/). 2 | # To run your application with Buck: 3 | # - install Buck 4 | # - `npm start` - to start the packager 5 | # - `cd android` 6 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"` 7 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck 8 | # - `buck install -r android/app` - compile, install and run application 9 | # 10 | 11 | load(":build_defs.bzl", "create_aar_targets", "create_jar_targets") 12 | 13 | lib_deps = [] 14 | 15 | create_aar_targets(glob(["libs/*.aar"])) 16 | 17 | create_jar_targets(glob(["libs/*.jar"])) 18 | 19 | android_library( 20 | name = "all-libs", 21 | exported_deps = lib_deps, 22 | ) 23 | 24 | android_library( 25 | name = "app-code", 26 | srcs = glob([ 27 | "src/main/java/**/*.java", 28 | ]), 29 | deps = [ 30 | ":all-libs", 31 | ":build_config", 32 | ":res", 33 | ], 34 | ) 35 | 36 | android_build_config( 37 | name = "build_config", 38 | package = "com.awesomeproject", 39 | ) 40 | 41 | android_resource( 42 | name = "res", 43 | package = "com.awesomeproject", 44 | res = "src/main/res", 45 | ) 46 | 47 | android_binary( 48 | name = "app", 49 | keystore = "//android/keystores:debug", 50 | manifest = "src/main/AndroidManifest.xml", 51 | package_type = "debug", 52 | deps = [ 53 | ":app-code", 54 | ], 55 | ) 56 | -------------------------------------------------------------------------------- /.github/workflows/scripts/start-firebase-emulator.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if ! [ -x "$(command -v firebase)" ]; then 3 | echo "❌ Firebase-tools CLI is missing. Run 'npm i -g firebase-tools' or the equivalent" 4 | exit 1 5 | fi 6 | 7 | EMU_START_COMMAND="firebase emulators:start --only auth,database,firestore,functions,storage --project react-native-firebase-testing" 8 | #EMU_START_COMMAND="sleep 120" 9 | MAX_RETRIES=3 10 | MAX_CHECKATTEMPTS=60 11 | CHECKATTEMPTS_WAIT=1 12 | 13 | # Make sure functions are ready to go 14 | pushd "$(dirname "$0")/functions" && yarn && yarn build && popd 15 | 16 | 17 | RETRIES=1 18 | while [ $RETRIES -le $MAX_RETRIES ]; do 19 | 20 | if [ "$1" == "--no-daemon" ]; then 21 | echo "Starting Firebase Emulator Suite in foreground." 22 | $EMU_START_COMMAND 23 | exit 0 24 | else 25 | echo "Starting Firebase Emulator Suite in background." 26 | $EMU_START_COMMAND & 27 | CHECKATTEMPTS=1 28 | while [ $CHECKATTEMPTS -le $MAX_CHECKATTEMPTS ]; do 29 | sleep $CHECKATTEMPTS_WAIT 30 | if curl --output /dev/null --silent --fail http://localhost:8080; then 31 | echo "Firebase Emulator Suite is online!" 32 | exit 0; 33 | fi 34 | echo "Waiting for Firebase Emulator Suite to come online, check $CHECKATTEMPTS of $MAX_CHECKATTEMPTS..." 35 | ((CHECKATTEMPTS = CHECKATTEMPTS + 1)) 36 | done 37 | fi 38 | 39 | echo "Firebase Emulator Suite did not come online in $MAX_CHECKATTEMPTS checks. Try $RETRIES of $MAX_RETRIES." 40 | ((RETRIES = RETRIES + 1)) 41 | 42 | done 43 | echo "Firebase Emulator Suite did not come online after $MAX_RETRIES attempts." 44 | exit 1 45 | -------------------------------------------------------------------------------- /Example/testHotUpdate/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 15 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /android/jni/hpatch.h: -------------------------------------------------------------------------------- 1 | // hpatch.h 2 | // import HDiffPatch, support patchData created by "hdiffz -SD -c-lzma2 oldfile newfile patchfile" 3 | // Copyright 2021 housisong, All rights reserved 4 | 5 | #ifndef HDIFFPATCH_PATCH_H 6 | #define HDIFFPATCH_PATCH_H 7 | # include //for uint8_t 8 | #include "HDiffPatch/libHDiffPatch/HPatch/patch_types.h" //for hpatch_singleCompressedDiffInfo 9 | #ifdef __cplusplus 10 | extern "C" { 11 | #endif 12 | 13 | //result 14 | enum { 15 | kHPatch_ok = 0, 16 | kHPatch_error_malloc =-1, 17 | kHPatch_error_info =-2, 18 | kHPatch_error_compressType =-3, 19 | kHPatch_error_patch =-4, 20 | kHPatch_error_old_fopen =-5, 21 | kHPatch_error_old_fread =-6, 22 | kHPatch_error_old_fclose =-7, 23 | kHPatch_error_pat_fopen =-8, 24 | kHPatch_error_pat_fread =-9, 25 | kHPatch_error_pat_fclose =-10, 26 | kHPatch_error_new_fopen =-11, 27 | kHPatch_error_new_fwrite =-12, 28 | kHPatch_error_new_fclose =-13, 29 | kHPatch_error_old_size =-14, 30 | kHPatch_error_new_size =-15, 31 | }; 32 | 33 | int hpatch_getInfo_by_mem(hpatch_singleCompressedDiffInfo* out_patinfo, 34 | const uint8_t* pat,size_t patsize); 35 | 36 | //patInfo can NULL 37 | int hpatch_by_mem(const uint8_t* old,size_t oldsize, uint8_t* newBuf,size_t newsize, 38 | const uint8_t* pat,size_t patsize,const hpatch_singleCompressedDiffInfo* patInfo); 39 | int hpatch_by_file(const char* oldfile, const char* newfile, const char* patchfile); 40 | 41 | #ifdef __cplusplus 42 | } 43 | #endif 44 | #endif //HDIFFPATCH_PATCH_H 45 | -------------------------------------------------------------------------------- /harmony/pushy/src/main/cpp/PushyTurboModule.h: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (C) 2023 Huawei Device Co., Ltd. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef GEO_LOCATION_TURBOMODULE_H 26 | #define GEO_LOCATION_TURBOMODULE_H 27 | 28 | #include 29 | #include "RNOH/ArkTSTurboModule.h" 30 | 31 | namespace rnoh { 32 | class JSI_EXPORT PushyTurboModule : public ArkTSTurboModule { 33 | public: 34 | PushyTurboModule(const ArkTSTurboModule::Context ctx, const std::string name); 35 | }; 36 | } // namespace rnoh 37 | 38 | #endif -------------------------------------------------------------------------------- /Example/expoUsePushy/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "expousepushy", 3 | "main": "index.js", 4 | "version": "1.0.0", 5 | "scripts": { 6 | "start": "expo start", 7 | "reset-project": "node ./scripts/reset-project.js", 8 | "android": "expo run:android", 9 | "ios": "expo run:ios", 10 | "web": "expo start --web", 11 | "lint": "expo lint" 12 | }, 13 | "dependencies": { 14 | "@expo/vector-icons": "^14.1.0", 15 | "@react-navigation/bottom-tabs": "^7.3.10", 16 | "@react-navigation/elements": "^2.3.8", 17 | "@react-navigation/native": "^7.1.6", 18 | "expo": "~53.0.22", 19 | "expo-blur": "~14.1.5", 20 | "expo-constants": "~17.1.7", 21 | "expo-font": "~13.3.2", 22 | "expo-haptics": "~14.1.4", 23 | "expo-image": "~2.4.0", 24 | "expo-linking": "~7.1.7", 25 | "expo-router": "~5.1.5", 26 | "expo-splash-screen": "~0.30.10", 27 | "expo-status-bar": "~2.2.3", 28 | "expo-symbols": "~0.4.5", 29 | "expo-system-ui": "~5.0.11", 30 | "expo-web-browser": "~14.2.0", 31 | "react": "19.0.0", 32 | "react-dom": "19.0.0", 33 | "react-native": "0.79.6", 34 | "react-native-gesture-handler": "~2.24.0", 35 | "react-native-reanimated": "~3.17.4", 36 | "react-native-safe-area-context": "5.4.0", 37 | "react-native-screens": "~4.11.1", 38 | "react-native-update": "^10.34.4", 39 | "react-native-web": "~0.20.0", 40 | "react-native-webview": "13.13.5" 41 | }, 42 | "devDependencies": { 43 | "@babel/core": "^7.25.2", 44 | "@types/react": "~19.0.10", 45 | "typescript": "~5.8.3", 46 | "eslint": "^9.25.0", 47 | "eslint-config-expo": "~9.2.0" 48 | }, 49 | "private": true 50 | } 51 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/entry/src/test/LocalUnit.test.ets: -------------------------------------------------------------------------------- 1 | import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'; 2 | 3 | export default function localUnitTest() { 4 | describe('localUnitTest', () => { 5 | // Defines a test suite. Two parameters are supported: test suite name and test suite function. 6 | beforeAll(() => { 7 | // Presets an action, which is performed only once before all test cases of the test suite start. 8 | // This API supports only one parameter: preset action function. 9 | }); 10 | beforeEach(() => { 11 | // Presets an action, which is performed before each unit test case starts. 12 | // The number of execution times is the same as the number of test cases defined by **it**. 13 | // This API supports only one parameter: preset action function. 14 | }); 15 | afterEach(() => { 16 | // Presets a clear action, which is performed after each unit test case ends. 17 | // The number of execution times is the same as the number of test cases defined by **it**. 18 | // This API supports only one parameter: clear action function. 19 | }); 20 | afterAll(() => { 21 | // Presets a clear action, which is performed after all test cases of the test suite end. 22 | // This API supports only one parameter: clear action function. 23 | }); 24 | it('assertContain', 0, () => { 25 | // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. 26 | let a = 'abc'; 27 | let b = 'b'; 28 | // Defines a variety of assertion methods, which are used to declare expected boolean conditions. 29 | expect(a).assertContain(b); 30 | expect(a).assertEqual(a); 31 | }); 32 | }); 33 | } -------------------------------------------------------------------------------- /.github/workflows/scripts/firestore.indexes.json: -------------------------------------------------------------------------------- 1 | { 2 | "indexes": [ 3 | { 4 | "collectionGroup": "firestore", 5 | "queryScope": "COLLECTION", 6 | "fields": [ 7 | { 8 | "fieldPath": "a", 9 | "order": "ASCENDING" 10 | }, 11 | { 12 | "fieldPath": "b", 13 | "order": "ASCENDING" 14 | } 15 | ] 16 | } 17 | ], 18 | "fieldOverrides": [ 19 | { 20 | "collectionGroup": "collectionGroup", 21 | "fieldPath": "value", 22 | "indexes": [ 23 | { 24 | "order": "ASCENDING", 25 | "queryScope": "COLLECTION" 26 | }, 27 | { 28 | "order": "DESCENDING", 29 | "queryScope": "COLLECTION" 30 | }, 31 | { 32 | "arrayConfig": "CONTAINS", 33 | "queryScope": "COLLECTION" 34 | }, 35 | { 36 | "order": "ASCENDING", 37 | "queryScope": "COLLECTION_GROUP" 38 | }, 39 | { 40 | "order": "DESCENDING", 41 | "queryScope": "COLLECTION_GROUP" 42 | } 43 | ] 44 | }, 45 | { 46 | "collectionGroup": "collectionGroup", 47 | "fieldPath": "number", 48 | "indexes": [ 49 | { 50 | "order": "ASCENDING", 51 | "queryScope": "COLLECTION" 52 | }, 53 | { 54 | "order": "DESCENDING", 55 | "queryScope": "COLLECTION" 56 | }, 57 | { 58 | "arrayConfig": "CONTAINS", 59 | "queryScope": "COLLECTION" 60 | }, 61 | { 62 | "order": "ASCENDING", 63 | "queryScope": "COLLECTION_GROUP" 64 | }, 65 | { 66 | "order": "DESCENDING", 67 | "queryScope": "COLLECTION_GROUP" 68 | } 69 | ] 70 | } 71 | ] 72 | } 73 | -------------------------------------------------------------------------------- /.github/workflows/scripts/functions/src/sample-data.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Testing tools for invertase/react-native-firebase use only. 3 | * 4 | * Copyright (C) 2018-present Invertase Limited 5 | * 6 | * See License file for more information. 7 | */ 8 | 9 | const SAMPLE_DATA: { [key: string]: any } = { 10 | number: 1234, 11 | string: 'acde', 12 | boolean: true, 13 | null: null, 14 | object: { 15 | number: 1234, 16 | string: 'acde', 17 | boolean: true, 18 | null: null, 19 | }, 20 | array: [1234, 'acde', true, null], 21 | deepObject: { 22 | array: [1234, 'acde', false, null], 23 | object: { 24 | number: 1234, 25 | string: 'acde', 26 | boolean: true, 27 | null: null, 28 | array: [1234, 'acde', true, null], 29 | }, 30 | number: 1234, 31 | string: 'acde', 32 | boolean: true, 33 | null: null, 34 | }, 35 | deepArray: [ 36 | 1234, 37 | 'acde', 38 | true, 39 | null, 40 | [1234, 'acde', true, null], 41 | { 42 | number: 1234, 43 | string: 'acde', 44 | boolean: true, 45 | null: null, 46 | array: [1234, 'acde', true, null], 47 | }, 48 | ], 49 | deepMap: { 50 | number: 123, 51 | string: 'foo', 52 | booleanTrue: true, 53 | booleanFalse: false, 54 | null: null, 55 | list: ['1', 2, true, false], 56 | map: { 57 | number: 123, 58 | string: 'foo', 59 | booleanTrue: true, 60 | booleanFalse: false, 61 | null: null, 62 | }, 63 | }, 64 | deepList: [ 65 | '1', 66 | 2, 67 | true, 68 | false, 69 | ['1', 2, true, false], 70 | { 71 | number: 123, 72 | string: 'foo', 73 | booleanTrue: true, 74 | booleanFalse: false, 75 | null: null, 76 | }, 77 | ], 78 | }; 79 | 80 | export default SAMPLE_DATA; 81 | -------------------------------------------------------------------------------- /Example/testHotUpdate/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "testHotUpdate", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "android": "react-native run-android", 7 | "ios": "react-native run-ios", 8 | "start": "react-native start", 9 | "test": "jest", 10 | "test:e2e": "detox test --configuration android.emu.debug", 11 | "lint": "eslint .", 12 | "postinstall": "patch-package", 13 | "apk": "cd android && ./gradlew assembleRelease" 14 | }, 15 | "dependencies": { 16 | "form-data": "^4.0.5", 17 | "patch-package": "^8.0.1", 18 | "react": "19.1.1", 19 | "react-native": "0.82.1", 20 | "react-native-camera-kit": "^16.1.3", 21 | "react-native-paper": "^5.14.5", 22 | "react-native-safe-area-context": "^5.6.2", 23 | "react-native-svg": "^15.15.0", 24 | "react-native-update": "^10.36.2", 25 | "react-native-vector-icons": "^10.3.0" 26 | }, 27 | "devDependencies": { 28 | "@babel/core": "^7.27.3", 29 | "@babel/preset-env": "^7.27.2", 30 | "@babel/runtime": "^7.27.3", 31 | "@react-native-community/cli": "20.0.0", 32 | "@react-native-community/cli-platform-android": "20.0.0", 33 | "@react-native-community/cli-platform-ios": "20.0.0", 34 | "@react-native/babel-preset": "0.82.1", 35 | "@react-native/eslint-config": "0.82.1", 36 | "@react-native/metro-config": "0.82.1", 37 | "@react-native/typescript-config": "0.82.1", 38 | "@types/react": "^19.1.13", 39 | "@types/react-test-renderer": "^19.1.0", 40 | "detox": "^20.41.2", 41 | "eslint": "^9.35.0", 42 | "jest": "^29.6.3", 43 | "prettier": "2.8.8", 44 | "react-test-renderer": "19.1.1", 45 | "typescript": "5.9.3" 46 | }, 47 | "engines": { 48 | "node": ">=20" 49 | }, 50 | "trustedDependencies": [ 51 | "detox", 52 | "dtrace-provider" 53 | ] 54 | } 55 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "harmony_use_pushy", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "android": "react-native run-android", 7 | "ios": "react-native run-ios", 8 | "lint": "eslint .", 9 | "start": "npm run codegen && hdc rport tcp:8081 tcp:8081 && react-native start", 10 | "codegen": "react-native codegen-harmony --rnoh-module-path ./harmony/react_native_openharmony", 11 | "build": "pushy bundle --platform harmony --no-interactive", 12 | "test": "jest", 13 | "hdiffFromPPK": "pushy hdiffFromPPK .pushy/output/harmony.1735052610653.ppk .pushy/output/harmony.1735052678646.ppk .pushy/output/hdiff.ppk-patch", 14 | "hdiffFromApp": "pushy hdiffFromApp .pushy/output/version-1.0.0.app .pushy/output/harmony.1735052610653.ppk .pushy/output/hdiff.app-patch", 15 | "hash": "pushy hash /Users/yanbo.he/Desktop/HarmonyOS/react-native-pushy/Example/harmony_use_pushy/.pushy/output/harmony.1735048297258.ppk" 16 | }, 17 | "dependencies": { 18 | "@react-native-oh/react-native-harmony": "^0.72.59", 19 | "react": "18.2.0", 20 | "react-native": "0.72.5", 21 | "react-native-update": "^10.35.4" 22 | }, 23 | "devDependencies": { 24 | "@babel/core": "^7.20.0", 25 | "@babel/preset-env": "^7.20.0", 26 | "@babel/runtime": "^7.20.0", 27 | "@react-native/eslint-config": "^0.72.2", 28 | "@react-native/metro-config": "^0.72.11", 29 | "@tsconfig/react-native": "^3.0.0", 30 | "@types/react": "^18.0.24", 31 | "@types/react-test-renderer": "^18.0.0", 32 | "babel-jest": "^29.2.1", 33 | "eslint": "^8.19.0", 34 | "jest": "^29.2.1", 35 | "metro-react-native-babel-preset": "0.76.8", 36 | "prettier": "^2.4.1", 37 | "react-test-renderer": "18.2.0", 38 | "typescript": "4.8.4" 39 | }, 40 | "engines": { 41 | "node": ">=16" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/ios/harmony_use_pushy/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | harmony_use_pushy 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(MARKETING_VERSION) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(CURRENT_PROJECT_VERSION) 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | NSExceptionDomains 30 | 31 | localhost 32 | 33 | NSExceptionAllowsInsecureHTTPLoads 34 | 35 | 36 | 37 | 38 | NSLocationWhenInUseUsageDescription 39 | 40 | UILaunchStoryboardName 41 | LaunchScreen 42 | UIRequiredDeviceCapabilities 43 | 44 | armv7 45 | 46 | UISupportedInterfaceOrientations 47 | 48 | UIInterfaceOrientationPortrait 49 | UIInterfaceOrientationLandscapeLeft 50 | UIInterfaceOrientationLandscapeRight 51 | 52 | UIViewControllerBasedStatusBarAppearance 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/entry/src/ohosTest/ets/test/Ability.test.ets: -------------------------------------------------------------------------------- 1 | import { hilog } from '@kit.PerformanceAnalysisKit'; 2 | import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'; 3 | 4 | export default function abilityTest() { 5 | describe('ActsAbilityTest', () => { 6 | // Defines a test suite. Two parameters are supported: test suite name and test suite function. 7 | beforeAll(() => { 8 | // Presets an action, which is performed only once before all test cases of the test suite start. 9 | // This API supports only one parameter: preset action function. 10 | }) 11 | beforeEach(() => { 12 | // Presets an action, which is performed before each unit test case starts. 13 | // The number of execution times is the same as the number of test cases defined by **it**. 14 | // This API supports only one parameter: preset action function. 15 | }) 16 | afterEach(() => { 17 | // Presets a clear action, which is performed after each unit test case ends. 18 | // The number of execution times is the same as the number of test cases defined by **it**. 19 | // This API supports only one parameter: clear action function. 20 | }) 21 | afterAll(() => { 22 | // Presets a clear action, which is performed after all test cases of the test suite end. 23 | // This API supports only one parameter: clear action function. 24 | }) 25 | it('assertContain', 0, () => { 26 | // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. 27 | hilog.info(0x0000, 'testTag', '%{public}s', 'it begin'); 28 | let a = 'abc'; 29 | let b = 'b'; 30 | // Defines a variety of assertion methods, which are used to declare expected boolean conditions. 31 | expect(a).assertContain(b); 32 | expect(a).assertEqual(a); 33 | }) 34 | }) 35 | } -------------------------------------------------------------------------------- /.github/workflows/scripts/functions/src/testFunctionDefaultRegion.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Testing tools for invertase/react-native-firebase use only. 4 | * 5 | * Copyright (C) 2018-present Invertase Limited 6 | * 7 | * See License file for more information. 8 | */ 9 | 10 | import * as assert from 'assert'; 11 | import { FirebaseError } from 'firebase-admin'; 12 | import * as functions from 'firebase-functions'; 13 | import SAMPLE_DATA from './sample-data'; 14 | 15 | export const testFunctionDefaultRegion = functions.https.onCall(data => { 16 | console.log(Date.now(), data); 17 | 18 | if (typeof data === 'undefined') { 19 | return 'undefined'; 20 | } 21 | 22 | if (typeof data === 'string') { 23 | return 'string'; 24 | } 25 | 26 | if (typeof data === 'number') { 27 | return 'number'; 28 | } 29 | 30 | if (typeof data === 'boolean') { 31 | return 'boolean'; 32 | } 33 | 34 | if (data === null) { 35 | return 'null'; 36 | } 37 | 38 | if (Array.isArray(data)) { 39 | return 'array'; 40 | } 41 | 42 | const { type, asError, inputData } = data; 43 | if (!Object.hasOwnProperty.call(SAMPLE_DATA, type)) { 44 | throw new functions.https.HttpsError( 45 | 'invalid-argument', 46 | 'Invalid test requested.', 47 | ); 48 | } 49 | 50 | const outputData = SAMPLE_DATA[type]; 51 | 52 | try { 53 | assert.deepEqual(outputData, inputData); 54 | } catch (e: any) { 55 | console.error(e); 56 | throw new functions.https.HttpsError( 57 | 'invalid-argument', 58 | 'Input and Output types did not match.', 59 | (e as FirebaseError).message, 60 | ); 61 | } 62 | 63 | // all good 64 | if (asError) { 65 | throw new functions.https.HttpsError( 66 | 'cancelled', 67 | 'Response data was requested to be sent as part of an Error payload, so here we are!', 68 | outputData, 69 | ); 70 | } 71 | 72 | return outputData; 73 | }); 74 | -------------------------------------------------------------------------------- /ios/Expo/ExpoPushyReactDelegateHandler.swift: -------------------------------------------------------------------------------- 1 | import ExpoModulesCore 2 | import React 3 | 4 | public final class ExpoPushyReactDelegateHandler: ExpoReactDelegateHandler { 5 | 6 | #if EXPO_SUPPORTS_BUNDLEURL 7 | // This code block compiles only if EXPO_SUPPORTS_BUNDLEURL is defined 8 | // For expo-modules-core >= 1.12.0 9 | 10 | // Override bundleURL, which is the primary mechanism for these versions. 11 | // Expo's default createBridge implementation should respect this. 12 | override public func bundleURL(reactDelegate: ExpoReactDelegate) -> URL? { 13 | let bundleURL = RCTPushy.bundleURL() 14 | print("PushyHandler: Using bundleURL: \(bundleURL?.absoluteString ?? "nil")") 15 | return bundleURL 16 | } 17 | 18 | // No createBridge override needed here, rely on default behavior using the bundleURL override. 19 | 20 | #else 21 | // This code block compiles only if EXPO_SUPPORTS_BUNDLEURL is NOT defined 22 | // For expo-modules-core < 1.12.0 23 | 24 | // No bundleURL override possible here. 25 | 26 | // createBridge is the mechanism to customize the URL here. 27 | // We completely override it and do not call super. 28 | override public func createBridge(reactDelegate: ExpoReactDelegate, bridgeDelegate: RCTBridgeDelegate, launchOptions: [AnyHashable: Any]?) -> RCTBridge? { 29 | let bundleURL = RCTPushy.bundleURL() 30 | // Print the URL being provided to the initializer 31 | print("PushyHandler: createBridge bundleURL: \(bundleURL?.absoluteString ?? "nil")") 32 | 33 | // Directly create the bridge using the bundleURL initializer. 34 | // Pass nil for moduleProvider, assuming default behavior is sufficient. 35 | // WARNING: If bundleURL is nil, this initialization might fail silently or crash. 36 | return RCTBridge(bundleURL: bundleURL, moduleProvider: nil, launchOptions: launchOptions) 37 | } 38 | 39 | #endif // EXPO_SUPPORTS_BUNDLEURL 40 | } 41 | -------------------------------------------------------------------------------- /Example/testHotUpdate/android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx512m -XX:MaxMetaspaceSize=256m 13 | org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | # AndroidX package structure to make it clearer which packages are bundled with the 21 | # Android operating system, and which are packaged with your app's APK 22 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 23 | android.useAndroidX=true 24 | 25 | # Use this property to specify which architecture you want to build. 26 | # You can also override it from the CLI using 27 | # ./gradlew -PreactNativeArchitectures=x86_64 28 | # reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 29 | reactNativeArchitectures=arm64-v8a 30 | 31 | # Use this property to enable support to the new architecture. 32 | # This will allow you to use TurboModules and the Fabric render in 33 | # your application. You should enable this flag either if you want 34 | # to write custom TurboModules/Fabric components OR use libraries that 35 | # are providing them. 36 | newArchEnabled=true 37 | 38 | # Use this property to enable or disable the Hermes JS engine. 39 | # If set to false, you will be using JSC instead. 40 | hermesEnabled=true 41 | -------------------------------------------------------------------------------- /android/src/main/java/cn/reactnative/modules/update/UpdatePackage.java: -------------------------------------------------------------------------------- 1 | package cn.reactnative.modules.update; 2 | 3 | import androidx.annotation.Nullable; 4 | import com.facebook.react.TurboReactPackage; 5 | import com.facebook.react.bridge.NativeModule; 6 | import com.facebook.react.bridge.ReactApplicationContext; 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; 11 | 12 | public class UpdatePackage extends TurboReactPackage { 13 | @Nullable 14 | @Override 15 | public NativeModule getModule(String name, ReactApplicationContext reactContext) { 16 | if (name.equals(UpdateModuleImpl.NAME)) { 17 | return new UpdateModule(reactContext); 18 | } else { 19 | return null; 20 | } 21 | } 22 | 23 | @Override 24 | public ReactModuleInfoProvider getReactModuleInfoProvider() { 25 | return new ReactModuleInfoProvider() { 26 | @Override 27 | public Map getReactModuleInfos() { 28 | final Map moduleInfos = new HashMap<>(); 29 | boolean isTurboModule = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED; 30 | moduleInfos.put( 31 | UpdateModuleImpl.NAME, 32 | new ReactModuleInfo( 33 | UpdateModuleImpl.NAME, 34 | UpdateModuleImpl.NAME, 35 | false, // canOverrideExistingModule 36 | false, // needsEagerInit 37 | true, // hasConstants 38 | false, // isCxxModule 39 | isTurboModule // isTurboModule 40 | )); 41 | return moduleInfos; 42 | } 43 | }; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/entry/src/main/module.json5: -------------------------------------------------------------------------------- 1 | { 2 | "module": { 3 | "name": "entry", 4 | "type": "entry", 5 | "description": "$string:module_desc", 6 | "mainElement": "EntryAbility", 7 | "deviceTypes": [ 8 | "phone", 9 | "tablet", 10 | "2in1" 11 | ], 12 | "deliveryWithInstall": true, 13 | "installationFree": false, 14 | "pages": "$profile:main_pages", 15 | "abilities": [ 16 | { 17 | "name": "EntryAbility", 18 | "srcEntry": "./ets/entryability/EntryAbility.ets", 19 | "description": "$string:EntryAbility_desc", 20 | "icon": "$media:layered_image", 21 | "label": "$string:EntryAbility_label", 22 | "startWindowIcon": "$media:startIcon", 23 | "startWindowBackground": "$color:start_window_background", 24 | "exported": true, 25 | "skills": [ 26 | { 27 | "entities": [ 28 | "entity.system.home" 29 | ], 30 | "actions": [ 31 | "action.system.home" 32 | ] 33 | } 34 | ] 35 | } 36 | ], 37 | "extensionAbilities": [ 38 | { 39 | "name": "EntryBackupAbility", 40 | "srcEntry": "./ets/entrybackupability/EntryBackupAbility.ets", 41 | "type": "backup", 42 | "exported": false, 43 | "metadata": [ 44 | { 45 | "name": "ohos.extension.backup", 46 | "resource": "$profile:backup_config" 47 | } 48 | ], 49 | } 50 | ], 51 | "requestPermissions": [ 52 | { 53 | "name": "ohos.permission.WRITE_MEDIA", 54 | "reason": "$string:reason_write_media", 55 | "usedScene": { 56 | "when": "always", 57 | } 58 | }, 59 | { 60 | "name": "ohos.permission.READ_MEDIA", 61 | "reason": "$string:reason_read_media", 62 | "usedScene": { 63 | "when": "always", 64 | } 65 | }, 66 | { 67 | "name": "ohos.permission.INTERNET" 68 | } 69 | ] 70 | } 71 | } -------------------------------------------------------------------------------- /Example/testHotUpdate/ios/AwesomeProject/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | AwesomeProject 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | NSAllowsArbitraryLoads 30 | 31 | NSExceptionDomains 32 | 33 | localhost 34 | 35 | NSExceptionAllowsInsecureHTTPLoads 36 | 37 | 38 | 39 | 40 | NSCameraUsageDescription 41 | For taking photos 42 | NSLocationWhenInUseUsageDescription 43 | 44 | NSPhotoLibraryUsageDescription 45 | For saving photos 46 | RCTNewArchEnabled 47 | 48 | UILaunchStoryboardName 49 | LaunchScreen 50 | UIRequiredDeviceCapabilities 51 | 52 | armv7 53 | 54 | UISupportedInterfaceOrientations 55 | 56 | UIInterfaceOrientationPortrait 57 | UIInterfaceOrientationLandscapeLeft 58 | UIInterfaceOrientationLandscapeRight 59 | 60 | UIViewControllerBasedStatusBarAppearance 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /Example/testHotUpdate/android/app/src/main/res/drawable/rn_edit_text_material.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 21 | 22 | 23 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/android/app/src/main/res/drawable/rn_edit_text_material.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 21 | 22 | 23 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx512m -XX:MaxMetaspaceSize=256m 13 | org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | # AndroidX package structure to make it clearer which packages are bundled with the 21 | # Android operating system, and which are packaged with your app's APK 22 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 23 | android.useAndroidX=true 24 | # Automatically convert third-party libraries to use AndroidX 25 | android.enableJetifier=true 26 | 27 | # Version of flipper SDK to use with React Native 28 | FLIPPER_VERSION=0.182.0 29 | 30 | # Use this property to specify which architecture you want to build. 31 | # You can also override it from the CLI using 32 | # ./gradlew -PreactNativeArchitectures=x86_64 33 | reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 34 | 35 | # Use this property to enable support to the new architecture. 36 | # This will allow you to use TurboModules and the Fabric render in 37 | # your application. You should enable this flag either if you want 38 | # to write custom TurboModules/Fabric components OR use libraries that 39 | # are providing them. 40 | newArchEnabled=false 41 | 42 | # Use this property to enable or disable the Hermes JS engine. 43 | # If set to false, you will be using JSC instead. 44 | hermesEnabled=true 45 | -------------------------------------------------------------------------------- /src/context.ts: -------------------------------------------------------------------------------- 1 | import { createContext, useContext } from 'react'; 2 | import { CheckResult, ProgressData } from './type'; 3 | import { Pushy, Cresc } from './client'; 4 | import i18n from './i18n'; 5 | 6 | const noop = () => {}; 7 | const asyncNoop = () => Promise.resolve(); 8 | 9 | export const defaultContext = { 10 | checkUpdate: asyncNoop, 11 | switchVersion: asyncNoop, 12 | switchVersionLater: asyncNoop, 13 | markSuccess: noop, 14 | dismissError: noop, 15 | downloadUpdate: asyncNoop, 16 | downloadAndInstallApk: asyncNoop, 17 | restartApp: asyncNoop, 18 | getCurrentVersionInfo: () => Promise.resolve({}), 19 | parseTestQrCode: () => false, 20 | currentHash: '', 21 | packageVersion: '', 22 | currentVersionInfo: {}, 23 | }; 24 | 25 | export const UpdateContext = createContext<{ 26 | checkUpdate: () => Promise; 27 | switchVersion: () => Promise; 28 | switchVersionLater: () => Promise; 29 | markSuccess: () => void; 30 | dismissError: () => void; 31 | downloadUpdate: () => Promise; 32 | downloadAndInstallApk: (url: string) => Promise; 33 | // @deprecated use currentVersionInfo instead 34 | getCurrentVersionInfo: () => Promise<{ 35 | name?: string; 36 | description?: string; 37 | metaInfo?: string; 38 | }>; 39 | currentVersionInfo: { 40 | name?: string; 41 | description?: string; 42 | metaInfo?: string; 43 | } | null; 44 | parseTestQrCode: (code: string) => boolean; 45 | restartApp: () => Promise; 46 | currentHash: string; 47 | packageVersion: string; 48 | client?: Pushy | Cresc; 49 | progress?: ProgressData; 50 | updateInfo?: CheckResult; 51 | lastError?: Error; 52 | }>(defaultContext); 53 | 54 | export const useUpdate = __DEV__ ? () => { 55 | const context = useContext(UpdateContext); 56 | 57 | // 检查是否在 UpdateProvider 内部使用 58 | if (!context.client) { 59 | throw new Error(i18n.t('error_use_update_outside_provider')); 60 | } 61 | 62 | return context; 63 | } : () => useContext(UpdateContext); 64 | 65 | /** @deprecated Please use `useUpdate` instead */ 66 | export const usePushy = useUpdate; 67 | -------------------------------------------------------------------------------- /harmony/pushy/src/main/cpp/PushyPackage.h: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (C) 2023 Huawei Device Co., Ltd. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef GEO_LOCATION_PACKAGE_H 26 | #define GEO_LOCATION_PACKAGE_H 27 | 28 | #include "RNOH/Package.h" 29 | #include "PushyTurboModule.h" 30 | 31 | using namespace rnoh; 32 | using namespace facebook; 33 | 34 | class PushyTurboModuleFactoryDelegate : public TurboModuleFactoryDelegate { 35 | public: 36 | SharedTurboModule createTurboModule(Context ctx, const std::string &name) const override 37 | { 38 | if (name == "Pushy") { 39 | return std::make_shared(ctx, name); 40 | } 41 | return nullptr; 42 | }; 43 | }; 44 | 45 | namespace rnoh { 46 | class PushyPackage : public Package { 47 | public: 48 | PushyPackage(Package::Context ctx) : Package(ctx){} 49 | std::unique_ptr createTurboModuleFactoryDelegate() override 50 | { 51 | return std::make_unique(); 52 | } 53 | }; 54 | } // namespace rnoh 55 | #endif 56 | -------------------------------------------------------------------------------- /src/isInRollout.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-fallthrough */ 2 | 3 | import { cInfo } from './core'; 4 | 5 | /* eslint-disable no-bitwise */ 6 | function murmurhash3_32_gc(key: string, seed = 0) { 7 | let remainder, bytes, h1, h1b, c1, c2, k1, i; 8 | 9 | remainder = key.length & 3; // key.length % 4 10 | bytes = key.length - remainder; 11 | h1 = seed; 12 | c1 = 0xcc9e2d51; 13 | c2 = 0x1b873593; 14 | i = 0; 15 | 16 | while (i < bytes) { 17 | k1 = 18 | (key.charCodeAt(i) & 0xff) | 19 | ((key.charCodeAt(++i) & 0xff) << 8) | 20 | ((key.charCodeAt(++i) & 0xff) << 16) | 21 | ((key.charCodeAt(++i) & 0xff) << 24); 22 | ++i; 23 | 24 | ((k1 & 0xffff) * c1 + ((((k1 >>> 16) * c1) & 0xffff) << 16)) & 0xffffffff; 25 | k1 = (k1 << 15) | (k1 >>> 17); 26 | k1 = 27 | ((k1 & 0xffff) * c2 + ((((k1 >>> 16) * c2) & 0xffff) << 16)) & 0xffffffff; 28 | 29 | h1 ^= k1; 30 | h1 = (h1 << 13) | (h1 >>> 19); 31 | h1b = 32 | ((h1 & 0xffff) * 5 + ((((h1 >>> 16) * 5) & 0xffff) << 16)) & 0xffffffff; 33 | h1 = (h1b & 0xffff) + 0x6b64 + ((((h1b >>> 16) + 0xe654) & 0xffff) << 16); 34 | } 35 | 36 | k1 = 0; 37 | 38 | switch (remainder) { 39 | case 3: 40 | k1 ^= (key.charCodeAt(i + 2) & 0xff) << 16; 41 | case 2: 42 | k1 ^= (key.charCodeAt(i + 1) & 0xff) << 8; 43 | case 1: 44 | k1 ^= key.charCodeAt(i) & 0xff; 45 | 46 | k1 = 47 | ((k1 & 0xffff) * c1 + ((((k1 >>> 16) * c1) & 0xffff) << 16)) & 48 | 0xffffffff; 49 | k1 = (k1 << 15) | (k1 >>> 17); 50 | k1 = 51 | ((k1 & 0xffff) * c2 + ((((k1 >>> 16) * c2) & 0xffff) << 16)) & 52 | 0xffffffff; 53 | h1 ^= k1; 54 | } 55 | 56 | h1 ^= key.length; 57 | 58 | h1 ^= h1 >>> 16; 59 | h1 = 60 | ((h1 & 0xffff) * 0x85ebca6b + 61 | ((((h1 >>> 16) * 0x85ebca6b) & 0xffff) << 16)) & 62 | 0xffffffff; 63 | h1 ^= h1 >>> 13; 64 | h1 = 65 | ((h1 & 0xffff) * 0xc2b2ae35 + 66 | ((((h1 >>> 16) * 0xc2b2ae35) & 0xffff) << 16)) & 67 | 0xffffffff; 68 | h1 ^= h1 >>> 16; 69 | 70 | return h1 >>> 0; 71 | } 72 | 73 | const intForUUID = murmurhash3_32_gc(cInfo.uuid); 74 | 75 | export function isInRollout(rollout: number) { 76 | return intForUUID % 100 < rollout; 77 | } 78 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/android/app/src/main/java/com/harmony_use_pushy/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.harmony_use_pushy; 2 | 3 | import android.app.Application; 4 | import com.facebook.react.PackageList; 5 | import com.facebook.react.ReactApplication; 6 | import com.facebook.react.ReactNativeHost; 7 | import com.facebook.react.ReactPackage; 8 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint; 9 | import com.facebook.react.defaults.DefaultReactNativeHost; 10 | import com.facebook.soloader.SoLoader; 11 | import java.util.List; 12 | 13 | public class MainApplication extends Application implements ReactApplication { 14 | 15 | private final ReactNativeHost mReactNativeHost = 16 | new DefaultReactNativeHost(this) { 17 | @Override 18 | public boolean getUseDeveloperSupport() { 19 | return BuildConfig.DEBUG; 20 | } 21 | 22 | @Override 23 | protected List getPackages() { 24 | @SuppressWarnings("UnnecessaryLocalVariable") 25 | List packages = new PackageList(this).getPackages(); 26 | // Packages that cannot be autolinked yet can be added manually here, for example: 27 | // packages.add(new MyReactNativePackage()); 28 | return packages; 29 | } 30 | 31 | @Override 32 | protected String getJSMainModuleName() { 33 | return "index"; 34 | } 35 | 36 | @Override 37 | protected boolean isNewArchEnabled() { 38 | return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED; 39 | } 40 | 41 | @Override 42 | protected Boolean isHermesEnabled() { 43 | return BuildConfig.IS_HERMES_ENABLED; 44 | } 45 | }; 46 | 47 | @Override 48 | public ReactNativeHost getReactNativeHost() { 49 | return mReactNativeHost; 50 | } 51 | 52 | @Override 53 | public void onCreate() { 54 | super.onCreate(); 55 | SoLoader.init(this, /* native exopackage */ false); 56 | if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { 57 | // If you opted-in for the New Architecture, we load the native entry point for this app. 58 | DefaultNewArchitectureEntryPoint.load(); 59 | } 60 | ReactNativeFlipper.initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/ios/harmony_use_pushyTests/harmony_use_pushyTests.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | #import 5 | #import 6 | 7 | #define TIMEOUT_SECONDS 600 8 | #define TEXT_TO_LOOK_FOR @"Welcome to React" 9 | 10 | @interface harmony_use_pushyTests : XCTestCase 11 | 12 | @end 13 | 14 | @implementation harmony_use_pushyTests 15 | 16 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL (^)(UIView *view))test 17 | { 18 | if (test(view)) { 19 | return YES; 20 | } 21 | for (UIView *subview in [view subviews]) { 22 | if ([self findSubviewInView:subview matching:test]) { 23 | return YES; 24 | } 25 | } 26 | return NO; 27 | } 28 | 29 | - (void)testRendersWelcomeScreen 30 | { 31 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; 32 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; 33 | BOOL foundElement = NO; 34 | 35 | __block NSString *redboxError = nil; 36 | #ifdef DEBUG 37 | RCTSetLogFunction( 38 | ^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { 39 | if (level >= RCTLogLevelError) { 40 | redboxError = message; 41 | } 42 | }); 43 | #endif 44 | 45 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { 46 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 47 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 48 | 49 | foundElement = [self findSubviewInView:vc.view 50 | matching:^BOOL(UIView *view) { 51 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { 52 | return YES; 53 | } 54 | return NO; 55 | }]; 56 | } 57 | 58 | #ifdef DEBUG 59 | RCTSetLogFunction(RCTDefaultLogFunction); 60 | #endif 61 | 62 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); 63 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); 64 | } 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /harmony/pushy/OAT.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | LICENSE 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Resolve react_native_pods.rb with node to allow for hoisting 2 | require Pod::Executable.execute_command('node', ['-p', 3 | 'require.resolve( 4 | "react-native/scripts/react_native_pods.rb", 5 | {paths: [process.argv[1]]}, 6 | )', __dir__]).strip 7 | 8 | platform :ios, min_ios_version_supported 9 | prepare_react_native_project! 10 | 11 | # If you are using a `react-native-flipper` your iOS build will fail when `NO_FLIPPER=1` is set. 12 | # because `react-native-flipper` depends on (FlipperKit,...) that will be excluded 13 | # 14 | # To fix this you can also exclude `react-native-flipper` using a `react-native.config.js` 15 | # ```js 16 | # module.exports = { 17 | # dependencies: { 18 | # ...(process.env.NO_FLIPPER ? { 'react-native-flipper': { platforms: { ios: null } } } : {}), 19 | # ``` 20 | flipper_config = ENV['NO_FLIPPER'] == "1" ? FlipperConfiguration.disabled : FlipperConfiguration.enabled 21 | 22 | linkage = ENV['USE_FRAMEWORKS'] 23 | if linkage != nil 24 | Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green 25 | use_frameworks! :linkage => linkage.to_sym 26 | end 27 | 28 | target 'harmony_use_pushy' do 29 | config = use_native_modules! 30 | 31 | # Flags change depending on the env values. 32 | flags = get_default_flags() 33 | 34 | use_react_native!( 35 | :path => config[:reactNativePath], 36 | # Hermes is now enabled by default. Disable by setting this flag to false. 37 | :hermes_enabled => flags[:hermes_enabled], 38 | :fabric_enabled => flags[:fabric_enabled], 39 | # Enables Flipper. 40 | # 41 | # Note that if you have use_frameworks! enabled, Flipper will not work and 42 | # you should disable the next line. 43 | :flipper_configuration => flipper_config, 44 | # An absolute path to your application root. 45 | :app_path => "#{Pod::Config.instance.installation_root}/.." 46 | ) 47 | 48 | target 'harmony_use_pushyTests' do 49 | inherit! :complete 50 | # Pods for testing 51 | end 52 | 53 | post_install do |installer| 54 | # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202 55 | react_native_post_install( 56 | installer, 57 | config[:reactNativePath], 58 | :mac_catalyst_enabled => false 59 | ) 60 | __apply_Xcode_12_5_M1_post_install_workaround(installer) 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /android/jni/DownloadTask.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by DengYun on 3/31/16. 3 | // 4 | 5 | #include "cn_reactnative_modules_update_DownloadTask.h" 6 | 7 | #include "hpatch.h" 8 | #define _check(v,errInfo) do{ if (!(v)) { _isError=hpatch_TRUE; _errInfo=errInfo; goto _clear; } }while(0) 9 | 10 | JNIEXPORT jbyteArray JNICALL Java_cn_reactnative_modules_update_DownloadTask_hdiffPatch 11 | (JNIEnv *env, jobject self, jbyteArray origin, jbyteArray patch){ 12 | hpatch_BOOL _isError=hpatch_FALSE; 13 | const char* _errInfo=""; 14 | 15 | jbyte* originPtr = (*env)->GetByteArrayElements(env, origin, NULL); 16 | size_t originLength = (*env)->GetArrayLength(env, origin); 17 | jbyte* patchPtr = (*env)->GetByteArrayElements(env, patch, NULL); 18 | size_t patchLength = (*env)->GetArrayLength(env, patch); 19 | jbyteArray ret = NULL; 20 | jbyte* outPtr = NULL; 21 | size_t newsize = 0; 22 | hpatch_singleCompressedDiffInfo patInfo; 23 | 24 | _check(((originLength==0)||originPtr) && patchPtr && (patchLength>0),"Corrupt patch"); 25 | _check(kHPatch_ok==hpatch_getInfo_by_mem(&patInfo,(const uint8_t*)patchPtr,patchLength),"Error info in hpatch"); 26 | _check(originLength==patInfo.oldDataSize,"Error oldDataSize in hpatch"); 27 | newsize=(size_t)patInfo.newDataSize; 28 | if (sizeof(size_t)!=sizeof(hpatch_StreamPos_t)) 29 | _check(newsize==patInfo.newDataSize,"Error newDataSize in hpatch"); 30 | 31 | ret = (*env)->NewByteArray(env,newsize); 32 | _check(ret,"Error JNIEnv::NewByteArray()"); 33 | if (newsize>0) { 34 | outPtr = (*env)->GetByteArrayElements(env, ret, NULL); 35 | _check(outPtr,"Corrupt JNIEnv::GetByteArrayElements"); 36 | } 37 | 38 | _check(kHPatch_ok==hpatch_by_mem((const uint8_t*)originPtr,originLength,(uint8_t*)outPtr,newsize, 39 | (const uint8_t*)patchPtr,patchLength,&patInfo),"hpacth"); 40 | 41 | _clear: 42 | if (outPtr) (*env)->ReleaseByteArrayElements(env, ret, outPtr, (_isError?JNI_ABORT:0)); 43 | if (originPtr) (*env)->ReleaseByteArrayElements(env, origin, originPtr, JNI_ABORT); 44 | if (patchPtr) (*env)->ReleaseByteArrayElements(env, patch, patchPtr, JNI_ABORT); 45 | if (_isError){ 46 | jclass newExcCls = NULL; 47 | if (ret){ 48 | (*env)->DeleteLocalRef(env, ret); 49 | ret = NULL; 50 | } 51 | newExcCls = (*env)->FindClass(env, "java/lang/Error"); 52 | if (newExcCls != NULL) // Unable to find the new exception class, give up. 53 | (*env)->ThrowNew(env, newExcCls, _errInfo); 54 | } 55 | return ret; 56 | } 57 | -------------------------------------------------------------------------------- /src/locales/zh.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | // Common messages 3 | checking_update: '正在检查更新...', 4 | downloading_update: '正在下载更新包...', 5 | installing_update: '正在安装更新...', 6 | update_available: '发现新版本', 7 | update_downloaded: '更新包下载完成', 8 | update_installed: '更新安装完成', 9 | no_update_available: '已是最新版本', 10 | update_failed: '更新失败', 11 | network_error: '网络连接错误', 12 | download_failed: '下载失败', 13 | install_failed: '安装失败', 14 | 15 | // Progress messages with interpolation 16 | download_progress: '下载进度: {{progress}}%', 17 | download_speed: '下载速度: {{speed}}/s', 18 | file_size: '文件大小: {{size}}', 19 | time_remaining: '剩余时间: {{time}}', 20 | 21 | // Error messages 22 | error_code: '错误代码: {{code}}', 23 | error_message: '错误信息: {{message}}', 24 | retry_count: '重试次数: {{count}}/{{max}}', 25 | 26 | // Update info 27 | version_info: '版本 {{version}} ({{build}})', 28 | release_notes: '更新说明: {{notes}}', 29 | update_size: '更新包大小: {{size}}MB', 30 | 31 | // Alert messages 32 | alert_title: '提示', 33 | alert_update_ready: '下载完毕,是否立即更新?', 34 | alert_next_time: '下次再说', 35 | alert_update_now: '立即更新', 36 | alert_app_updated: '您的应用版本已更新,点击更新下载安装新版本', 37 | alert_update_button: '更新', 38 | alert_cancel: '取消', 39 | alert_confirm: '确定', 40 | alert_info: '信息', 41 | alert_no_update_wait: '未发现更新,请等待10秒让服务器生成补丁包', 42 | 43 | // Error messages 44 | error_appkey_required: '需要提供 appKey', 45 | error_update_check_failed: '更新检查失败', 46 | error_cannot_connect_server: '无法连接到更新服务器。请检查网络连接。', 47 | error_cannot_connect_backup: 48 | '无法连接到更新服务器: {{message}}。正在尝试备用端点。', 49 | error_diff_failed: 'diff 错误: {{message}}', 50 | error_pdiff_failed: 'pdiff 错误: {{message}}', 51 | error_full_patch_failed: '完整补丁错误: {{message}}', 52 | error_all_promises_rejected: '所有请求都被拒绝', 53 | error_ping_failed: 'Ping 失败', 54 | error_ping_timeout: 'Ping 超时', 55 | error_http_status: '{{status}} {{statusText}}', 56 | 57 | // Development messages 58 | dev_debug_disabled: 59 | '您当前处于开发环境且未启用调试模式。{{matter}} 将不会执行。如需在开发环境中调试 {{matter}},请在客户端中将 debug 设为 true。', 60 | dev_log_prefix: 'react-native-update: ', 61 | dev_web_not_supported: 62 | 'react-native-update 不支持 Web 平台,不会执行任何操作', 63 | 64 | // More alert messages 65 | alert_new_version_found: 66 | '检查到新的版本{{name}},是否下载?\n{{description}}', 67 | 68 | // Development environment messages 69 | dev_incremental_update_disabled: 70 | '当前是开发环境,无法执行增量式热更新,重启不会生效。如果需要在开发环境中测试可生效的全量热更新(但也会在再次重启后重新连接 metro),请打开"忽略时间戳"开关再重试。', 71 | 72 | // Context error messages 73 | error_use_update_outside_provider: 74 | 'useUpdate 必须在 UpdateProvider 内部使用。请使用 包裹您的组件树。', 75 | }; 76 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-native-update [![npm version](https://badge.fury.io/js/react-native-update.svg)](http://badge.fury.io/js/react-native-update) 2 | 3 | 本组件是面向 React Native 提供热更新功能的组件,详情请访问我们的官方网站 。 4 | 5 | **现已支持鸿蒙以及新架构** 6 | 7 | ### 快速开始 8 | 9 | 请查看[文档](https://pushy.reactnative.cn/docs/getting-started.html) 10 | 11 | ### 优势 12 | 13 | 1. 对中国用户使用阿里云高速 CDN 分发,对比其他服务器在国外的热更新服务,分发更稳定,更新成功率极高。对国外用户则智能分流至 cloudflare,同样享受高速更新服务。 14 | 2. 基于 bsdiff/hdiff 算法创建的**超小更新包**,通常版本迭代后在几十 KB 级别(其他全量热更新服务所需流量通常在几十 MB 级别)。 15 | 3. 始终跟进 RN 最新正式版本,第一时间提供支持。支持 hermes 字节码格式。支持新架构(注:安卓 0.73.0 ~ 0.76.0 的新架构因官方 bug 不支持,0.73 以下或 0.76.1 以上的新架构可用)。 16 | 4. 跨越多个版本进行更新时,只需要下载**一个更新包**,不需要逐版本依次更新。 17 | 5. 命令行工具 & 网页双端管理,版本发布过程简单便捷,完全可以集成 CI。 18 | 6. 支持崩溃回滚,安全可靠。 19 | 7. meta 信息及开放 API,提供更高扩展性。 20 | 8. 提供付费的专人技术支持。 21 | 22 | ### 与其他热更新库对比 23 | 24 | | 对比维度 | react-native-update | expo-update | react-native-code-push | 25 | |---------|---------------------|-------------|------------------------| 26 | | **价格/成本** | 提供免费额度,多级梯度付费(最低约 66 元/月),流量不单独计费 | 提供免费额度,多级梯度付费(最低约 136 元/月),超出流量额外计费 | ❌ **已停运**(Microsoft App Center 已于 2025 年 3 月 31 日停止服务) | 27 | | **更新包大小** | ⭐⭐⭐⭐⭐ 几十 KB(增量更新) | ⭐⭐⭐ 全量更新(通常几十 MB) | ❌ **已停运** | 28 | | **中国地区访问速度** | ⭐⭐⭐⭐⭐ 阿里云 CDN,速度极快 | ⭐⭐ 国外服务器,可能较慢 | ❌ **已停运** | 29 | | **iOS 支持** | ✅ 支持 | ✅ 支持 | ❌ **已停运** | 30 | | **Android 支持** | ✅ 支持 | ✅ 支持 | ❌ **已停运** | 31 | | **鸿蒙支持** | ✅ 支持 | ❌ 不支持 | ❌ **已停运** | 32 | | **Expo 支持** | ✅ 支持 | ✅ 支持 | ❌ **已停运** | 33 | | **RN 版本支持** | ⭐⭐⭐⭐⭐ 第一时间支持最新版本 | ⭐⭐⭐⭐ 跟随 Expo SDK | ❌ **已停运** | 34 | | **新架构支持** | ✅ 支持 | ✅ 支持 | ❌ **已停运** | 35 | | **Hermes 支持** | ✅ 支持 | ✅ 支持 | ❌ **已停运** | 36 | | **崩溃回滚** | ✅ 自动回滚机制 | ✅ 支持 | ❌ **已停运** | 37 | | **管理界面** | ✅ 命令行工具 + Web 管理界面 | ✅ Expo Dashboard | ❌ **已停运** | 38 | | **CI/CD 集成** | ✅ 支持 | ✅ 支持 | ❌ **已停运** | 39 | | **API 扩展性** | ✅ Meta 信息 + 开放 API | ⚠️ 有限 | ❌ **已停运** | 40 | | **中文文档/支持** | ⭐⭐⭐⭐⭐ 完整中文文档,中文社区支持 | ⭐⭐ 英文为主 | ❌ **已停运** | 41 | | **技术支持** | ✅ 付费专人技术支持 | ⚠️ 社区支持 | ❌ **已停运** | 42 | | **服务器部署** | ✅ 可托管也可付费私有部署 | ✅ Expo 托管(EAS Update) | ❌ **已停运** | 43 | | **更新策略** | 灵活配置(静默/提示/立即/延迟) | 相对固定 | ❌ **已停运** | 44 | | **流量消耗** | ⭐⭐⭐⭐⭐ 极低(增量更新) | ⭐⭐⭐ 较高(全量更新) | ❌ **已停运** | 45 | | **更新成功率** | ⭐⭐⭐⭐⭐ 极高(国内 CDN 优势) | ⭐⭐⭐ 中等 | ❌ **已停运** | 46 | 47 | 48 | 49 | ### 本地开发 50 | 51 | ``` 52 | $ git clone git@github.com:reactnativecn/react-native-update.git 53 | $ cd react-native-pushy/Example/testHotUpdate 54 | $ yarn 55 | $ yarn start 56 | ``` 57 | 58 | 本地库文件使用 yarn link 链接,因此可直接在源文件中修改,在 testHotUpdate 项目中调试。 59 | 60 | ### 关于我们 61 | 62 | 本组件由[React Native 中文网](https://reactnative.cn/)独家发布,如有定制需求可以[联系我们](https://reactnative.cn/about.html#content)。 63 | 64 | 关于此插件发现任何问题,可以前往[Issues](https://github.com/reactnativecn/react-native-update/issues)发帖提问。 65 | -------------------------------------------------------------------------------- /src/type.ts: -------------------------------------------------------------------------------- 1 | export interface VersionInfo { 2 | name: string; 3 | hash: string; 4 | description: string; 5 | metaInfo: string; 6 | config: { 7 | rollout: { 8 | [packageVersion: string]: number; 9 | }; 10 | [key: string]: any; 11 | }; 12 | pdiff?: string; 13 | diff?: string; 14 | full?: string; 15 | } 16 | 17 | interface RootResult { 18 | upToDate?: true; 19 | expired?: true; 20 | downloadUrl?: string; 21 | update?: true; 22 | paused?: 'app' | 'package'; 23 | message?: string; 24 | paths?: string[]; 25 | } 26 | 27 | export type CheckResult = RootResult & 28 | Partial & { 29 | expVersion?: VersionInfo; 30 | }; 31 | 32 | export interface ProgressData { 33 | hash: string; 34 | received: number; 35 | total: number; 36 | } 37 | 38 | export type EventType = 39 | | 'rollback' 40 | | 'errorChecking' 41 | | 'checking' 42 | | 'downloading' 43 | | 'downloadSuccess' 44 | | 'errorUpdate' 45 | | 'markSuccess' 46 | | 'downloadingApk' 47 | | 'rejectStoragePermission' 48 | | 'errorStoragePermission' 49 | | 'errorDownloadAndInstallApk' 50 | | 'errorInstallApk'; 51 | 52 | export interface EventData { 53 | currentVersion: string; 54 | cInfo: { 55 | rnu: string; 56 | rn: string; 57 | os: string; 58 | uuid: string; 59 | }; 60 | packageVersion: string; 61 | buildTime: string; 62 | message?: string; 63 | rolledBackVersion?: string; 64 | newVersion?: string; 65 | name?: string; 66 | description?: string; 67 | metaInfo?: string; 68 | [key: string]: any; 69 | } 70 | 71 | export type UpdateEventsLogger = ({ 72 | type, 73 | data, 74 | }: { 75 | type: EventType; 76 | data: EventData; 77 | }) => void; 78 | 79 | export interface UpdateServerConfig { 80 | main: string; 81 | backups?: string[]; 82 | queryUrls?: string[]; 83 | } 84 | 85 | export interface ClientOptions { 86 | appKey: string; 87 | server?: UpdateServerConfig; 88 | logger?: UpdateEventsLogger; 89 | locale?: 'zh' | 'en'; 90 | updateStrategy?: 91 | | 'alwaysAlert' 92 | | 'alertUpdateAndIgnoreError' 93 | | 'silentAndNow' 94 | | 'silentAndLater' 95 | | null; 96 | checkStrategy?: 'onAppStart' | 'onAppResume' | 'both' | null; 97 | autoMarkSuccess?: boolean; 98 | dismissErrorAfter?: number; 99 | debug?: boolean; 100 | throwError?: boolean; 101 | beforeCheckUpdate?: () => Promise; 102 | beforeDownloadUpdate?: (info: CheckResult) => Promise; 103 | afterDownloadUpdate?: (info: CheckResult) => Promise; 104 | onPackageExpired?: (info: CheckResult) => Promise; 105 | overridePackageVersion?: string; 106 | } 107 | 108 | export interface UpdateTestPayload { 109 | type: '__rnPushyVersionHash' | string | null; 110 | data: any; 111 | } 112 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/harmony/entry/src/main/ets/pages/Index.ets: -------------------------------------------------------------------------------- 1 | import { PushyFileJSBundleProvider } from 'pushy/src/main/ets/PushyFileJSBundleProvider'; 2 | import { ComponentBuilderContext, RNOHCoreContext,RNAbility, 3 | MetroJSBundleProvider } from '@rnoh/react-native-openharmony'; 4 | import { 5 | RNApp, 6 | AnyJSBundleProvider, 7 | ResourceJSBundleProvider, 8 | TraceJSBundleProviderDecorator, 9 | } from '@rnoh/react-native-openharmony' 10 | import { createRNPackages } from '../RNPackagesFactory' 11 | import preferences from '@ohos.data.preferences'; 12 | 13 | const arkTsComponentNames: Array = []; 14 | 15 | @Builder 16 | export function buildCustomRNComponent(ctx: ComponentBuilderContext) { 17 | // There seems to be a problem with the placement of ArkTS components in mixed mode. Nested Stack temporarily avoided. 18 | Stack() { 19 | } 20 | .position({ x: 0, y: 0 }) 21 | } 22 | 23 | 24 | const wrappedCustomRNComponentBuilder = wrapBuilder(buildCustomRNComponent) 25 | 26 | @Entry 27 | @Component 28 | struct Index { 29 | @StorageLink('RNOHCoreContext') private rnohCoreContext: RNOHCoreContext | undefined = undefined 30 | @State shouldShow: boolean = false 31 | 32 | aboutToAppear(): void { 33 | this.shouldShow = true 34 | } 35 | 36 | onBackPress(): boolean | undefined { 37 | // NOTE: this is required since `Ability`'s `onBackPressed` function always 38 | // terminates or puts the app in the background, but we want Ark to ignore it completely 39 | // when handled by RN 40 | this.rnohCoreContext!.dispatchBackPress() 41 | 42 | // this.preferences = preferences.getPreferencesSync(this.context, {name:'update'}); 43 | return true 44 | } 45 | 46 | build() { 47 | Column() { 48 | if (this.rnohCoreContext && this.shouldShow) { 49 | RNApp({ 50 | rnInstanceConfig: { 51 | createRNPackages, 52 | enableNDKTextMeasuring: true, 53 | enableBackgroundExecutor: false, 54 | enableCAPIArchitecture: true, 55 | arkTsComponentNames: arkTsComponentNames, 56 | }, 57 | appKey: "harmony_use_pushy", 58 | wrappedCustomRNComponentBuilder: wrappedCustomRNComponentBuilder, 59 | onSetUp: (rnInstance) => { 60 | rnInstance.enableFeatureFlag("ENABLE_RN_INSTANCE_CLEAN_UP") 61 | }, 62 | jsBundleProvider: new TraceJSBundleProviderDecorator( 63 | new AnyJSBundleProvider([ 64 | // local debug mode 65 | new MetroJSBundleProvider(), 66 | // release mode 67 | new PushyFileJSBundleProvider(this.rnohCoreContext.uiAbilityContext), 68 | new ResourceJSBundleProvider(this.rnohCoreContext.uiAbilityContext.resourceManager, 'bundle.harmony.js') 69 | ]), 70 | this.rnohCoreContext.logger), 71 | }) 72 | } 73 | } 74 | .height('100%') 75 | .width('100%') 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /ios/RCTPushy/RCTPushyDownloader.mm: -------------------------------------------------------------------------------- 1 | #import "RCTPushyDownloader.h" 2 | 3 | @interface RCTPushyDownloader() 4 | 5 | @property (copy) void (^progressHandler)(long long, long long); 6 | @property (copy) void (^completionHandler)(NSString*, NSError*); 7 | @property (copy) NSString *savePath; 8 | @end 9 | 10 | @implementation RCTPushyDownloader 11 | 12 | + (void)download:(NSString *)downloadPath savePath:(NSString *)savePath 13 | progressHandler:(void (^)(long long receivedBytes, long long totalBytes))progressHandler 14 | completionHandler:(void (^)(NSString *path, NSError *error))completionHandler 15 | { 16 | NSAssert(downloadPath, @"no download path"); 17 | NSAssert(savePath, @"no save path"); 18 | 19 | RCTPushyDownloader *downloader = [RCTPushyDownloader new]; 20 | downloader.progressHandler = progressHandler; 21 | downloader.completionHandler = completionHandler; 22 | downloader.savePath = savePath; 23 | 24 | [downloader download:downloadPath]; 25 | } 26 | 27 | - (void)dealloc 28 | { 29 | } 30 | 31 | - (void)download:(NSString *)path 32 | { 33 | NSURL *url = [NSURL URLWithString:path]; 34 | 35 | NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration]; 36 | NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig 37 | delegate:self 38 | delegateQueue:nil]; 39 | 40 | NSURLSessionDownloadTask *task = [session downloadTaskWithURL:url]; 41 | [session downloadTaskWithURL:url]; 42 | [task resume]; 43 | } 44 | 45 | #pragma mark - session delegate 46 | 47 | - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask 48 | didWriteData:(int64_t)bytesWritten 49 | totalBytesWritten:(int64_t)totalBytesWritten 50 | totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite 51 | { 52 | #ifdef DEBUG 53 | NSLog(@"download progress, %lld, %lld, %lld", bytesWritten, totalBytesWritten, totalBytesExpectedToWrite); 54 | #endif 55 | 56 | if (self.progressHandler) { 57 | self.progressHandler(totalBytesWritten ,totalBytesExpectedToWrite); 58 | } 59 | } 60 | 61 | - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask 62 | didFinishDownloadingToURL:(NSURL *)location 63 | { 64 | NSData *data = [NSData dataWithContentsOfURL:location]; 65 | NSError *error; 66 | [data writeToFile:self.savePath options:NSDataWritingAtomic error:&error]; 67 | if (error) { 68 | if (self.completionHandler) { 69 | self.completionHandler(nil, error); 70 | self.completionHandler = nil; 71 | } 72 | } 73 | } 74 | 75 | - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task 76 | didCompleteWithError:(NSError *)error 77 | { 78 | if (self.completionHandler) { 79 | self.completionHandler(self.savePath, error); 80 | } 81 | } 82 | 83 | @end 84 | -------------------------------------------------------------------------------- /Example/testHotUpdate/.detoxrc.js: -------------------------------------------------------------------------------- 1 | /** @type {Detox.DetoxConfig} */ 2 | module.exports = { 3 | logger: { 4 | level: process.env.CI ? 'debug' : undefined, 5 | }, 6 | testRunner: { 7 | args: { 8 | config: 'e2e/jest.config.js', 9 | maxWorkers: process.env.CI ? 2 : undefined, 10 | _: ['e2e'], 11 | }, 12 | }, 13 | artifacts: { 14 | plugins: { 15 | log: process.env.CI ? 'failing' : undefined, 16 | screenshot: process.env.CI ? 'failing' : undefined, 17 | }, 18 | }, 19 | apps: { 20 | 'ios.debug': { 21 | type: 'ios.app', 22 | binaryPath: 'ios/build/Build/Products/Debug-iphonesimulator/AwesomeProject.app', 23 | build: "xcodebuild -workspace ios/AwesomeProject.xcworkspace -UseNewBuildSystem=NO -scheme AwesomeProject -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build", 24 | start: "scripts/start-rn.sh ios", 25 | }, 26 | 'ios.release': { 27 | type: 'ios.app', 28 | binaryPath: 29 | 'ios/build/Build/Products/Release-iphonesimulator/AwesomeProject.app', 30 | build: 31 | 'export RCT_NO_LAUNCH_PACKAGER=true && xcodebuild -workspace ios/AwesomeProject.xcworkspace -UseNewBuildSystem=NO -scheme AwesomeProject -configuration Release -sdk iphonesimulator -derivedDataPath ios/build -quiet', 32 | }, 33 | 'android.debug': { 34 | type: 'android.apk', 35 | binaryPath: 'android/app/build/outputs/apk/debug/app-debug.apk', 36 | build: 37 | 'cd android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug', 38 | start: "scripts/start-rn.sh android", 39 | reversePorts: [8081], 40 | }, 41 | 'android.release': { 42 | type: 'android.apk', 43 | binaryPath: 'android/app/build/outputs/apk/release/app-release.apk', 44 | build: 45 | 'cd android && ./gradlew assembleRelease assembleAndroidTest -DtestBuildType=release', 46 | }, 47 | }, 48 | devices: { 49 | simulator: { 50 | type: 'ios.simulator', 51 | device: { 52 | type: 'iPhone 14', 53 | }, 54 | }, 55 | attached: { 56 | type: 'android.attached', 57 | device: { 58 | adbName: '.*', 59 | }, 60 | }, 61 | emulator: { 62 | type: 'android.emulator', 63 | device: { 64 | avdName: 'Pixel_3a_API_33_arm64-v8a', 65 | }, 66 | }, 67 | }, 68 | configurations: { 69 | 'ios.sim.debug': { 70 | device: 'simulator', 71 | app: 'ios.debug', 72 | }, 73 | 'ios.sim.release': { 74 | device: 'simulator', 75 | app: 'ios.release', 76 | }, 77 | 'android.att.debug': { 78 | device: 'attached', 79 | app: 'android.debug', 80 | }, 81 | 'android.att.release': { 82 | device: 'attached', 83 | app: 'android.release', 84 | }, 85 | 'android.emu.debug': { 86 | device: 'emulator', 87 | app: 'android.debug', 88 | }, 89 | 'android.emu.release': { 90 | device: 'emulator', 91 | app: 'android.release', 92 | }, 93 | }, 94 | }; 95 | -------------------------------------------------------------------------------- /scripts/check-expo-version.js: -------------------------------------------------------------------------------- 1 | const ownPackageJson = require('../package.json'); 2 | 3 | if (process.env.npm_package_name === ownPackageJson.name) { 4 | console.log('Skipping postinstall during local development.'); 5 | process.exit(0); 6 | } 7 | 8 | const fs = require('fs'); 9 | const path = require('path'); 10 | 11 | const projectRoot = path.resolve(__dirname, '..'); // react-native-update module root 12 | const expoConfigPath = path.resolve(projectRoot, 'expo-module.config.json'); 13 | 14 | function getExpoMajorVersion() { 15 | let resolvedExpoPackagePath; 16 | try { 17 | // Use require.resolve to find expo's package.json from the host project's perspective 18 | resolvedExpoPackagePath = require.resolve('expo/package.json', { 19 | paths: [path.resolve(projectRoot, '..', '..')], 20 | }); 21 | } catch (e) { 22 | console.log( 23 | 'Expo not found in project node_modules (via require.resolve).', 24 | ); 25 | return null; // Expo not found or resolvable 26 | } 27 | 28 | // Check if the resolved path actually exists (belt-and-suspenders) 29 | if (!fs.existsSync(resolvedExpoPackagePath)) { 30 | console.log( 31 | `Expo package.json path resolved to ${resolvedExpoPackagePath}, but file does not exist.`, 32 | ); 33 | return null; 34 | } 35 | 36 | try { 37 | const packageJson = JSON.parse( 38 | fs.readFileSync(resolvedExpoPackagePath, 'utf8'), 39 | ); 40 | const version = packageJson.version; 41 | if (!version) { 42 | console.log('Expo package.json does not contain a version.'); 43 | return null; // Version not found 44 | } 45 | 46 | // Extract the first number sequence as the major version 47 | const match = version.match(/\d+/); 48 | if (!match) { 49 | console.log( 50 | `Could not parse major version from Expo version string: ${version}`, 51 | ); 52 | return null; // Cannot parse version 53 | } 54 | 55 | return parseInt(match[0], 10); 56 | } catch (error) { 57 | console.error('Error reading or parsing Expo package.json:', error); 58 | return null; // Error during processing 59 | } 60 | } 61 | 62 | function checkAndCleanExpoConfig() { 63 | const majorVersion = getExpoMajorVersion(); 64 | 65 | // Condition: Expo not found OR major version is less than 50 66 | if (majorVersion === null || majorVersion < 50) { 67 | if (fs.existsSync(expoConfigPath)) { 68 | try { 69 | fs.unlinkSync(expoConfigPath); 70 | console.log( 71 | `Expo version (${ 72 | majorVersion !== null ? majorVersion : 'not found' 73 | }) is < 50 or Expo not found. Deleted ${expoConfigPath}`, 74 | ); 75 | } catch (error) { 76 | console.error(`Failed to delete ${expoConfigPath}:`, error); 77 | } 78 | } else { 79 | console.log( 80 | `Expo version (${ 81 | majorVersion !== null ? majorVersion : 'not found' 82 | }) is < 50 or Expo not found. ${expoConfigPath} does not exist, no action needed.`, 83 | ); 84 | } 85 | } else { 86 | console.log( 87 | `Expo version (${majorVersion}) is >= 50. Kept ${expoConfigPath}`, 88 | ); 89 | } 90 | } 91 | 92 | checkAndCleanExpoConfig(); 93 | -------------------------------------------------------------------------------- /android/src/main/java/cn/reactnative/modules/update/SafeZipFile.java: -------------------------------------------------------------------------------- 1 | package cn.reactnative.modules.update; 2 | 3 | import android.util.Log; 4 | 5 | import java.io.BufferedInputStream; 6 | import java.io.BufferedOutputStream; 7 | import java.io.File; 8 | import java.io.FileOutputStream; 9 | import java.io.IOException; 10 | import java.io.InputStream; 11 | import java.util.Enumeration; 12 | import java.util.zip.ZipEntry; 13 | import java.util.zip.ZipFile; 14 | 15 | 16 | public class SafeZipFile extends ZipFile { 17 | 18 | public SafeZipFile(File file) throws IOException { 19 | super(file); 20 | } 21 | 22 | private static final int BUFFER_SIZE = 8192; 23 | 24 | @Override 25 | public Enumeration entries() { 26 | return new SafeZipEntryIterator(super.entries()); 27 | } 28 | 29 | private static class SafeZipEntryIterator implements Enumeration { 30 | 31 | final private Enumeration delegate; 32 | 33 | private SafeZipEntryIterator(Enumeration delegate) { 34 | this.delegate = delegate; 35 | } 36 | 37 | @Override 38 | public boolean hasMoreElements() { 39 | return delegate.hasMoreElements(); 40 | } 41 | 42 | @Override 43 | public ZipEntry nextElement() { 44 | ZipEntry entry = delegate.nextElement(); 45 | if (null != entry) { 46 | String name = entry.getName(); 47 | /** 48 | * avoid ZipperDown 49 | */ 50 | if (null != name && (name.contains("../") || name.contains("..\\"))) { 51 | throw new SecurityException("illegal entry: " + name); 52 | } 53 | } 54 | return entry; 55 | } 56 | } 57 | 58 | public void unzipToPath(ZipEntry ze, File targetPath) throws IOException { 59 | String name = ze.getName(); 60 | File target = new File(targetPath, name); 61 | 62 | // Fixing a Zip Path Traversal Vulnerability 63 | // https://support.google.com/faqs/answer/9294009 64 | String canonicalPath = target.getCanonicalPath(); 65 | if (!canonicalPath.startsWith(targetPath.getCanonicalPath() + File.separator)) { 66 | throw new SecurityException("Illegal name: " + name); 67 | } 68 | 69 | 70 | Log.d("react-native-update", "Unzipping " + name); 71 | 72 | if (ze.isDirectory()) { 73 | target.mkdirs(); 74 | return; 75 | } 76 | unzipToFile(ze, target); 77 | } 78 | 79 | public void unzipToFile(ZipEntry ze, File target) throws IOException { 80 | try (InputStream inputStream = getInputStream(ze)) { 81 | try (BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(target)); 82 | BufferedInputStream input = new BufferedInputStream(inputStream)) { 83 | byte[] buffer = new byte[BUFFER_SIZE]; 84 | int n; 85 | while ((n = input.read(buffer, 0, BUFFER_SIZE)) >= 0) { 86 | output.write(buffer, 0, n); 87 | } 88 | } 89 | } 90 | } 91 | 92 | } -------------------------------------------------------------------------------- /src/core.ts: -------------------------------------------------------------------------------- 1 | import { NativeEventEmitter, NativeModules, Platform } from 'react-native'; 2 | import { emptyModule, log } from './utils'; 3 | const { 4 | version: v, 5 | } = require('react-native/Libraries/Core/ReactNativeVersion'); 6 | const RNVersion = `${v.major}.${v.minor}.${v.patch}`; 7 | const isTurboModuleEnabled = 8 | // https://github.com/facebook/react-native/pull/48362 9 | (global as any).__turboModuleProxy || (global as any).RN$Bridgeless; 10 | 11 | export const PushyModule = 12 | Platform.OS === 'web' 13 | ? emptyModule 14 | : isTurboModuleEnabled 15 | ? require('./NativePushy').default 16 | : NativeModules.Pushy; 17 | 18 | export const UpdateModule = PushyModule; 19 | 20 | if (!PushyModule) { 21 | throw Error( 22 | 'Failed to load react-native-update native module, please try to recompile', 23 | ); 24 | } 25 | 26 | const PushyConstants = isTurboModuleEnabled 27 | ? PushyModule.getConstants() 28 | : PushyModule; 29 | 30 | export const downloadRootDir: string = PushyConstants.downloadRootDir; 31 | export const packageVersion: string = PushyConstants.packageVersion; 32 | export const currentVersion: string = PushyConstants.currentVersion; 33 | 34 | export function setLocalHashInfo(hash: string, info: Record) { 35 | PushyModule.setLocalHashInfo(hash, JSON.stringify(info)); 36 | } 37 | 38 | const currentVersionInfoString: string = PushyConstants.currentVersionInfo; 39 | let _currentVersionInfo: Record = {}; 40 | let isDebugChannel = false; 41 | if (currentVersionInfoString) { 42 | try { 43 | _currentVersionInfo = JSON.parse(currentVersionInfoString); 44 | if (_currentVersionInfo.debugChannel) { 45 | isDebugChannel = true; 46 | delete _currentVersionInfo.debugChannel; 47 | setLocalHashInfo(currentVersion, _currentVersionInfo); 48 | } 49 | } catch (error) { 50 | console.error( 51 | 'Failed to parse currentVersionInfo:', 52 | currentVersionInfoString, 53 | ); 54 | } 55 | } 56 | export const currentVersionInfo = _currentVersionInfo; 57 | 58 | export const isFirstTime: boolean = PushyConstants.isFirstTime; 59 | export const isFirstTimeDebug: boolean = isFirstTime && isDebugChannel; 60 | export const rolledBackVersion: string = PushyConstants.rolledBackVersion; 61 | export const isRolledBack: boolean = !!rolledBackVersion; 62 | 63 | export const buildTime: string = PushyConstants.buildTime; 64 | let uuid = PushyConstants.uuid; 65 | 66 | 67 | async function getLocalHashInfo(hash: string) { 68 | return JSON.parse(await PushyModule.getLocalHashInfo(hash)); 69 | } 70 | 71 | // @deprecated use currentVersionInfo instead 72 | export async function getCurrentVersionInfo(): Promise<{ 73 | name?: string; 74 | description?: string; 75 | metaInfo?: string; 76 | }> { 77 | return currentVersion ? (await getLocalHashInfo(currentVersion)) || {} : {}; 78 | } 79 | 80 | export const pushyNativeEventEmitter = new NativeEventEmitter(PushyModule); 81 | 82 | if (!uuid) { 83 | uuid = require('nanoid/non-secure').nanoid(); 84 | PushyModule.setUuid(uuid); 85 | } 86 | 87 | log('uuid: ' + uuid); 88 | 89 | export const cInfo = { 90 | rnu: require('../package.json').version, 91 | rn: RNVersion, 92 | os: Platform.OS + ' ' + Platform.Version, 93 | uuid, 94 | }; 95 | -------------------------------------------------------------------------------- /Example/harmony_use_pushy/android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | --------------------------------------------------------------------------------