mirror of
				https://gitcode.com/gh_mirrors/re/react-native-pushy.git
				synced 2025-10-31 13:23:12 +08:00 
			
		
		
		
	react-native-pushy适配harmony (#461)
* init * update * udpate * update * update * update * add pushy build time logic
This commit is contained in:
		
							
								
								
									
										17
									
								
								harmony/BuildProfile.ets
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								harmony/BuildProfile.ets
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| /** | ||||
|  * Use these variables when you tailor your ArkTS code. They must be of the const type. | ||||
|  */ | ||||
| export const HAR_VERSION = '3.1.0-0.0.7'; | ||||
| export const BUILD_MODE_NAME = 'debug'; | ||||
| export const DEBUG = true; | ||||
| export const TARGET_NAME = 'default'; | ||||
|  | ||||
| /** | ||||
|  * BuildProfile Class is used only for compatibility purposes. | ||||
|  */ | ||||
| export default class BuildProfile {  | ||||
| 	static readonly HAR_VERSION = HAR_VERSION; | ||||
| 	static readonly BUILD_MODE_NAME = BUILD_MODE_NAME; | ||||
| 	static readonly DEBUG = DEBUG; | ||||
| 	static readonly TARGET_NAME = TARGET_NAME; | ||||
| } | ||||
							
								
								
									
										38
									
								
								harmony/OAT.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								harmony/OAT.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <configuration> | ||||
| 	<oatconfig> | ||||
| 	<licensefile>LICENSE</licensefile> | ||||
| 		<filefilterlist> | ||||
| 			<filefilter name="copyrightPolicyFilter" desc="Filters for compatibility,license header policies"> | ||||
| 				<filteritem type="filename" name="hvigorfile.ts" desc="hvigor构建脚本,DevEco Studio自动生成,不需要添加版权头"/> | ||||
| 				<filteritem type="filename" name="*.json5" desc="hvigor工程配置文件,DevEco Studio自动生成,不需要添加版权头"/> | ||||
| 				<filteritem type="filename" name="*.proto" desc="资源文件,不需要添加版权头"/> | ||||
| 				<filteritem type="filename" name="*.json" desc="资源文件,不需要添加版权头"/> | ||||
| 				<filteritem type="filepath" name="hvigorw" desc="工程模板,不修改版权头,以防有修改版权风险"/> | ||||
| 				<filteritem type="filepath" name="hvigorw.bat" desc="工程模板,不修改版权头,以防有修改版权风险"/> | ||||
| 				<filteritem type="filepath" name="hvigor/hvigor-wrapper.js" desc="工程模板,不修改版权头,以防有修改版权风险"/> | ||||
|                 <filteritem type="filename" name="LICENSE" desc="工程文件,不修改版权头"/> | ||||
| 			</filefilter> | ||||
| 			<filefilter name="defaultPolicyFilter" desc="Filters for compatibility,license header policies"> | ||||
| 				<filteritem type="filename" name="hvigorfile.ts" desc="hvigor构建脚本,DevEco Studio自动生成,不需要添加许可证头"/> | ||||
| 				<filteritem type="filename" name="*.json5" desc="hvigor工程配置文件,DevEco Studio自动生成,不需要添加许可证头"/> | ||||
| 				<filteritem type="filename" name="LICENSE" desc="原三方库证书文件无需更改,因此添加过滤"/> | ||||
| 				<filteritem type="filename" name="*.proto" desc="资源文件,不需要添加许可证头"/> | ||||
| 				<filteritem type="filename" name="*.json" desc="资源文件,不需要添加许可证头"/> | ||||
| 				<filteritem type="filepath" name="hvigorw" desc="工程模板,不修改版权头,以防有修改版权风险"/> | ||||
| 				<filteritem type="filepath" name="hvigorw.bat" desc="工程模板,不修改版权头,以防有修改版权风险"/> | ||||
| 				<filteritem type="filepath" name="hvigor/hvigor-wrapper.js" desc="工程模板,不修改版权头,以防有修改版权风险"/> | ||||
| 			</filefilter> | ||||
| 			<filefilter name="binaryFileTypePolicyFilter" desc="Filters for resources files policies"> | ||||
| 				<filteritem type="filename" name="icon.png" desc="应用图标"/> | ||||
| 				<filteritem type="filename" name="app_icon.png" desc="应用图标"/> | ||||
| 				<filteritem type="filename" name="warn.png" desc="页面展示图标"/> | ||||
| 			</filefilter> | ||||
| 		</filefilterlist> | ||||
|         <policylist> | ||||
|             <policy name="projectPolicy" desc=""> | ||||
|                 <policyitem type="license" name="MIT" path="*.*" desc="license under the MIT"/> | ||||
|             </policy> | ||||
|         </policylist> | ||||
| 	</oatconfig> | ||||
| </configuration> | ||||
							
								
								
									
										11
									
								
								harmony/README.OpenSource
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								harmony/README.OpenSource
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| [ | ||||
|     { | ||||
|         "Name": "react-native-netinfo", | ||||
|         "License": "MIT License", | ||||
|         "License File": "https://github.com/react-native-netinfo/react-native-netinfo/blob/master/LICENSE", | ||||
|         "Version Number": "11.1.0", | ||||
|         "Owner" : "Matt Oakes <hello@mattoakes.net>" | ||||
|         "Upstream URL": "https://github.com/react-native-netinfo/react-native-netinfo", | ||||
|         "Description": "React Native Network Info API for Android, iOS, macOS, Windows & Web. It allows you to get information on:Connection type,Connection quality" | ||||
|     } | ||||
| ] | ||||
							
								
								
									
										0
									
								
								harmony/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								harmony/README.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										8
									
								
								harmony/build-profile.json5
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								harmony/build-profile.json5
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| { | ||||
|   "apiType": "stageMode", | ||||
|   "targets": [ | ||||
|     { | ||||
|       "name": "default", | ||||
|     } | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										1
									
								
								harmony/hvigorfile.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								harmony/hvigorfile.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| export { harTasks } from '@ohos/hvigor-ohos-plugin'; | ||||
							
								
								
									
										2
									
								
								harmony/index.ets
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								harmony/index.ets
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
|  | ||||
| export * from './ts' | ||||
							
								
								
									
										18
									
								
								harmony/oh-package-lock.json5
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								harmony/oh-package-lock.json5
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| { | ||||
|   "meta": { | ||||
|     "stableOrder": true | ||||
|   }, | ||||
|   "lockfileVersion": 3, | ||||
|   "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.", | ||||
|   "specifiers": { | ||||
|     "rnoh@../../../libs/rnoh": "rnoh@../../../libs/rnoh" | ||||
|   }, | ||||
|   "packages": { | ||||
|     "rnoh@../../../libs/rnoh": { | ||||
|       "name": "rnoh", | ||||
|       "version": "0.72.12", | ||||
|       "resolved": "../../../libs/rnoh", | ||||
|       "registryType": "local" | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										12
									
								
								harmony/oh-package.json5
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								harmony/oh-package.json5
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| { | ||||
|   "license": "ISC", | ||||
|   "types": "", | ||||
|   "devDependencies": {}, | ||||
|   "name": "pushy", | ||||
|   "description": "", | ||||
|   "main": "index.ets", | ||||
|   "version": "3.1.0-0.0.7", | ||||
|   "dependencies": { | ||||
|     "rnoh": "file:../../../libs/rnoh" | ||||
|   } | ||||
| } | ||||
							
								
								
									
										36
									
								
								harmony/src/main/cpp/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								harmony/src/main/cpp/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | ||||
| cmake_minimum_required(VERSION 3.13) | ||||
| project(rnupdate) | ||||
|  | ||||
| set(HDIFFPATCH_DIR ${CMAKE_CURRENT_SOURCE_DIR}/HDiffPatch) | ||||
| set(LZMA_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lzma) | ||||
| set(HDP_SOURCES | ||||
|      ${CMAKE_CURRENT_SOURCE_DIR}/pushy.c | ||||
|      ${CMAKE_CURRENT_SOURCE_DIR}/hpatch.c | ||||
|      ${HDIFFPATCH_DIR}/libHDiffPatch/HPatch/patch.c | ||||
|      ${HDIFFPATCH_DIR}/file_for_patch.c | ||||
|      ${LZMA_DIR}/C/LzmaDec.c | ||||
|      ${LZMA_DIR}/C/Lzma2Dec.c | ||||
| ) | ||||
| set(CMAKE_VERBOSE_MAKEFILE on) | ||||
|  | ||||
|  | ||||
| add_library(rnupdate SHARED | ||||
|     ${HDP_SOURCES} | ||||
| ) | ||||
|  | ||||
| target_include_directories(rnupdate PRIVATE | ||||
|      ${CMAKE_CURRENT_SOURCE_DIR} | ||||
|      ${HDIFFPATCH_DIR} | ||||
|      ${HDIFFPATCH_DIR}/libHDiffPatch/HPatch | ||||
|      ${LZMA_DIR}/C | ||||
| ) | ||||
|  | ||||
| target_link_libraries(rnupdate PUBLIC | ||||
|     libace_napi.z.so | ||||
| ) | ||||
|  | ||||
| file(GLOB rnoh_pushy_SRC CONFIGURE_DEPENDS *.cpp) | ||||
| add_library(rnoh_pushy SHARED ${rnoh_pushy_SRC}) | ||||
| target_include_directories(rnoh_pushy PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) | ||||
| target_link_libraries(rnoh_pushy PUBLIC rnoh) | ||||
|  | ||||
							
								
								
									
										1
									
								
								harmony/src/main/cpp/HDiffPatch
									
									
									
									
									
										Submodule
									
								
							
							
								
								
								
								
								
							
						
						
									
										1
									
								
								harmony/src/main/cpp/HDiffPatch
									
									
									
									
									
										Submodule
									
								
							 Submodule harmony/src/main/cpp/HDiffPatch added at eb959d671c
									
								
							
							
								
								
									
										55
									
								
								harmony/src/main/cpp/PushyPackage.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								harmony/src/main/cpp/PushyPackage.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | ||||
| /** | ||||
|  * MIT License | ||||
|  * | ||||
|  * Copyright (C) 2023 Huawei Device Co., Ltd. | ||||
|  * | ||||
|  * 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: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all | ||||
|  * copies or substantial portions of the Software. | ||||
|  * | ||||
|  * 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. | ||||
|  */ | ||||
|  | ||||
| #ifndef GEO_LOCATION_PACKAGE_H | ||||
| #define GEO_LOCATION_PACKAGE_H | ||||
|  | ||||
| #include "RNOH/Package.h" | ||||
| #include "PushyTurboModule.h" | ||||
|  | ||||
| using namespace rnoh; | ||||
| using namespace facebook; | ||||
|  | ||||
| class PushyTurboModuleFactoryDelegate : public TurboModuleFactoryDelegate { | ||||
| public: | ||||
|     SharedTurboModule createTurboModule(Context ctx, const std::string &name) const override | ||||
|     { | ||||
|         if (name == "Pushy") { | ||||
|         return std::make_shared<PushyTurboModule>(ctx, name); | ||||
|         } | ||||
|         return nullptr; | ||||
|     }; | ||||
| }; | ||||
|  | ||||
| namespace rnoh { | ||||
| class PushyPackage : public Package { | ||||
| public: | ||||
|     PushyPackage(Package::Context ctx) : Package(ctx){} | ||||
|     std::unique_ptr<TurboModuleFactoryDelegate> createTurboModuleFactoryDelegate() override | ||||
|     { | ||||
|     return std::make_unique<PushyTurboModuleFactoryDelegate>(); | ||||
|     } | ||||
| }; | ||||
| } // namespace rnoh | ||||
| #endif | ||||
							
								
								
									
										142
									
								
								harmony/src/main/cpp/PushyTurboModule.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								harmony/src/main/cpp/PushyTurboModule.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,142 @@ | ||||
|  | ||||
| #include "PushyTurboModule.h" | ||||
|  | ||||
| using namespace rnoh; | ||||
| using namespace facebook; | ||||
|  | ||||
| static jsi::Value _hostFunction_PushyTurboModule_getConstants( | ||||
|     jsi::Runtime &rt, | ||||
|     react::TurboModule & turboModule, | ||||
|     const jsi::Value* args, | ||||
|     size_t count) | ||||
|     { | ||||
|         return jsi::Value(static_cast<ArkTSTurboModule &> (turboModule).call(rt,"getConstants", args, count)); | ||||
|     } | ||||
|  | ||||
| static jsi::Value _hostFunction_PushyTurboModule_setLocalHashInfo( | ||||
|     jsi::Runtime &rt, | ||||
|     react::TurboModule & turboModule, | ||||
|     const jsi::Value* args, | ||||
|     size_t count) | ||||
|     { | ||||
|         return jsi::Value(static_cast<ArkTSTurboModule &> (turboModule).call(rt,"setLocalHashInfo", args, count)); | ||||
|     } | ||||
|      | ||||
| static jsi::Value _hostFunction_PushyTurboModule_getLocalHashInfo( | ||||
|     jsi::Runtime &rt, | ||||
|     react::TurboModule & turboModule, | ||||
|     const jsi::Value* args, | ||||
|     size_t count) | ||||
|     { | ||||
|         return jsi::Value(static_cast<ArkTSTurboModule &> (turboModule).call(rt,"getLocalHashInfo", args, count)); | ||||
|     } | ||||
|  | ||||
| static jsi::Value _hostFunction_PushyTurboModule_setUuid( | ||||
|     jsi::Runtime &rt, | ||||
|     react::TurboModule & turboModule, | ||||
|     const jsi::Value* args, | ||||
|     size_t count) | ||||
|     { | ||||
|         return jsi::Value(static_cast<ArkTSTurboModule &> (turboModule).call(rt,"setUuid", args, count)); | ||||
|     } | ||||
|  | ||||
| static jsi::Value _hostFunction_PushyTurboModule_reloadUpdate( | ||||
|     jsi::Runtime &rt, | ||||
|     react::TurboModule & turboModule, | ||||
|     const jsi::Value* args, | ||||
|     size_t count) | ||||
|     { | ||||
|         return jsi::Value(static_cast<ArkTSTurboModule &> (turboModule).call(rt,"reloadUpdate", args, count)); | ||||
|     } | ||||
|  | ||||
| static jsi::Value _hostFunction_PushyTurboModule_setNeedUpdate( | ||||
|     jsi::Runtime &rt, | ||||
|     react::TurboModule & turboModule, | ||||
|     const jsi::Value* args, | ||||
|     size_t count) | ||||
|     { | ||||
|         return jsi::Value(static_cast<ArkTSTurboModule &> (turboModule).call(rt,"setNeedUpdate", args, count)); | ||||
|     } | ||||
|  | ||||
| static jsi::Value _hostFunction_PushyTurboModule_markSuccess( | ||||
|     jsi::Runtime &rt, | ||||
|     react::TurboModule & turboModule, | ||||
|     const jsi::Value* args, | ||||
|     size_t count) | ||||
|     { | ||||
|         return jsi::Value(static_cast<ArkTSTurboModule &> (turboModule).call(rt,"markSuccess", args, count)); | ||||
|     } | ||||
|  | ||||
| static jsi::Value _hostFunction_PushyTurboModule_downloadPatchFromPpk( | ||||
|     jsi::Runtime &rt, | ||||
|     react::TurboModule & turboModule, | ||||
|     const jsi::Value* args, | ||||
|     size_t count) | ||||
|     { | ||||
|         return jsi::Value(static_cast<ArkTSTurboModule &> (turboModule).call(rt,"downloadPatchFromPpk", args, count)); | ||||
|     } | ||||
|  | ||||
| static jsi::Value _hostFunction_PushyTurboModule_downloadPatchFromPackage( | ||||
|     jsi::Runtime &rt, | ||||
|     react::TurboModule & turboModule, | ||||
|     const jsi::Value* args, | ||||
|     size_t count) | ||||
|     { | ||||
|         return jsi::Value(static_cast<ArkTSTurboModule &> (turboModule).call(rt,"downloadPatchFromPackage", args, count)); | ||||
|     } | ||||
|  | ||||
| static jsi::Value _hostFunction_PushyTurboModule_downloadFullUpdate( | ||||
|     jsi::Runtime &rt, | ||||
|     react::TurboModule & turboModule, | ||||
|     const jsi::Value* args, | ||||
|     size_t count) | ||||
|     { | ||||
|         return jsi::Value(static_cast<ArkTSTurboModule &> (turboModule).call(rt,"downloadFullUpdate", args, count)); | ||||
|     } | ||||
|  | ||||
| static jsi::Value _hostFunction_PushyTurboModule_downloadAndInstallApk( | ||||
|     jsi::Runtime &rt, | ||||
|     react::TurboModule & turboModule, | ||||
|     const jsi::Value* args, | ||||
|     size_t count) | ||||
|     { | ||||
|         return jsi::Value(static_cast<ArkTSTurboModule &> (turboModule).call(rt,"downloadAndInstallApk", args, count)); | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
| static jsi::Value _hostFunction_PushyTurboModule_addListener( | ||||
|     jsi::Runtime &rt, | ||||
|     react::TurboModule & turboModule, | ||||
|     const jsi::Value* args, | ||||
|     size_t count) | ||||
|     { | ||||
|         return jsi::Value(static_cast<ArkTSTurboModule &> (turboModule).call(rt,"addListener", args, count)); | ||||
|     } | ||||
|  | ||||
| static jsi::Value _hostFunction_PushyTurboModule_removeListeners( | ||||
|     jsi::Runtime &rt, | ||||
|     react::TurboModule & turboModule, | ||||
|     const jsi::Value* args, | ||||
|     size_t count) | ||||
|     { | ||||
|         return jsi::Value(static_cast<ArkTSTurboModule &> (turboModule).call(rt,"removeListeners", args, count)); | ||||
|     } | ||||
|  | ||||
| PushyTurboModule::PushyTurboModule(const ArkTSTurboModule::Context ctx, const std::string name) | ||||
|     : ArkTSTurboModule(ctx,name) | ||||
| { | ||||
|     methodMap_["getConstants"]= MethodMetadata{1, _hostFunction_PushyTurboModule_getConstants}; | ||||
|     methodMap_["setLocalHashInfo"]= MethodMetadata{2, _hostFunction_PushyTurboModule_setLocalHashInfo}; | ||||
|     methodMap_["getLocalHashInfo"]= MethodMetadata{3, _hostFunction_PushyTurboModule_getLocalHashInfo}; | ||||
|     methodMap_["setUuid"]= MethodMetadata{1, _hostFunction_PushyTurboModule_setUuid}; | ||||
|     methodMap_["reloadUpdate"]= MethodMetadata{0, _hostFunction_PushyTurboModule_reloadUpdate}; | ||||
|     methodMap_["setNeedUpdate"]= MethodMetadata{0, _hostFunction_PushyTurboModule_setNeedUpdate}; | ||||
|     methodMap_["markSuccess"]= MethodMetadata{0, _hostFunction_PushyTurboModule_markSuccess}; | ||||
|     methodMap_["downloadPatchFromPpk"]= MethodMetadata{0, _hostFunction_PushyTurboModule_downloadPatchFromPpk}; | ||||
|     methodMap_["downloadPatchFromPackage"]= MethodMetadata{0, _hostFunction_PushyTurboModule_downloadPatchFromPackage}; | ||||
|     methodMap_["downloadFullUpdate"]= MethodMetadata{0, _hostFunction_PushyTurboModule_downloadFullUpdate}; | ||||
|     methodMap_["downloadAndInstallApk"]= MethodMetadata{0, _hostFunction_PushyTurboModule_downloadAndInstallApk}; | ||||
|     methodMap_["addListener"]= MethodMetadata{1, _hostFunction_PushyTurboModule_addListener}; | ||||
|     methodMap_["removeListeners"]= MethodMetadata{1, _hostFunction_PushyTurboModule_removeListeners}; | ||||
| } | ||||
							
								
								
									
										38
									
								
								harmony/src/main/cpp/PushyTurboModule.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								harmony/src/main/cpp/PushyTurboModule.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
| /** | ||||
|  * MIT License | ||||
|  * | ||||
|  * Copyright (C) 2023 Huawei Device Co., Ltd. | ||||
|  * | ||||
|  * 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: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all | ||||
|  * copies or substantial portions of the Software. | ||||
|  * | ||||
|  * 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. | ||||
|  */ | ||||
|  | ||||
| #ifndef GEO_LOCATION_TURBOMODULE_H | ||||
| #define GEO_LOCATION_TURBOMODULE_H | ||||
|  | ||||
| #include <ReactCommon/TurboModule.h> | ||||
| #include "RNOH/ArkTSTurboModule.h" | ||||
|  | ||||
| namespace rnoh { | ||||
| class JSI_EXPORT PushyTurboModule : public ArkTSTurboModule { | ||||
|     public: | ||||
|         PushyTurboModule(const ArkTSTurboModule::Context ctx, const std::string name); | ||||
| }; | ||||
| } // namespace rnoh | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										137
									
								
								harmony/src/main/cpp/hpatch.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								harmony/src/main/cpp/hpatch.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,137 @@ | ||||
| // hpatch.c | ||||
| // Copyright 2021 housisong, All rights reserved | ||||
| #include "hpatch.h" | ||||
| #include "HDiffPatch/libHDiffPatch/HPatch/patch.h" | ||||
| #include "HDiffPatch/file_for_patch.h" | ||||
|  | ||||
| //#define _CompressPlugin_zlib | ||||
| //#define _CompressPlugin_bz2 | ||||
| #define _CompressPlugin_lzma | ||||
| #define _CompressPlugin_lzma2 | ||||
| #define _IsNeedIncludeDefaultCompressHead 0 | ||||
| #include "lzma/C/LzmaDec.h" | ||||
| #include "lzma/C/Lzma2Dec.h" | ||||
| #include "HDiffPatch/decompress_plugin_demo.h" | ||||
|  | ||||
| #define kMaxLoadMemOldSize ((1<<20)*8) | ||||
|  | ||||
| #define  _check(v,errorType) do{ \ | ||||
|     if (!(v)){ if (result==kHPatch_ok) result=errorType; if (!_isInClear){ goto _clear; }; } }while(0) | ||||
|  | ||||
| int hpatch_getInfo_by_mem(hpatch_singleCompressedDiffInfo* out_patinfo, | ||||
|                           const uint8_t* pat,size_t patsize){ | ||||
|     hpatch_TStreamInput patStream; | ||||
|     mem_as_hStreamInput(&patStream,pat,pat+patsize); | ||||
|     if (!getSingleCompressedDiffInfo(out_patinfo,&patStream,0)) | ||||
|         return kHPatch_error_info;//data error; | ||||
|     return kHPatch_ok; //ok | ||||
| } | ||||
|  | ||||
| static hpatch_TDecompress* getDecompressPlugin(const char* compressType){ | ||||
| #ifdef  _CompressPlugin_zlib | ||||
|     if (zlibDecompressPlugin.is_can_open(compressType)) | ||||
|         return &zlibDecompressPlugin; | ||||
| #endif | ||||
| #ifdef  _CompressPlugin_bz2 | ||||
|     if (bz2DecompressPlugin.is_can_open(compressType)) | ||||
|         return &bz2DecompressPlugin; | ||||
| #endif | ||||
| #ifdef  _CompressPlugin_lzma | ||||
|     if (lzmaDecompressPlugin.is_can_open(compressType)) | ||||
|         return &lzmaDecompressPlugin; | ||||
| #endif | ||||
| #ifdef  _CompressPlugin_lzma2 | ||||
|     if (lzma2DecompressPlugin.is_can_open(compressType)) | ||||
|         return &lzma2DecompressPlugin; | ||||
| #endif | ||||
|     return 0; | ||||
| } | ||||
| static int hpatch_by_stream(const hpatch_TStreamInput* old,hpatch_BOOL isLoadOldAllToMem,const hpatch_TStreamInput* pat, | ||||
|                             hpatch_TStreamOutput* out_new,const hpatch_singleCompressedDiffInfo* patInfo){ | ||||
|     int     result=kHPatch_ok; | ||||
|     int     _isInClear=hpatch_FALSE; | ||||
|     hpatch_TDecompress* decompressPlugin=0; | ||||
|     uint8_t* temp_cache=0; | ||||
|     size_t temp_cache_size; | ||||
|     hpatch_singleCompressedDiffInfo _patinfo; | ||||
|     hpatch_TStreamInput _old; | ||||
|     {// info | ||||
|         if (!patInfo){ | ||||
|             _check(getSingleCompressedDiffInfo(&_patinfo,pat,0),kHPatch_error_info); | ||||
|             patInfo=&_patinfo; | ||||
|         } | ||||
|         _check(old->streamSize==patInfo->oldDataSize,kHPatch_error_old_size); | ||||
|         _check(out_new->streamSize>=patInfo->newDataSize,kHPatch_error_new_size); | ||||
|         out_new->streamSize=patInfo->newDataSize; | ||||
|         if (strlen(patInfo->compressType)>0){ | ||||
|             decompressPlugin=getDecompressPlugin(patInfo->compressType); | ||||
|             _check(decompressPlugin,kHPatch_error_compressType); | ||||
|         } | ||||
|     } | ||||
|     {// mem | ||||
|         size_t mem_size; | ||||
|         size_t oldSize=(size_t)old->streamSize; | ||||
|         isLoadOldAllToMem=isLoadOldAllToMem&&(old->streamSize<=kMaxLoadMemOldSize); | ||||
|         temp_cache_size=patInfo->stepMemSize+hpatch_kFileIOBufBetterSize*3; | ||||
|         mem_size=temp_cache_size+(isLoadOldAllToMem?oldSize:0); | ||||
|         temp_cache=malloc(mem_size); | ||||
|         _check(temp_cache,kHPatch_error_malloc); | ||||
|         if (isLoadOldAllToMem){//load old to mem | ||||
|             uint8_t* oldMem=temp_cache+temp_cache_size; | ||||
|             _check(old->read(old,0,oldMem,oldMem+oldSize),kHPatch_error_old_fread); | ||||
|             mem_as_hStreamInput(&_old,oldMem,oldMem+oldSize); | ||||
|             old=&_old; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     _check(patch_single_compressed_diff(out_new,old,pat,patInfo->diffDataPos, | ||||
|                patInfo->uncompressedSize,decompressPlugin,patInfo->coverCount, | ||||
|                patInfo->stepMemSize,temp_cache,temp_cache+temp_cache_size),kHPatch_error_patch); | ||||
|  | ||||
| _clear: | ||||
|     _isInClear=hpatch_TRUE; | ||||
|     if (temp_cache){ free(temp_cache); temp_cache=0; } | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| int hpatch_by_mem(const uint8_t* old,size_t oldsize,uint8_t* newBuf,size_t newsize, | ||||
|                   const uint8_t* pat,size_t patsize,const hpatch_singleCompressedDiffInfo* patInfo){ | ||||
|     hpatch_TStreamInput oldStream; | ||||
|     hpatch_TStreamInput patStream; | ||||
|     hpatch_TStreamOutput newStream; | ||||
|     mem_as_hStreamInput(&oldStream,old,old+oldsize); | ||||
|     mem_as_hStreamInput(&patStream,pat,pat+patsize); | ||||
|     mem_as_hStreamOutput(&newStream,newBuf,newBuf+newsize); | ||||
|     return hpatch_by_stream(&oldStream,hpatch_FALSE,&patStream,&newStream,patInfo); | ||||
| } | ||||
|  | ||||
| int hpatch_by_file(const char* oldfile, const char* newfile, const char* patchfile){ | ||||
|     int     result=kHPatch_ok; | ||||
|     int     _isInClear=hpatch_FALSE; | ||||
|     int     patch_result; | ||||
|     hpatch_TFileStreamInput oldStream; | ||||
|     hpatch_TFileStreamInput patStream; | ||||
|     hpatch_TFileStreamOutput newStream; | ||||
|     hpatch_TFileStreamInput_init(&oldStream); | ||||
|     hpatch_TFileStreamInput_init(&patStream); | ||||
|     hpatch_TFileStreamOutput_init(&newStream); | ||||
|  | ||||
|     _check(hpatch_TFileStreamInput_open(&oldStream,oldfile),kHPatch_error_old_fopen); | ||||
|     _check(hpatch_TFileStreamInput_open(&patStream,patchfile),kHPatch_error_pat_fopen); | ||||
|     _check(hpatch_TFileStreamOutput_open(&newStream,newfile,~(hpatch_StreamPos_t)0),kHPatch_error_new_fopen); | ||||
|  | ||||
|     patch_result=hpatch_by_stream(&oldStream.base,hpatch_TRUE,&patStream.base,&newStream.base,0); | ||||
|     if (patch_result!=kHPatch_ok){ | ||||
|         _check(!oldStream.fileError,kHPatch_error_old_fread); | ||||
|         _check(!patStream.fileError,kHPatch_error_pat_fread); | ||||
|         _check(!newStream.fileError,kHPatch_error_new_fwrite); | ||||
|         _check(hpatch_FALSE,patch_result); | ||||
|     } | ||||
|  | ||||
| _clear: | ||||
|     _isInClear=hpatch_TRUE; | ||||
|     _check(hpatch_TFileStreamInput_close(&oldStream),kHPatch_error_old_fclose); | ||||
|     _check(hpatch_TFileStreamInput_close(&patStream),kHPatch_error_pat_fclose); | ||||
|     _check(hpatch_TFileStreamOutput_close(&newStream),kHPatch_error_new_fclose); | ||||
|     return result; | ||||
| } | ||||
							
								
								
									
										44
									
								
								harmony/src/main/cpp/hpatch.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								harmony/src/main/cpp/hpatch.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | ||||
| // hpatch.h | ||||
| // import HDiffPatch, support patchData created by "hdiffz -SD -c-lzma2 oldfile newfile patchfile" | ||||
| // Copyright 2021 housisong, All rights reserved | ||||
|  | ||||
| #ifndef HDIFFPATCH_PATCH_H | ||||
| #define HDIFFPATCH_PATCH_H | ||||
| # include <stdint.h> //for uint8_t | ||||
| #include "HDiffPatch/libHDiffPatch/HPatch/patch_types.h" //for hpatch_singleCompressedDiffInfo | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
|  | ||||
| //result | ||||
| enum { | ||||
|     kHPatch_ok                  = 0, | ||||
|     kHPatch_error_malloc        =-1, | ||||
|     kHPatch_error_info          =-2, | ||||
|     kHPatch_error_compressType  =-3, | ||||
|     kHPatch_error_patch         =-4, | ||||
|     kHPatch_error_old_fopen     =-5, | ||||
|     kHPatch_error_old_fread     =-6, | ||||
|     kHPatch_error_old_fclose    =-7, | ||||
|     kHPatch_error_pat_fopen     =-8, | ||||
|     kHPatch_error_pat_fread     =-9, | ||||
|     kHPatch_error_pat_fclose    =-10, | ||||
|     kHPatch_error_new_fopen     =-11, | ||||
|     kHPatch_error_new_fwrite    =-12, | ||||
|     kHPatch_error_new_fclose    =-13, | ||||
|     kHPatch_error_old_size      =-14, | ||||
|     kHPatch_error_new_size      =-15, | ||||
| }; | ||||
|  | ||||
| int hpatch_getInfo_by_mem(hpatch_singleCompressedDiffInfo* out_patinfo, | ||||
|                           const uint8_t* pat,size_t patsize); | ||||
|  | ||||
| //patInfo can NULL | ||||
| int hpatch_by_mem(const uint8_t* old,size_t oldsize, uint8_t* newBuf,size_t newsize, | ||||
|                   const uint8_t* pat,size_t patsize,const hpatch_singleCompressedDiffInfo* patInfo); | ||||
| int hpatch_by_file(const char* oldfile, const char* newfile, const char* patchfile); | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
| #endif //HDIFFPATCH_PATCH_H | ||||
							
								
								
									
										1
									
								
								harmony/src/main/cpp/lzma
									
									
									
									
									
										Submodule
									
								
							
							
								
								
								
								
								
							
						
						
									
										1
									
								
								harmony/src/main/cpp/lzma
									
									
									
									
									
										Submodule
									
								
							 Submodule harmony/src/main/cpp/lzma added at c80bb80bbb
									
								
							
							
								
								
									
										115
									
								
								harmony/src/main/cpp/pushy.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								harmony/src/main/cpp/pushy.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,115 @@ | ||||
| #include "pushy.h" | ||||
| #include "hpatch.h" | ||||
| #include <napi/native_api.h> | ||||
| #include <js_native_api.h> | ||||
| #include <js_native_api_types.h> | ||||
|  | ||||
| #define _check(v,errInfo) do { \ | ||||
|     if (!(v)) { \ | ||||
|         _isError = hpatch_TRUE; \ | ||||
|         _errInfo = errInfo; \ | ||||
|         goto _clear; \ | ||||
|     } \ | ||||
| } while(0) | ||||
|  | ||||
| napi_value HdiffPatch(napi_env env, napi_callback_info info) { | ||||
|     napi_status status; | ||||
|     size_t argc = 2; | ||||
|     napi_value args[2]; | ||||
|     hpatch_BOOL _isError = hpatch_FALSE; | ||||
|     const char* _errInfo = ""; | ||||
|  | ||||
|     // 获取参数 | ||||
|     status = napi_get_cb_info(env, info, &argc, args, NULL, NULL); | ||||
|     if (status != napi_ok || argc < 2) { | ||||
|         napi_throw_error(env, NULL, "Wrong number of arguments"); | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     // 获取origin buffer | ||||
|     bool isTypedArray; | ||||
|     status = napi_is_typedarray(env, args[0], &isTypedArray); | ||||
|     if (status != napi_ok || !isTypedArray) { | ||||
|         napi_throw_error(env, NULL, "First argument must be a TypedArray"); | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     uint8_t* originPtr; | ||||
|     size_t originLength; | ||||
|     status = napi_get_typedarray_info(env, args[0], NULL, &originLength,  | ||||
|                                      (void**)&originPtr, NULL, NULL); | ||||
|     if (status != napi_ok) { | ||||
|         napi_throw_error(env, NULL, "Failed to get origin buffer"); | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     // 获取patch buffer | ||||
|     status = napi_is_typedarray(env, args[1], &isTypedArray); | ||||
|     if (status != napi_ok || !isTypedArray) { | ||||
|         napi_throw_error(env, NULL, "Second argument must be a TypedArray"); | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     uint8_t* patchPtr; | ||||
|     size_t patchLength; | ||||
|     status = napi_get_typedarray_info(env, args[1], NULL, &patchLength,  | ||||
|                                      (void**)&patchPtr, NULL, NULL); | ||||
|     if (status != napi_ok) { | ||||
|         napi_throw_error(env, NULL, "Failed to get patch buffer"); | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     // 执行patch操作 | ||||
|     hpatch_singleCompressedDiffInfo patInfo; | ||||
|      | ||||
|     _check(((originLength==0)||originPtr) && patchPtr && (patchLength>0), "Corrupt patch"); | ||||
|     _check(kHPatch_ok==hpatch_getInfo_by_mem(&patInfo, patchPtr, patchLength), "Error info in hpatch"); | ||||
|     _check(originLength==patInfo.oldDataSize, "Error oldDataSize in hpatch"); | ||||
|     size_t newsize = (size_t)patInfo.newDataSize; | ||||
|     if (sizeof(size_t)!=sizeof(hpatch_StreamPos_t)) | ||||
|         _check(newsize==patInfo.newDataSize, "Error newDataSize in hpatch"); | ||||
|  | ||||
|     // 创建结果buffer | ||||
|     napi_value resultBuffer; | ||||
|     uint8_t* outPtr; | ||||
|     status = napi_create_buffer(env, newsize, (void**)&outPtr, &resultBuffer); | ||||
|     if (status != napi_ok) { | ||||
|         napi_throw_error(env, NULL, "Failed to create result buffer"); | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     // 执行patch | ||||
|     _check(kHPatch_ok==hpatch_by_mem(originPtr, originLength, outPtr, newsize, | ||||
|                                     patchPtr, patchLength, &patInfo), "hpatch"); | ||||
|  | ||||
|     return resultBuffer; | ||||
|  | ||||
| _clear: | ||||
|     if (_isError) { | ||||
|         napi_throw_error(env, NULL, _errInfo); | ||||
|         return NULL; | ||||
|     } | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| // 模块初始化 | ||||
| napi_value Init(napi_env env, napi_value exports) { | ||||
|     napi_status status; | ||||
|     napi_value fn; | ||||
|  | ||||
|     status = napi_create_function(env, NULL, 0, HdiffPatch, NULL, &fn); | ||||
|     if (status != napi_ok) { | ||||
|         napi_throw_error(env, NULL, "Unable to wrap native function"); | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     status = napi_set_named_property(env, exports, "hdiffPatch", fn); | ||||
|     if (status != napi_ok) { | ||||
|         napi_throw_error(env, NULL, "Unable to populate exports"); | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     return exports; | ||||
| } | ||||
|  | ||||
| NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) | ||||
							
								
								
									
										8
									
								
								harmony/src/main/cpp/pushy.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								harmony/src/main/cpp/pushy.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| #ifndef _DOWNLOAD_TASK_H_ | ||||
| #define _DOWNLOAD_TASK_H_ | ||||
|  | ||||
| #include <napi/native_api.h> | ||||
|  | ||||
| napi_value HdiffPatch(napi_env env, napi_callback_info info); | ||||
|  | ||||
| #endif // _DOWNLOAD_TASK_H_ | ||||
							
								
								
									
										492
									
								
								harmony/src/main/ets/DownloadTask.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										492
									
								
								harmony/src/main/ets/DownloadTask.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,492 @@ | ||||
| import http from '@ohos.net.http'; | ||||
| import fileIo from '@ohos.file.fs'; | ||||
| import util from '@ohos.util'; | ||||
| import common from '@ohos.app.ability.common'; | ||||
| import { BusinessError } from '@kit.BasicServicesKit'; | ||||
| import { buffer } from '@kit.ArkTS'; | ||||
| import zip from '@ohos.zlib'; | ||||
| import { EventHub } from './EventHub'; | ||||
| import { DownloadTaskParams } from './DownloadTaskParams'; | ||||
| import Pushy from 'librnupdate.so'; | ||||
|  | ||||
| interface ZipEntry { | ||||
|   filename: string; | ||||
|   content: ArrayBuffer; | ||||
| } | ||||
|  | ||||
| interface ZipFile { | ||||
|   entries: ZipEntry[]; | ||||
| } | ||||
|  | ||||
| export class DownloadTask { | ||||
|   private context: common.Context; | ||||
|   private hash: string; | ||||
|   private readonly DOWNLOAD_CHUNK_SIZE = 4096; | ||||
|   private eventHub: EventHub; | ||||
|  | ||||
|   constructor(context: common.Context) { | ||||
|     this.context = context; | ||||
|     this.eventHub = EventHub.getInstance(); | ||||
|   } | ||||
|  | ||||
|   private async removeDirectory(path: string): Promise<void> { | ||||
|     try { | ||||
|       const res = fileIo.accessSync(path); | ||||
|       if (res) { | ||||
|         const stat = await fileIo.stat(path); | ||||
|         if (stat.isDirectory()) { | ||||
|           const files = await fileIo.listFile(path); | ||||
|           for (const file of files) { | ||||
|             if (file === '.' || file === '..') continue; | ||||
|             await this.removeDirectory(`${path}/${file}`); | ||||
|           } | ||||
|           await fileIo.rmdir(path); | ||||
|         } else { | ||||
|           await fileIo.unlink(path); | ||||
|         } | ||||
|       } | ||||
|     } catch (error) { | ||||
|       console.error('Failed to delete directory:', error); | ||||
|       throw error; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private async downloadFile(params: DownloadTaskParams): Promise<void> { | ||||
|     const httpRequest = http.createHttp(); | ||||
|     this.hash = params.hash; | ||||
|  | ||||
|     try { | ||||
|       try { | ||||
|         const exists = fileIo.accessSync(params.targetFile); | ||||
|         if (exists) { | ||||
|           await fileIo.unlink(params.targetFile); | ||||
|         }else{ | ||||
|           const targetDir = params.targetFile.substring( | ||||
|             0, | ||||
|             params.targetFile.lastIndexOf('/'), | ||||
|           ); | ||||
|           await fileIo.mkdir(targetDir); | ||||
|         } | ||||
|       } catch (error) { | ||||
|       } | ||||
|  | ||||
|       const response = await httpRequest.request(params.url, { | ||||
|         method: http.RequestMethod.GET, | ||||
|         readTimeout: 60000, | ||||
|         connectTimeout: 60000, | ||||
|         header: { | ||||
|           'Content-Type': 'application/octet-stream', | ||||
|         }, | ||||
|       }); | ||||
|  | ||||
|       if (response.responseCode > 299) { | ||||
|         throw new Error(`Server error: ${response.responseCode}`); | ||||
|       } | ||||
|  | ||||
|       const contentLength = parseInt(response.header['Content-Length'] || '0'); | ||||
|       const writer = await fileIo.open( | ||||
|         params.targetFile, | ||||
|         fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE, | ||||
|       ); | ||||
|       let received = 0; | ||||
|       const data = response.result as ArrayBuffer; | ||||
|       const chunks = Math.ceil(data.byteLength / this.DOWNLOAD_CHUNK_SIZE); | ||||
|       for (let i = 0; i < chunks; i++) { | ||||
|         const start = i * this.DOWNLOAD_CHUNK_SIZE; | ||||
|         const end = Math.min(start + this.DOWNLOAD_CHUNK_SIZE, data.byteLength); | ||||
|         const chunk = data.slice(start, end); | ||||
|  | ||||
|         await fileIo.write(writer.fd, chunk); | ||||
|         received += chunk.byteLength; | ||||
|  | ||||
|         this.onProgressUpdate(received, contentLength); | ||||
|       } | ||||
|       await fileIo.close(writer); | ||||
|       const stat = await fileIo.stat(params.targetFile); | ||||
|       const fileSize = stat.size; | ||||
|     } catch (error) { | ||||
|       console.error('Download failed:', error); | ||||
|       throw error; | ||||
|     } finally { | ||||
|       httpRequest.destroy(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private onProgressUpdate(received: number, total: number): void { | ||||
|     this.eventHub.emit('downloadProgress', { | ||||
|       received, | ||||
|       total, | ||||
|       hash: this.hash, | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   private async copyFile(from: string, to: string): Promise<void> { | ||||
|     let reader; | ||||
|     let writer; | ||||
|     try { | ||||
|       reader = fileIo.openSync(from, fileIo.OpenMode.READ_ONLY); | ||||
|       writer = fileIo.openSync( | ||||
|         to, | ||||
|         fileIo.OpenMode.CREATE | fileIo.OpenMode.WRITE_ONLY, | ||||
|       ); | ||||
|       const arrayBuffer = new ArrayBuffer(4096); | ||||
|       let bytesRead: number; | ||||
|       do { | ||||
|         bytesRead = await fileIo | ||||
|           .read(reader.fd, arrayBuffer) | ||||
|           .catch((err: BusinessError) => { | ||||
|             throw new Error( | ||||
|               `Error reading file: ${err.message}, code: ${err.code}`, | ||||
|             ); | ||||
|           }); | ||||
|         if (bytesRead > 0) { | ||||
|           const buf = buffer.from(arrayBuffer, 0, bytesRead); | ||||
|           await fileIo | ||||
|             .write(writer.fd, buf.buffer, { | ||||
|               offset: 0, | ||||
|               length: bytesRead, | ||||
|             }) | ||||
|             .catch((err: BusinessError) => { | ||||
|               throw new Error( | ||||
|                 `Error writing file: ${err.message}, code: ${err.code}`, | ||||
|               ); | ||||
|             }); | ||||
|         } | ||||
|       } while (bytesRead > 0); | ||||
|       console.info('File copied successfully'); | ||||
|     } catch (error) { | ||||
|       console.error('Copy file failed:', error); | ||||
|       throw error; | ||||
|     } finally { | ||||
|       if (reader !== undefined) { | ||||
|         fileIo.closeSync(reader); | ||||
|       } | ||||
|       if (writer !== undefined) { | ||||
|         fileIo.closeSync(writer); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private async doFullPatch(params: DownloadTaskParams): Promise<void> { | ||||
|     await this.downloadFile(params); | ||||
|     await this.removeDirectory(params.unzipDirectory); | ||||
|     await fileIo.mkdir(params.unzipDirectory); | ||||
|  | ||||
|     try { | ||||
|       await zip.decompressFile(params.targetFile, params.unzipDirectory); | ||||
|     } catch (error) { | ||||
|       console.error('Unzip failed:', error); | ||||
|       throw error; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private async processUnzippedFiles(directory: string): Promise<ZipFile> { | ||||
|     const entries: ZipEntry[] = []; | ||||
|     try { | ||||
|       const files = await fileIo.listFile(directory); | ||||
|       for (const file of files) { | ||||
|         if (file === '.' || file === '..') continue; | ||||
|  | ||||
|         const filePath = `${directory}/${file}`; | ||||
|         const stat = await fileIo.stat(filePath); | ||||
|  | ||||
|         if (!stat.isDirectory()) { | ||||
|           const reader = await fileIo.open(filePath, fileIo.OpenMode.READ_ONLY); | ||||
|           const fileSize = stat.size; | ||||
|           const content = new ArrayBuffer(fileSize); | ||||
|  | ||||
|           try { | ||||
|             await fileIo.read(reader.fd, content); | ||||
|             entries.push({ | ||||
|               filename: file, | ||||
|               content: content, | ||||
|             }); | ||||
|           } finally { | ||||
|             await fileIo.close(reader); | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       return { entries }; | ||||
|     } catch (error) { | ||||
|       console.error('Failed to process unzipped files:', error); | ||||
|       throw error; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private async doPatchFromApp(params: DownloadTaskParams): Promise<void> { | ||||
|     await this.downloadFile(params); | ||||
|     await this.removeDirectory(params.unzipDirectory); | ||||
|     await fileIo.mkdir(params.unzipDirectory); | ||||
|  | ||||
|     let foundDiff = false; | ||||
|     let foundBundlePatch = false; | ||||
|     const copyList: Map<string, Array<any>> = new Map(); | ||||
|     await zip.decompressFile(params.targetFile, params.unzipDirectory); | ||||
|     const zipFile = await this.processUnzippedFiles(params.unzipDirectory); | ||||
|     for (const entry of zipFile.entries) { | ||||
|       const fn = entry.filename; | ||||
|  | ||||
|       if (fn === '__diff.json') { | ||||
|         foundDiff = true; | ||||
|         let jsonContent = ''; | ||||
|         const bufferArray = new Uint8Array(entry.content); | ||||
|         for (let i = 0; i < bufferArray.length; i++) { | ||||
|           jsonContent += String.fromCharCode(bufferArray[i]); | ||||
|         } | ||||
|         const obj = JSON.parse(jsonContent); | ||||
|  | ||||
|         const copies = obj.copies; | ||||
|         for (const to in copies) { | ||||
|           let from = copies[to]; | ||||
|           if (from === '') { | ||||
|             from = to; | ||||
|           } | ||||
|  | ||||
|           if (!copyList.has(from)) { | ||||
|             copyList.set(from, []); | ||||
|           } | ||||
|  | ||||
|           const target = copyList.get(from); | ||||
|           if (target) { | ||||
|             const toFile = `${params.unzipDirectory}/${to}`; | ||||
|             target.push(toFile); | ||||
|           } | ||||
|         } | ||||
|         continue; | ||||
|       } | ||||
|       if (fn === 'bundle.harmony.js.patch') { | ||||
|         foundBundlePatch = true; | ||||
|         try { | ||||
|           const resourceManager = this.context.resourceManager; | ||||
|           const originContent = await resourceManager.getRawFileContent( | ||||
|             'bundle.harmony.js', | ||||
|           ); | ||||
|           const patched = await Pushy.hdiffPatch( | ||||
|             new Uint8Array(originContent.buffer), | ||||
|             new Uint8Array(entry.content), | ||||
|           ); | ||||
|           const outputFile = `${params.unzipDirectory}/bundle.harmony.js`; | ||||
|           const writer = await fileIo.open( | ||||
|             outputFile, | ||||
|             fileIo.OpenMode.CREATE | fileIo.OpenMode.WRITE_ONLY, | ||||
|           ); | ||||
|           const chunkSize = 4096; | ||||
|           let bytesWritten = 0; | ||||
|           const totalLength = patched.byteLength; | ||||
|  | ||||
|           while (bytesWritten < totalLength) { | ||||
|             const chunk = patched.slice(bytesWritten, bytesWritten + chunkSize); | ||||
|             await fileIo.write(writer.fd, chunk); | ||||
|             bytesWritten += chunk.byteLength; | ||||
|           } | ||||
|           await fileIo.close(writer); | ||||
|           continue; | ||||
|         } catch (error) { | ||||
|           console.error('Failed to process bundle patch:', error); | ||||
|           throw error; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       await zip.decompressFile(entry.filename, params.unzipDirectory); | ||||
|     } | ||||
|  | ||||
|     if (!foundDiff) { | ||||
|       throw new Error('diff.json not found'); | ||||
|     } | ||||
|     if (!foundBundlePatch) { | ||||
|       throw new Error('bundle patch not found'); | ||||
|     } | ||||
|     await this.copyFromResource(copyList); | ||||
|   } | ||||
|  | ||||
|   private async doPatchFromPpk(params: DownloadTaskParams): Promise<void> { | ||||
|     await this.downloadFile(params); | ||||
|     await this.removeDirectory(params.unzipDirectory); | ||||
|     await fileIo.mkdir(params.unzipDirectory); | ||||
|  | ||||
|     let foundDiff = false; | ||||
|     let foundBundlePatch = false; | ||||
|     const copyList: Map<string, Array<any>> = new Map(); | ||||
|     await zip.decompressFile(params.targetFile, params.unzipDirectory); | ||||
|     const zipFile = await this.processUnzippedFiles(params.unzipDirectory); | ||||
|     for (const entry of zipFile.entries) { | ||||
|       const fn = entry.filename; | ||||
|  | ||||
|       if (fn === '__diff.json') { | ||||
|         foundDiff = true; | ||||
|         let jsonContent = ''; | ||||
|         const bufferArray = new Uint8Array(entry.content); | ||||
|         for (let i = 0; i < bufferArray.length; i++) { | ||||
|           jsonContent += String.fromCharCode(bufferArray[i]); | ||||
|         } | ||||
|         const obj = JSON.parse(jsonContent); | ||||
|  | ||||
|         const copies = obj.copies; | ||||
|         for (const to in copies) { | ||||
|           let from = copies[to]; | ||||
|           if (from === '') { | ||||
|             from = to; | ||||
|           } | ||||
|  | ||||
|           if (!copyList.has(from)) { | ||||
|             copyList.set(from, []); | ||||
|           } | ||||
|  | ||||
|           const target = copyList.get(from); | ||||
|           if (target) { | ||||
|             const toFile = `${params.unzipDirectory}/${to}`; | ||||
|             target.push(toFile); | ||||
|           } | ||||
|         } | ||||
|         continue; | ||||
|       } | ||||
|       if (fn === 'bundle.harmony.js.patch') { | ||||
|         foundBundlePatch = true; | ||||
|         const filePath = params.originDirectory + '/bundle.harmony.js'; | ||||
|         const res = fileIo.accessSync(filePath); | ||||
|         if (res) { | ||||
|           const stat = await fileIo.stat(filePath); | ||||
|           const reader = await fileIo.open(filePath, fileIo.OpenMode.READ_ONLY); | ||||
|           const fileSize = stat.size; | ||||
|           const originContent = new ArrayBuffer(fileSize); | ||||
|           try { | ||||
|             await fileIo.read(reader.fd, originContent); | ||||
|             const patched = await Pushy.hdiffPatch( | ||||
|               new Uint8Array(originContent), | ||||
|               new Uint8Array(entry.content), | ||||
|             ); | ||||
|             const outputFile = `${params.unzipDirectory}/bundle.harmony.js`; | ||||
|             const writer = await fileIo.open(outputFile, fileIo.OpenMode.CREATE | fileIo.OpenMode.WRITE_ONLY); | ||||
|             const chunkSize = 4096; | ||||
|             let bytesWritten = 0; | ||||
|             const totalLength = patched.byteLength; | ||||
|             while (bytesWritten < totalLength) { | ||||
|               const chunk = patched.slice(bytesWritten, bytesWritten + chunkSize); | ||||
|               await fileIo.write(writer.fd, chunk); | ||||
|               bytesWritten += chunk.byteLength; | ||||
|             } | ||||
|             await fileIo.close(writer); | ||||
|             continue; | ||||
|           } finally { | ||||
|             await fileIo.close(reader); | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       await zip.decompressFile(entry.filename, params.unzipDirectory); | ||||
|     } | ||||
|  | ||||
|     if (!foundDiff) { | ||||
|       throw new Error('diff.json not found'); | ||||
|     } | ||||
|     if (!foundBundlePatch) { | ||||
|       throw new Error('bundle patch not found'); | ||||
|     } | ||||
|     console.info('Patch from PPK completed'); | ||||
|   } | ||||
|  | ||||
|   private async copyFromResource( | ||||
|     copyList: Map<string, Array<string>>, | ||||
|   ): Promise<void> { | ||||
|     try { | ||||
|       const bundlePath = this.context.bundleCodeDir; | ||||
|  | ||||
|       const files = await fileIo.listFile(bundlePath); | ||||
|       for (const file of files) { | ||||
|         if (file === '.' || file === '..') continue; | ||||
|  | ||||
|         const targets = copyList.get(file); | ||||
|         if (targets) { | ||||
|           let lastTarget: string | undefined; | ||||
|  | ||||
|           for (const target of targets) { | ||||
|             console.info(`Copying from resource ${file} to ${target}`); | ||||
|  | ||||
|             if (lastTarget) { | ||||
|               await this.copyFile(lastTarget, target); | ||||
|             } else { | ||||
|               const sourcePath = `${bundlePath}/${file}`; | ||||
|               await this.copyFile(sourcePath, target); | ||||
|               lastTarget = target; | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } catch (error) { | ||||
|       console.error('Copy from resource failed:', error); | ||||
|       throw error; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private async doCleanUp(params: DownloadTaskParams): Promise<void> { | ||||
|     const DAYS_TO_KEEP = 7; | ||||
|     const now = Date.now(); | ||||
|     const maxAge = DAYS_TO_KEEP * 24 * 60 * 60 * 1000; | ||||
|  | ||||
|     try { | ||||
|       const files = await fileIo.listFile(params.unzipDirectory); | ||||
|       for (const file of files) { | ||||
|         if (file.startsWith('.')) continue; | ||||
|  | ||||
|         const filePath = `${params.unzipDirectory}/${file}`; | ||||
|         const stat = await fileIo.stat(filePath); | ||||
|  | ||||
|         if ( | ||||
|           now - stat.mtime > maxAge && | ||||
|           file !== params.hash && | ||||
|           file !== params.originHash | ||||
|         ) { | ||||
|           if (stat.isDirectory()) { | ||||
|             await this.removeDirectory(filePath); | ||||
|           } else { | ||||
|             await fileIo.unlink(filePath); | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } catch (error) { | ||||
|       console.error('Cleanup failed:', error); | ||||
|       throw error; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   public async execute(params: DownloadTaskParams): Promise<void> { | ||||
|     try { | ||||
|       switch (params.type) { | ||||
|         case DownloadTaskParams.TASK_TYPE_PATCH_FULL: | ||||
|           await this.doFullPatch(params); | ||||
|           break; | ||||
|         case DownloadTaskParams.TASK_TYPE_PATCH_FROM_APP: | ||||
|           await this.doPatchFromApp(params); | ||||
|           break; | ||||
|         case DownloadTaskParams.TASK_TYPE_PATCH_FROM_PPK: | ||||
|           await this.doPatchFromPpk(params); | ||||
|           break; | ||||
|         case DownloadTaskParams.TASK_TYPE_CLEANUP: | ||||
|           await this.doCleanUp(params); | ||||
|           break; | ||||
|         case DownloadTaskParams.TASK_TYPE_PLAIN_DOWNLOAD: | ||||
|           await this.downloadFile(params); | ||||
|           break; | ||||
|         default: | ||||
|           throw new Error(`Unknown task type: ${params.type}`); | ||||
|       } | ||||
|  | ||||
|       params.listener?.onDownloadCompleted(params); | ||||
|     } catch (error) { | ||||
|       console.error('Task execution failed:', error); | ||||
|       if (params.type !== DownloadTaskParams.TASK_TYPE_CLEANUP) { | ||||
|         try { | ||||
|           if (params.type === DownloadTaskParams.TASK_TYPE_PLAIN_DOWNLOAD) { | ||||
|             await fileIo.unlink(params.targetFile); | ||||
|           } else { | ||||
|             await this.removeDirectory(params.unzipDirectory); | ||||
|           } | ||||
|         } catch (cleanupError) { | ||||
|           console.error('Cleanup after error failed:', cleanupError); | ||||
|         } | ||||
|       } | ||||
|       params.listener?.onDownloadFailed(error); | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										25
									
								
								harmony/src/main/ets/DownloadTaskParams.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								harmony/src/main/ets/DownloadTaskParams.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| export interface DownloadTaskListener { | ||||
|   onDownloadCompleted(params: DownloadTaskParams): void; | ||||
|   onDownloadFailed(error: Error): void; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * 下载任务参数类 | ||||
|  */ | ||||
| export class DownloadTaskParams { | ||||
|   // 任务类型常量 | ||||
|   static readonly TASK_TYPE_CLEANUP: number = 0;        // 保留hash和originHash | ||||
|   static readonly TASK_TYPE_PATCH_FULL: number = 1;     // 全量补丁 | ||||
|   static readonly TASK_TYPE_PATCH_FROM_APP: number = 2; // 从APP补丁 | ||||
|   static readonly TASK_TYPE_PATCH_FROM_PPK: number = 3; // 从PPK补丁 | ||||
|   static readonly TASK_TYPE_PLAIN_DOWNLOAD: number = 4; // 普通下载 | ||||
|  | ||||
|   type: number;                 // 任务类型 | ||||
|   url: string;                  // 下载URL | ||||
|   hash: string;                 // 文件哈希值 | ||||
|   originHash: string;           // 原始文件哈希值 | ||||
|   targetFile: string;           // 目标文件路径 | ||||
|   unzipDirectory: string;       // 解压目录路径 | ||||
|   originDirectory: string;      // 原始文件目录路径 | ||||
|   listener: DownloadTaskListener; // 下载监听器 | ||||
| } | ||||
							
								
								
									
										38
									
								
								harmony/src/main/ets/EventHub.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								harmony/src/main/ets/EventHub.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
| type EventCallback = (data: any) => void; | ||||
|  | ||||
| export class EventHub { | ||||
|   private static instance: EventHub; | ||||
|   private listeners: Map<string, Set<EventCallback>>; | ||||
|  | ||||
|   private constructor() { | ||||
|     this.listeners = new Map(); | ||||
|   } | ||||
|  | ||||
|   public static getInstance(): EventHub { | ||||
|     if (!EventHub.instance) { | ||||
|       EventHub.instance = new EventHub(); | ||||
|     } | ||||
|     return EventHub.instance; | ||||
|   } | ||||
|  | ||||
|   public on(event: string, callback: EventCallback): void { | ||||
|     if (!this.listeners.has(event)) { | ||||
|       this.listeners.set(event, new Set()); | ||||
|     } | ||||
|     this.listeners.get(event)?.add(callback); | ||||
|   } | ||||
|  | ||||
|   public off(event: string, callback: EventCallback): void { | ||||
|     this.listeners.get(event)?.delete(callback); | ||||
|   } | ||||
|  | ||||
|   public emit(event: string, data: any): void { | ||||
|     this.listeners.get(event)?.forEach(callback => { | ||||
|       try { | ||||
|         callback(data); | ||||
|       } catch (error) { | ||||
|         console.error(`Error in event listener for ${event}:`, error); | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										59
									
								
								harmony/src/main/ets/FileJSBundleProvider.ets
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								harmony/src/main/ets/FileJSBundleProvider.ets
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| import { HotReloadConfig, JSBundleProvider, JSBundleProviderError, JSPackagerClientConfig } from 'rnoh'; | ||||
| import fileIo from '@ohos.file.fs'; | ||||
| import common from '@ohos.app.ability.common'; | ||||
| import { UpdateContext } from './UpdateContext'; | ||||
|  | ||||
| export class FileJSBundleProvider extends JSBundleProvider { | ||||
|   private updateContext: UpdateContext; | ||||
|   private filePath: string = '' | ||||
|  | ||||
|   constructor(context: common.UIAbilityContext) { | ||||
|     super(); | ||||
|     this.updateContext = new UpdateContext(context); | ||||
|   } | ||||
|   getURL(): string { | ||||
|     return this.updateContext.getBundleUrl(); | ||||
|   } | ||||
|  | ||||
|   async getBundle(): Promise<ArrayBuffer> { | ||||
|     try { | ||||
|       this.filePath =  this.updateContext.getBundleUrl(); | ||||
|       const res = fileIo.accessSync(this.filePath); | ||||
|       if (res) { | ||||
|         const file = fileIo.openSync(this.filePath, fileIo.OpenMode.READ_ONLY); | ||||
|         try { | ||||
|           const stat = await fileIo.stat(this.filePath); | ||||
|           const fileSize = stat.size; | ||||
|           const buffer = new ArrayBuffer(fileSize); | ||||
|           const bytesRead = fileIo.readSync(file.fd, buffer, { | ||||
|             offset: 0, | ||||
|             length: fileSize | ||||
|           }); | ||||
|            | ||||
|           if (bytesRead !== fileSize) { | ||||
|             throw new Error(`Failed to read entire file: read ${bytesRead} of ${fileSize} bytes`); | ||||
|           } | ||||
|           return buffer; | ||||
|         } finally { | ||||
|           fileIo.closeSync(file.fd); | ||||
|         } | ||||
|       } | ||||
|       throw new Error('Update bundle not found'); | ||||
|     } catch (error) { | ||||
|       throw new JSBundleProviderError(`Couldn't load JSBundle from ${this.filePath}`, error) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   getAppKeys(): string[] { | ||||
|     return []; | ||||
|   } | ||||
|  | ||||
|   getHotReloadConfig(): HotReloadConfig | null { | ||||
|    return null; | ||||
|   } | ||||
|  | ||||
|   getJSPackagerClientConfig(): JSPackagerClientConfig | null { | ||||
|     return null; | ||||
|   } | ||||
|  | ||||
| } | ||||
							
								
								
									
										35
									
								
								harmony/src/main/ets/Logger.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								harmony/src/main/ets/Logger.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
|  | ||||
| import hilog from '@ohos.hilog'; | ||||
|  | ||||
| class Logger { | ||||
|   private domain: number; | ||||
|   private prefix: string; | ||||
|   private format: string = '%{public}s,%{public}s'; | ||||
|   private isDebug: boolean; | ||||
|  | ||||
|   constructor(prefix: string = 'MyApp', domain: number = 0xFF00, isDebug = false) { | ||||
|     this.prefix = prefix; | ||||
|     this.domain = domain; | ||||
|     this.isDebug = isDebug; | ||||
|   } | ||||
|  | ||||
|   debug(...args: string[]): void { | ||||
|     if (this.isDebug) { | ||||
|       hilog.debug(this.domain, this.prefix, this.format, args); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   info(...args: string[]): void { | ||||
|     hilog.info(this.domain, this.prefix, this.format, args); | ||||
|   } | ||||
|  | ||||
|   warn(...args: string[]): void { | ||||
|     hilog.warn(this.domain, this.prefix, this.format, args); | ||||
|   } | ||||
|  | ||||
|   error(...args: string[]): void { | ||||
|     hilog.error(this.domain, this.prefix, this.format, args); | ||||
|   } | ||||
| } | ||||
|  | ||||
| export default new Logger('geolocation', 0xFF00, false) | ||||
							
								
								
									
										22
									
								
								harmony/src/main/ets/PushyPackage.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								harmony/src/main/ets/PushyPackage.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| import { RNPackage, TurboModulesFactory } from 'rnoh/ts'; | ||||
| import type { TurboModule, TurboModuleContext } from 'rnoh/ts'; | ||||
| import { PushyTurboModule } from './PushyTurboModule'; | ||||
|  | ||||
| class PushyTurboModulesFactory extends TurboModulesFactory { | ||||
|   createTurboModule(name: string): TurboModule | null { | ||||
|     if (name === 'Pushy') { | ||||
|       return new PushyTurboModule(this.ctx); | ||||
|     } | ||||
|     return null; | ||||
|   } | ||||
|  | ||||
|   hasTurboModule(name: string): boolean { | ||||
|     return name === 'Pushy'; | ||||
|   } | ||||
| } | ||||
|  | ||||
| export class PushyPackage extends RNPackage { | ||||
|   createTurboModulesFactory(ctx: TurboModuleContext): TurboModulesFactory { | ||||
|     return new PushyTurboModulesFactory(ctx); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										123
									
								
								harmony/src/main/ets/PushyTurboModule.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								harmony/src/main/ets/PushyTurboModule.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,123 @@ | ||||
| import { TurboModule, TurboModuleContext } from 'rnoh/ts'; | ||||
| import common from '@ohos.app.ability.common'; | ||||
| import dataPreferences from '@ohos.data.preferences'; | ||||
| import { bundleManager } from '@kit.AbilityKit'; | ||||
| import abilityAccessCtrl, { Permissions } from '@ohos.abilityAccessCtrl'; | ||||
| import { BusinessError } from '@ohos.base'; | ||||
| import logger from './Logger'; | ||||
| import { UpdateModuleImpl } from './UpdateModuleImpl'; | ||||
| import { UpdateContext } from './UpdateContext'; | ||||
|  | ||||
| const TAG = "PushyTurboModule" | ||||
|  | ||||
| export class PushyTurboModule extends TurboModule { | ||||
|   mUiCtx: common.UIAbilityContext | ||||
|   context: UpdateContext | ||||
|  | ||||
|   constructor(protected ctx: TurboModuleContext) { | ||||
|     super(ctx); | ||||
|     logger.debug(TAG, ",PushyTurboModule constructor"); | ||||
|     this.mUiCtx = ctx.uiAbilityContext | ||||
|     let rnInstance = ctx.rnInstance | ||||
|     this.context = new UpdateContext(this.mUiCtx) | ||||
|     // rnInstance.emitDeviceEvent("Pushy",{code: err.code, message: err.message}); | ||||
|   } | ||||
|  | ||||
|  | ||||
| getConstants(): Object { | ||||
|   logger.debug(TAG, ",call getConstants"); | ||||
|   const context = this.mUiCtx; | ||||
|   const preferencesManager = dataPreferences.getPreferencesSync(context,{ name: 'update' }); | ||||
|   const isFirstTime = preferencesManager.getSync("isFirstTime", false) as boolean; | ||||
|   const rolledBackVersion = preferencesManager.getSync("rolledBackVersion", "") as string; | ||||
|   const uuid = preferencesManager.getSync("uuid", "") as string; | ||||
|   const currentVersion = preferencesManager.getSync("currentVersion", "") as string; | ||||
|   const buildTime = preferencesManager.getSync("buildTime", "") as string; | ||||
|   const isUsingBundleUrl = this.context.getIsUsingBundleUrl(); | ||||
|   let bundleFlags = bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_REQUESTED_PERMISSION; | ||||
|   let packageVersion = ''; | ||||
|   try { | ||||
|     const bundleInfo = bundleManager.getBundleInfoForSelfSync(bundleFlags); | ||||
|     packageVersion = bundleInfo?.versionName || "Unknown" | ||||
|   } catch (error) { | ||||
|     console.error("Failed to get bundle info:", error); | ||||
|   } | ||||
|  | ||||
|   if (isFirstTime) { | ||||
|     preferencesManager.deleteSync("isFirstTime"); | ||||
|   } | ||||
|  | ||||
|   if (rolledBackVersion) { | ||||
|     preferencesManager.deleteSync("rolledBackVersion"); | ||||
|   } | ||||
|  | ||||
|   return { | ||||
|     downloadRootDir: `${context.filesDir}/_update`, | ||||
|     packageVersion, | ||||
|     currentVersion, | ||||
|     buildTime, | ||||
|     isUsingBundleUrl, | ||||
|     isFirstTime, | ||||
|     rolledBackVersion, | ||||
|     uuid, | ||||
|   } | ||||
| } | ||||
|  | ||||
|  | ||||
|   async setLocalHashInfo(hash: string, info: string): Promise<boolean>  { | ||||
|     logger.debug(TAG, ",call setLocalHashInfo"); | ||||
|     return UpdateModuleImpl.setLocalHashInfo(this.context,hash,info); | ||||
|   } | ||||
|  | ||||
|   async getLocalHashInfo(hash: string): Promise<string> { | ||||
|     return UpdateModuleImpl.getLocalHashInfo(this.context,hash); | ||||
|   } | ||||
|  | ||||
|   async setUuid(uuid: string): Promise<boolean>  { | ||||
|     logger.debug(TAG, `,call setUuid`); | ||||
|     return UpdateModuleImpl.setUuid(this.context,uuid); | ||||
|   } | ||||
|  | ||||
|   async reloadUpdate(options: { hash: string }): Promise<void> { | ||||
|     logger.debug(TAG, `,call reloadUpdate`); | ||||
|     return UpdateModuleImpl.reloadUpdate(this.context, this.mUiCtx, options); | ||||
|   } | ||||
|  | ||||
|   async setNeedUpdate(options: { hash: string }): Promise<boolean> { | ||||
|     logger.debug(TAG, `,call setNeedUpdate`); | ||||
|     return UpdateModuleImpl.setNeedUpdate(this.context, options); | ||||
|   } | ||||
|  | ||||
|   async markSuccess(): Promise<boolean> { | ||||
|     logger.debug(TAG, `,call markSuccess`); | ||||
|     return UpdateModuleImpl.markSuccess(this.context); | ||||
|   } | ||||
|  | ||||
|   async downloadPatchFromPpk(options: { updateUrl: string; hash: string; originHash: string }): Promise<void> { | ||||
|     logger.debug(TAG, `,call downloadPatchFromPpk`); | ||||
|     return UpdateModuleImpl.downloadPatchFromPpk(this.context, options); | ||||
|   } | ||||
|  | ||||
|   async downloadPatchFromPackage(options: { updateUrl: string; hash: string }): Promise<void>  { | ||||
|     logger.debug(TAG, `,call downloadPatchFromPackage`); | ||||
|     return UpdateModuleImpl.downloadPatchFromPackage(this.context, options); | ||||
|   } | ||||
|  | ||||
|   async downloadFullUpdate(options: { updateUrl: string; hash: string }): Promise<void> { | ||||
|     logger.debug(TAG, `,call downloadFullUpdate`); | ||||
|     return UpdateModuleImpl.downloadFullUpdate(this.context, options); | ||||
|   } | ||||
|  | ||||
|   async downloadAndInstallApk(options: { url: string; target: string; hash: string }): Promise<void> { | ||||
|     logger.debug(TAG, `,call downloadAndInstallApk`); | ||||
|     return UpdateModuleImpl.downloadAndInstallApk(this.mUiCtx, options); | ||||
|   } | ||||
|  | ||||
|   addListener(eventName: string): void { | ||||
|     logger.debug(TAG, `,call addListener`); | ||||
|   } | ||||
|  | ||||
|   removeListeners(count: number): void { | ||||
|     logger.debug(TAG, `,call removeListeners`); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										251
									
								
								harmony/src/main/ets/UpdateContext.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										251
									
								
								harmony/src/main/ets/UpdateContext.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,251 @@ | ||||
| import preferences from '@ohos.data.preferences'; | ||||
| import bundleManager from '@ohos.bundle.bundleManager'; | ||||
| import fileIo from '@ohos.file.fs'; | ||||
| import { DownloadTask } from './DownloadTask'; | ||||
| import common from '@ohos.app.ability.common'; | ||||
| import { DownloadTaskParams } from './DownloadTaskParams'; | ||||
|  | ||||
| export class UpdateContext { | ||||
|     private context: common.UIAbilityContext; | ||||
|     private rootDir: string; | ||||
|     private preferences: preferences.Preferences; | ||||
|     private static DEBUG: boolean = false; | ||||
|     private static isUsingBundleUrl: boolean = false; | ||||
|  | ||||
|     constructor(context: common.UIAbilityContext) { | ||||
|         this.context = context; | ||||
|         this.rootDir = context.filesDir + '/_update'; | ||||
|          | ||||
|         try { | ||||
|             if (!fileIo.accessSync(this.rootDir)) { | ||||
|                 fileIo.mkdirSync(this.rootDir); | ||||
|             } | ||||
|         } catch (e) { | ||||
|             console.error('Failed to create root directory:', e); | ||||
|         } | ||||
|         this.initPreferences(); | ||||
|     } | ||||
|  | ||||
|     private async initPreferences() { | ||||
|         try { | ||||
|             this.preferences = await preferences.getPreferences(this.context, 'update'); | ||||
|             const packageVersion = await this.getPackageVersion(); | ||||
|             const storedVersion = await this.preferences.get('packageVersion', ''); | ||||
|             if (packageVersion !== storedVersion) { | ||||
|                 await this.preferences.clear(); | ||||
|                 await this.preferences.put('packageVersion', packageVersion); | ||||
|                 await this.preferences.flush(); | ||||
|                 this.cleanUp(); | ||||
|             } | ||||
|         } catch (e) { | ||||
|             console.error('Failed to init preferences:', e); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async setKv(key: string, value: string): Promise<void> { | ||||
|         await this.preferences.put(key, value); | ||||
|         await this.preferences.flush(); | ||||
|     } | ||||
|  | ||||
|     public async getKv(key: string): Promise<string> { | ||||
|         return await this.preferences.get(key, '') as string; | ||||
|     } | ||||
|  | ||||
|     public async isFirstTime(): Promise<boolean> { | ||||
|         return await this.preferences.get('firstTime', false) as boolean; | ||||
|     } | ||||
|  | ||||
|     public async rolledBackVersion(): Promise<string> { | ||||
|         return await this.preferences.get('rolledBackVersion', '') as string; | ||||
|     } | ||||
|  | ||||
|     public async markSuccess(): Promise<void> { | ||||
|         await this.preferences.put('firstTimeOk', true); | ||||
|         const lastVersion = await this.preferences.get('lastVersion', '') as string; | ||||
|         const curVersion = await this.preferences.get('currentVersion', '') as string; | ||||
|          | ||||
|         if (lastVersion && lastVersion !== curVersion) { | ||||
|             await this.preferences.delete('lastVersion'); | ||||
|             await this.preferences.delete(`hash_${lastVersion}`); | ||||
|         } | ||||
|         await this.preferences.flush(); | ||||
|         this.cleanUp(); | ||||
|     } | ||||
|  | ||||
|     public clearFirstTime(): void { | ||||
|         this.preferences.putSync('firstTime', false); | ||||
|         this.preferences.flush(); | ||||
|         this.cleanUp(); | ||||
|     } | ||||
|  | ||||
|     public clearRollbackMark(): void { | ||||
|         this.preferences.putSync('rolledBackVersion', null); | ||||
|         this.preferences.flush(); | ||||
|         this.cleanUp(); | ||||
|     } | ||||
|  | ||||
|     public async downloadFullUpdate(url: string, hash: string, listener: DownloadFileListener): Promise<void> { | ||||
|         try { | ||||
|             const params = new DownloadTaskParams(); | ||||
|             params.type = DownloadTaskParams.TASK_TYPE_PATCH_FULL; | ||||
|             params.url = url; | ||||
|             params.hash = hash; | ||||
|             params.listener = listener; | ||||
|             params.targetFile = `${this.rootDir}/${hash}.ppk`; | ||||
|             const downloadTask = new DownloadTask(this.context); | ||||
|             await downloadTask.execute(params); | ||||
|         } catch (e) { | ||||
|             console.error('Failed to download full update:', e); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async downloadFile(url: string, hash: string, fileName: string, listener: DownloadFileListener): Promise<void> { | ||||
|         const params = new DownloadTaskParams(); | ||||
|         params.type = DownloadTaskParams.TASK_TYPE_PLAIN_DOWNLOAD; | ||||
|         params.url = url; | ||||
|         params.hash = hash; | ||||
|         params.listener = listener; | ||||
|         params.targetFile = this.rootDir + '/' + fileName; | ||||
|  | ||||
|         const downloadTask = new DownloadTask(this.context); | ||||
|         await downloadTask.execute(params); | ||||
|     } | ||||
|  | ||||
|     public async downloadPatchFromPpk(url: string, hash: string, originHash: string, listener: DownloadFileListener): Promise<void> { | ||||
|         const params = new DownloadTaskParams(); | ||||
|         params.type = DownloadTaskParams.TASK_TYPE_PATCH_FROM_PPK; | ||||
|         params.url = url; | ||||
|         params.hash = hash; | ||||
|         params.originHash = originHash; | ||||
|         params.listener = listener; | ||||
|         params.targetFile = `${this.rootDir}/${originHash}_${hash}.ppk.patch`; | ||||
|         params.unzipDirectory = `${this.rootDir}/${hash}`; | ||||
|         params.originDirectory = `${this.rootDir}/${params.originHash}`; | ||||
|          | ||||
|         const downloadTask = new DownloadTask(this.context); | ||||
|         await downloadTask.execute(params); | ||||
|     } | ||||
|  | ||||
|     public async downloadPatchFromPackage(url: string, hash: string, listener: DownloadFileListener): Promise<void> { | ||||
|         try { | ||||
|             const params = new DownloadTaskParams(); | ||||
|             params.type = DownloadTaskParams.TASK_TYPE_PATCH_FROM_APP; | ||||
|             params.url = url; | ||||
|             params.hash = hash; | ||||
|             params.listener = listener; | ||||
|             params.targetFile = `${this.rootDir}/${hash}.app.patch`; | ||||
|             params.unzipDirectory = `${this.rootDir}/${hash}`; | ||||
|  | ||||
|             const downloadTask = new DownloadTask(this.context); | ||||
|             await downloadTask.execute(params); | ||||
|         } catch (e) { | ||||
|             console.error('Failed to download APK patch:', e); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async switchVersion(hash: string): Promise<void> { | ||||
|         try { | ||||
|             const bundlePath = `${this.rootDir}/${hash}/bundle.harmony.js`; | ||||
|             if (!fileIo.accessSync(bundlePath)) { | ||||
|                 throw new Error(`Bundle version ${hash} not found.`); | ||||
|             } | ||||
|  | ||||
|             const lastVersion = await this.getKv('currentVersion'); | ||||
|             await this.setKv('currentVersion', hash); | ||||
|  | ||||
|             if (lastVersion && lastVersion !== hash) { | ||||
|                 await this.setKv('lastVersion', lastVersion); | ||||
|             } | ||||
|  | ||||
|             await this.setKv('firstTime', 'true'); | ||||
|             await this.setKv('firstTimeOk', 'false'); | ||||
|             await this.setKv('rolledBackVersion', null); | ||||
|         } catch (e) { | ||||
|             console.error('Failed to switch version:', e); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public static getBundleUrl(context: common.UIAbilityContext, defaultAssetsUrl?: string): string { | ||||
|         return new UpdateContext(context).getBundleUrl(defaultAssetsUrl); | ||||
|     } | ||||
|  | ||||
|     public getBundleUrl(defaultAssetsUrl?: string): string { | ||||
|         UpdateContext.isUsingBundleUrl = true; | ||||
|         const currentVersion = this.getCurrentVersion(); | ||||
|         if (!currentVersion) { | ||||
|             return defaultAssetsUrl; | ||||
|         } | ||||
|         if (!this.isFirstTime()) { | ||||
|             if (!this.preferences.get('firstTimeOk', true)) { | ||||
|                 return this.rollBack(); | ||||
|             } | ||||
|         } | ||||
|         let version = currentVersion; | ||||
|         while (version) { | ||||
|             const bundleFile = `${this.rootDir}/${version}/bundle.harmony.js`; | ||||
|             try { | ||||
|                 if (!fileIo.accessSync(bundleFile)) { | ||||
|                     console.error(`Bundle version ${version} not found.`); | ||||
|                     version = this.rollBack(); | ||||
|                     continue; | ||||
|                 } | ||||
|                 return bundleFile; | ||||
|             } catch (e) { | ||||
|                 console.error('Failed to access bundle file:', e); | ||||
|                 version = this.rollBack(); | ||||
|             } | ||||
|         } | ||||
|         return defaultAssetsUrl; | ||||
|     } | ||||
|  | ||||
|     getPackageVersion(): string { | ||||
|         let bundleFlags = bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_REQUESTED_PERMISSION; | ||||
|         let packageVersion = ''; | ||||
|         try { | ||||
|             const bundleInfo = bundleManager.getBundleInfoForSelfSync(bundleFlags); | ||||
|             packageVersion = bundleInfo?.versionName || "Unknown"; | ||||
|         } catch (error) { | ||||
|             console.error("获取包信息失败:", error); | ||||
|         } | ||||
|         return packageVersion; | ||||
|     } | ||||
|  | ||||
|     public getCurrentVersion() : string { | ||||
|         const currentVersion = this.preferences.getSync('currentVersion', '') as string; | ||||
|         return currentVersion; | ||||
|     } | ||||
|  | ||||
|     private rollBack(): string { | ||||
|         const lastVersion = this.preferences.getSync('lastVersion', '') as string; | ||||
|         const currentVersion = this.preferences.getSync('currentVersion', '') as string; | ||||
|         if (!lastVersion) { | ||||
|             this.preferences.deleteSync('currentVersion'); | ||||
|         } else { | ||||
|             this.preferences.putSync('currentVersion', lastVersion); | ||||
|         } | ||||
|          this.preferences.putSync('firstTimeOk', true); | ||||
|          this.preferences.putSync('firstTime', false); | ||||
|          this.preferences.putSync('rolledBackVersion', currentVersion); | ||||
|          this.preferences.flush(); | ||||
|         return lastVersion; | ||||
|     } | ||||
|  | ||||
|     private cleanUp(): void { | ||||
|         const params = new DownloadTaskParams(); | ||||
|         params.type = DownloadTaskParams.TASK_TYPE_CLEANUP; | ||||
|         params.hash =  this.preferences.getSync('currentVersion', '') as string; | ||||
|         params.originHash =  this.preferences.getSync('lastVersion', '') as string; | ||||
|         params.unzipDirectory = this.rootDir; | ||||
|         const downloadTask = new DownloadTask(this.context); | ||||
|         downloadTask.execute(params); | ||||
|     } | ||||
|  | ||||
|     public getIsUsingBundleUrl(): boolean { | ||||
|         return UpdateContext.isUsingBundleUrl; | ||||
|     } | ||||
| } | ||||
|  | ||||
| export interface DownloadFileListener { | ||||
|     onDownloadCompleted(params: DownloadTaskParams): void; | ||||
|     onDownloadFailed(error: Error): void; | ||||
| } | ||||
							
								
								
									
										200
									
								
								harmony/src/main/ets/UpdateModuleImpl.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										200
									
								
								harmony/src/main/ets/UpdateModuleImpl.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,200 @@ | ||||
| import { TurboModuleContext } from 'rnoh/ts'; | ||||
| import dataPreferences from '@ohos.data.preferences'; | ||||
| import bundleManager from '@ohos.bundle.bundleManager'; | ||||
| import common from '@ohos.app.ability.common'; | ||||
| import { BusinessError } from '@ohos.base'; | ||||
| import { UpdateContext } from './UpdateContext'; | ||||
| import { DownloadTaskParams } from './DownloadTaskParams'; | ||||
| import logger from './Logger'; | ||||
|  | ||||
| const TAG = "UpdateModuleImpl"; | ||||
|  | ||||
| export class UpdateModuleImpl { | ||||
|   static readonly NAME = "Pushy"; | ||||
|  | ||||
|   static async downloadFullUpdate( | ||||
|     updateContext: UpdateContext,  | ||||
|     options: { updateUrl: string; hash: string } | ||||
|   ): Promise<void> { | ||||
|     try { | ||||
|       await updateContext.downloadFullUpdate(options.updateUrl, options.hash, { | ||||
|         onDownloadCompleted: (params: DownloadTaskParams) => { | ||||
|           return Promise.resolve(); | ||||
|         }, | ||||
|         onDownloadFailed: (error: Error) => { | ||||
|           return Promise.reject(error); | ||||
|         } | ||||
|       }); | ||||
|     } catch (error) { | ||||
|       logger.error(TAG, `downloadFullUpdate failed: ${error}`); | ||||
|       throw error; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   static async downloadAndInstallApk( | ||||
|     context: common.UIAbilityContext, | ||||
|     options: { url: string; hash: string; target: string } | ||||
|   ): Promise<void> { | ||||
|     try { | ||||
|       const want = { | ||||
|         action: 'action.system.home', | ||||
|         parameters: { | ||||
|           uri: 'appmarket://details' | ||||
|         } | ||||
|       }; | ||||
|  | ||||
|       if (!context) { | ||||
|         throw new Error('获取context失败'); | ||||
|       } | ||||
|  | ||||
|       await context.startAbility(want); | ||||
|     } catch (error) { | ||||
|       logger.error(TAG, `installApk failed: ${error}`); | ||||
|       throw error; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   static async downloadPatchFromPackage( | ||||
|     updateContext: UpdateContext, | ||||
|     options: { updateUrl: string; hash: string } | ||||
|   ): Promise<void> { | ||||
|     try { | ||||
|       await updateContext.downloadPatchFromPackage(options.updateUrl, options.hash, { | ||||
|         onDownloadCompleted: (params: DownloadTaskParams) => { | ||||
|           return Promise.resolve(); | ||||
|         }, | ||||
|         onDownloadFailed: (error: Error) => { | ||||
|           return Promise.reject(error); | ||||
|         } | ||||
|       }); | ||||
|     } catch (error) { | ||||
|       logger.error(TAG, `downloadPatchFromPackage failed: ${error}`); | ||||
|       throw error; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   static async downloadPatchFromPpk( | ||||
|     updateContext: UpdateContext, | ||||
|     options: { updateUrl: string; hash: string; originHash: string } | ||||
|   ): Promise<void> { | ||||
|     try { | ||||
|       await updateContext.downloadPatchFromPpk( | ||||
|         options.updateUrl, | ||||
|         options.hash, | ||||
|         options.originHash, | ||||
|         { | ||||
|           onDownloadCompleted: (params: DownloadTaskParams) => { | ||||
|             return Promise.resolve(); | ||||
|           }, | ||||
|           onDownloadFailed: (error: Error) => { | ||||
|             return Promise.reject(error); | ||||
|           } | ||||
|         } | ||||
|       ); | ||||
|     } catch (error) { | ||||
|       logger.error(TAG, `downloadPatchFromPpk failed: ${error}`); | ||||
|       throw new Error(`执行报错: ${error.message}`); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   static async reloadUpdate( | ||||
|     updateContext: UpdateContext, | ||||
|     context: common.UIAbilityContext, | ||||
|     options: { hash: string } | ||||
|   ): Promise<void> { | ||||
|     const hash = options.hash; | ||||
|     if (!hash) { | ||||
|       throw new Error('hash不能为空'); | ||||
|     } | ||||
|  | ||||
|     try { | ||||
|       await updateContext.switchVersion(hash); | ||||
|       const bundleInfo = await bundleManager.getBundleInfoForSelf( | ||||
|         bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_REQUESTED_PERMISSION | ||||
|       ); | ||||
|       await context.terminateSelf(); | ||||
|       const want = { | ||||
|         bundleName: bundleInfo.name, | ||||
|         abilityName: context.abilityInfo?.name | ||||
|       }; | ||||
|       await context.startAbility(want); | ||||
|     } catch (error) { | ||||
|       logger.error(TAG, `reloadUpdate failed: ${error}`); | ||||
|       throw new Error(`pushy:switchVersion failed ${error.message}`); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   static async setNeedUpdate( | ||||
|     updateContext: UpdateContext, | ||||
|     options: { hash: string } | ||||
|   ): Promise<boolean> { | ||||
|     const hash = options.hash; | ||||
|     if (!hash) { | ||||
|       throw new Error('hash不能为空'); | ||||
|     } | ||||
|  | ||||
|     try { | ||||
|       await updateContext.switchVersion(hash); | ||||
|       return true; | ||||
|     } catch (error) { | ||||
|       logger.error(TAG, `setNeedUpdate failed: ${error}`); | ||||
|       throw new Error(`switchVersionLater failed: ${error.message}`); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   static async markSuccess(updateContext: UpdateContext): Promise<boolean> { | ||||
|     try { | ||||
|       await updateContext.markSuccess(); | ||||
|       return true; | ||||
|     } catch (error) { | ||||
|       logger.error(TAG, `markSuccess failed: ${error}`); | ||||
|       throw new Error(`执行报错: ${error.message}`); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   static async setUuid( | ||||
|     updateContext: UpdateContext, | ||||
|     uuid: string | ||||
|   ): Promise<boolean> { | ||||
|     try { | ||||
|       await updateContext.setKv('uuid', uuid); | ||||
|       return true; | ||||
|     } catch (error) { | ||||
|       logger.error(TAG, `setUuid failed: ${error}`); | ||||
|       throw new Error(`执行报错: ${error.message}`); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   static checkJson(json: string): boolean { | ||||
|     try { | ||||
|       JSON.parse(json); | ||||
|       return true; | ||||
|     } catch { | ||||
|       return false; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   static async setLocalHashInfo( | ||||
|     updateContext: UpdateContext, | ||||
|     hash: string, | ||||
|     info: string | ||||
|   ): Promise<boolean> { | ||||
|     if (!this.checkJson(info)) { | ||||
|       await updateContext.setKv(`hash_${hash}`, info); | ||||
|       throw new Error('校验报错:json字符串格式错误'); | ||||
|     } | ||||
|     await updateContext.setKv(`hash_${hash}`, info); | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   static async getLocalHashInfo( | ||||
|     updateContext: UpdateContext, | ||||
|     hash: string | ||||
|   ): Promise<string> { | ||||
|     const value = await updateContext.getKv(`hash_${hash}`); | ||||
|     if (!this.checkJson(value)) { | ||||
|       throw new Error('校验报错:json字符串格式错误'); | ||||
|     } | ||||
|     return value; | ||||
|   } | ||||
| } | ||||
							
								
								
									
										7
									
								
								harmony/src/main/module.json5
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								harmony/src/main/module.json5
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| { | ||||
|   "module": { | ||||
|     "name": "pushy", | ||||
|     "type": "har", | ||||
|     "deviceTypes": ['default'], | ||||
|   }, | ||||
| } | ||||
							
								
								
									
										8
									
								
								harmony/src/main/resources/base/element/string.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								harmony/src/main/resources/base/element/string.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| { | ||||
|   "string": [ | ||||
|     { | ||||
|       "name": "page_show", | ||||
|       "value": "page from npm package" | ||||
|     } | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										8
									
								
								harmony/src/main/resources/en_US/element/string.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								harmony/src/main/resources/en_US/element/string.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| { | ||||
|   "string": [ | ||||
|     { | ||||
|       "name": "page_show", | ||||
|       "value": "page from npm package" | ||||
|     } | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										8
									
								
								harmony/src/main/resources/zh_CN/element/string.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								harmony/src/main/resources/zh_CN/element/string.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| { | ||||
|   "string": [ | ||||
|     { | ||||
|       "name": "page_show", | ||||
|       "value": "page from npm package" | ||||
|     } | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										3
									
								
								harmony/ts.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								harmony/ts.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
|  | ||||
| export * from './src/main/ets/PushyPackage' | ||||
| export * from './src/main/ets/PushyTurboModule' | ||||
		Reference in New Issue
	
	Block a user
	 波仔糕
					波仔糕