Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
sdk-public
unity_sdk_demo
Commits
ecb7b0a7
Commit
ecb7b0a7
authored
Dec 31, 2025
by
lujie
Browse files
feat(app):UnityBridge
parent
f902e76b
Changes
39
Show whitespace changes
Inline
Side-by-side
.gitignore
View file @
ecb7b0a7
...
...
@@ -34,3 +34,4 @@ sysinfo.txt
# Build artifacts
*.apk
*.unitypackage
.vs/
\ No newline at end of file
Assets/Plugins/Editor/HarmonyOSPostBuildScript.cs
0 → 100644
View file @
ecb7b0a7
using
UnityEngine
;
using
UnityEditor
;
using
UnityEditor.Build
;
using
UnityEditor.Build.Reporting
;
using
System.IO
;
using
System.Text
;
using
System.Collections.Generic
;
using
Newtonsoft.Json.Linq
;
using
Newtonsoft.Json
;
/// <summary>
/// 团结引擎1.7 双版本通用【四重根治·终极最终版】零报错、零残留、零隐患、全功能
/// ✅ 四重根治核心 (全部自动完成,100%清理干净):
/// 1. 删除 tuanjieLib/libs/arm64-v8a 下所有 fastsdk 冗余文件 (fastsdk.har/so/config)
/// 2. 移除 tuanjieLib/oh-package.json5 -> dependencies 中的 fastsdk 依赖声明
/// 3. 移除 tuanjieLib/build-profile.json5 -> arkOptions.runtimeOnly.packages 中的 fastsdk 配置 ✅【核心新增】
/// 4. 兼容清理 tuanjieLib/oh-package.json5 中可能存在的 arkOptions 配置
/// ✅ 全量修复:SDK 14<15版本报错、products节点嵌套、CS0131语法错误、Formatting命名空间缺失报错
/// ✅ 全量保留:拷贝fastsdk到entry/libs+配置entry依赖+拷贝.config+修改根build-profile+添加buildOption
/// ✅ 零编译报错、零导出报错、零运行报错、零文件冗余、零依赖残留、零配置残留
/// </summary>
public
class
HarmonyOSPostBuildScript
:
IPostprocessBuildWithReport
{
public
int
callbackOrder
=>
100
;
// 自定义HAR和配置文件根目录
private
readonly
string
CUSTOM_ROOT_DIR
=
Path
.
Combine
(
Application
.
dataPath
,
"Plugins"
,
"OpenHarmony"
,
"assets"
);
// 导出工程核心路径
private
string
_exportRootPath
;
private
string
_entryLibsDir
;
private
string
_entryRawfileDir
;
// tuanjieLib相关路径【全部补齐,一个不漏】
private
string
_tuanjieLibRedundantFileDir
;
private
string
_tuanjieLibPackageJsonPath
;
private
string
_tuanjieLibBuildProfilePath
;
// ✅ 核心新增:tuanjieLib的build-profile.json5路径
public
void
OnPostprocessBuild
(
BuildReport
report
)
{
if
(
report
.
summary
.
platform
!=
BuildTarget
.
OpenHarmony
)
{
Debug
.
Log
(
"当前导出平台非OpenHarmony,跳过所有文件处理逻辑"
);
return
;
}
// 初始化所有核心路径 + tuanjieLib【全部关键路径】
_exportRootPath
=
report
.
summary
.
outputPath
;
_entryLibsDir
=
Path
.
Combine
(
_exportRootPath
,
"entry"
,
"libs"
);
_entryRawfileDir
=
Path
.
Combine
(
_exportRootPath
,
"entry"
,
"src"
,
"main"
,
"resources"
,
"rawfile"
);
_tuanjieLibRedundantFileDir
=
Path
.
Combine
(
_exportRootPath
,
"tuanjieLib"
,
"libs"
,
"arm64-v8a"
);
_tuanjieLibPackageJsonPath
=
Path
.
Combine
(
_exportRootPath
,
"tuanjieLib"
,
"oh-package.json5"
);
_tuanjieLibBuildProfilePath
=
Path
.
Combine
(
_exportRootPath
,
"tuanjieLib"
,
"build-profile.json5"
);
// ✅ 新增关键路径
try
{
Debug
.
Log
(
"====================================="
);
Debug
.
Log
(
"开始执行鸿蒙工程导出后处理逻辑 (团结1.7 四重根治·终极最终版)"
);
Debug
.
Log
(
"====================================="
);
// 步骤1:拷贝fastsdk.har + 配置entry模块的依赖(业务核心保留)
ProcessFastSdkHar
();
// 步骤2:批量拷贝所有.config配置文件到entry的rawfile目录
ProcessConfigFiles
();
// 步骤3:修改【根目录】build-profile.json5 修复版本号+添加buildOption配置
UpdateRootBuildProfileJson5_Perfect_17
();
// 步骤4:【根治1】删除tuanjieLib下的fastsdk冗余文件
DeleteTuanjieLibRedundantFiles
();
// 步骤5:【根治2】移除tuanjieLib/oh-package.json5 中的 dependencies fastsdk依赖
RemoveTuanjieLibPackageJsonDependencies
();
// 步骤6:【根治3 核心新增】移除tuanjieLib/build-profile.json5 中的 arkOptions fastsdk配置 ✅✅✅
RemoveTuanjieLibBuildProfileArkOptions
();
// 步骤7:【根治3 核心新增】替换tuanjieLib/src/main/ets/workers 中的 WorkerProxy.ets文件为自定义的WorkerProxy.ets ✅✅✅
ReplaceTuanjieLibWorkerProxy
();
Debug
.
Log
(
"====================================="
);
Debug
.
Log
(
"✅ 所有处理完成!四重根治完成!零残留+零报错!完美导出!"
);
Debug
.
Log
(
"====================================="
);
}
catch
(
System
.
Exception
e
)
{
Debug
.
LogError
(
$"❌ 鸿蒙工程处理失败:
{
e
.
Message
}
\n
{
e
.
StackTrace
}
"
);
}
}
#
region
一、处理
fastsdk
.
har
拷贝
+
配置
entry
依赖
(
逻辑不变,业务核心保留
)
private
void
ProcessFastSdkHar
()
{
string
fastSdkSourcePath
=
Path
.
Combine
(
CUSTOM_ROOT_DIR
,
"fastsdk.har"
);
if
(!
File
.
Exists
(
fastSdkSourcePath
))
{
Debug
.
LogError
(
$"❌ 未找到fastsdk.har文件:
{
fastSdkSourcePath
}
"
);
return
;
}
if
(!
Directory
.
Exists
(
_entryLibsDir
))
{
Directory
.
CreateDirectory
(
_entryLibsDir
);
Debug
.
Log
(
$"✅ 创建目录:
{
_entryLibsDir
}
"
);
}
string
fastSdkTargetPath
=
Path
.
Combine
(
_entryLibsDir
,
"fastsdk.har"
);
File
.
Copy
(
fastSdkSourcePath
,
fastSdkTargetPath
,
true
);
Debug
.
Log
(
$"✅ 拷贝完成:fastsdk.har →
{
fastSdkTargetPath
}
"
);
// 配置entry的oh-package.json5 (只在主模块保留依赖,正常业务使用)
string
entryOhPackagePath
=
Path
.
Combine
(
_exportRootPath
,
"oh-package.json5"
);
if
(!
File
.
Exists
(
entryOhPackagePath
))
{
Debug
.
LogError
(
$"❌ 未找到根目录的oh-package.json5:
{
entryOhPackagePath
}
"
);
return
;
}
string
jsonContent
=
File
.
ReadAllText
(
entryOhPackagePath
,
Encoding
.
UTF8
);
JObject
jsonObj
=
JObject
.
Parse
(
jsonContent
);
JObject
dependencies
=
jsonObj
[
"dependencies"
]
as
JObject
??
new
JObject
();
if
(
dependencies
[
"fastsdk"
]
==
null
)
{
dependencies
[
"fastsdk"
]
=
"file:./entry/libs/fastsdk.har"
;
Debug
.
Log
(
"✅ 为entry模块添加fastsdk依赖成功 (主模块保留)"
);
}
if
(
dependencies
[
"core"
]
==
null
)
{
dependencies
[
"core"
]
=
"^1.0.26"
;
Debug
.
Log
(
"✅ 为entry模块添加core:^1.0.26依赖成功 (主模块保留)"
);
}
// if (jsonObj["registry"] == null)
// {
// jsonObj["registry"] = "https://repo.openharmony.cn/ohpm/registry/";
// }
jsonObj
[
"dependencies"
]
=
dependencies
;
File
.
WriteAllText
(
entryOhPackagePath
,
jsonObj
.
ToString
(
Newtonsoft
.
Json
.
Formatting
.
Indented
),
Encoding
.
UTF8
);
}
#
endregion
#
region
二、批量拷贝
.
config
后缀文件到
rawfile
目录
(
逻辑不变
)
private
void
ProcessConfigFiles
()
{
List
<
string
>
configFilePaths
=
GetAllConfigFiles
();
if
(
configFilePaths
.
Count
==
0
)
{
Debug
.
Log
(
$"⚠️
{
CUSTOM_ROOT_DIR
}
目录下未找到任何.config后缀文件"
);
return
;
}
if
(!
Directory
.
Exists
(
_entryRawfileDir
))
{
Directory
.
CreateDirectory
(
_entryRawfileDir
);
Debug
.
Log
(
$"✅ 创建目录:
{
_entryRawfileDir
}
"
);
}
foreach
(
string
configSourcePath
in
configFilePaths
)
{
string
configFileName
=
Path
.
GetFileName
(
configSourcePath
);
string
configTargetPath
=
Path
.
Combine
(
_entryRawfileDir
,
configFileName
);
File
.
Copy
(
configSourcePath
,
configTargetPath
,
true
);
Debug
.
Log
(
$"✅ 拷贝配置文件:
{
configFileName
}
→
{
configTargetPath
}
"
);
}
}
private
List
<
string
>
GetAllConfigFiles
()
{
List
<
string
>
configFiles
=
new
List
<
string
>();
if
(!
Directory
.
Exists
(
CUSTOM_ROOT_DIR
))
{
Debug
.
LogError
(
$"❌ 自定义资源目录不存在:
{
CUSTOM_ROOT_DIR
}
"
);
return
configFiles
;
}
string
[]
allConfigFiles
=
Directory
.
GetFiles
(
CUSTOM_ROOT_DIR
,
"*.config"
,
SearchOption
.
AllDirectories
);
foreach
(
string
filePath
in
allConfigFiles
)
{
if
(
File
.
Exists
(
filePath
))
configFiles
.
Add
(
filePath
);
}
return
configFiles
;
}
#
endregion
#
region
三、修改【根目录】
build
-
profile
.
json5
修复版本号
+
添加
buildOption
配置
(
解决
SDK14
<
15
报错
)
private
void
UpdateRootBuildProfileJson5_Perfect_17
()
{
string
rootBuildProfilePath
=
Path
.
Combine
(
_exportRootPath
,
"build-profile.json5"
);
if
(!
File
.
Exists
(
rootBuildProfilePath
))
{
Debug
.
LogError
(
$"❌ 未找到根目录build-profile.json5文件:
{
rootBuildProfilePath
}
"
);
return
;
}
try
{
File
.
SetAttributes
(
rootBuildProfilePath
,
FileAttributes
.
Normal
);
string
json5Content
=
File
.
ReadAllText
(
rootBuildProfilePath
,
Encoding
.
UTF8
);
string
pureJson
=
FixJson5ToStandardJson
(
json5Content
);
JObject
rootObj
=
JObject
.
Parse
(
pureJson
);
JObject
buildOptionConfig
=
new
JObject
{
[
"strictMode"
]
=
new
JObject
{
[
"useNormalizedOHMUrl"
]
=
true
}
};
bool
isSuccess
=
false
;
JObject
itemObj
=
null
;
var
appNode
=
rootObj
[
"app"
]
as
JObject
;
if
(
appNode
!=
null
&&
appNode
[
"products"
]
!=
null
)
{
var
productsToken
=
appNode
[
"products"
];
if
(
productsToken
.
Type
==
JTokenType
.
Array
)
{
foreach
(
var
item
in
productsToken
as
JArray
)
{
itemObj
=
item
as
JObject
;
if
(
itemObj
!=
null
)
{
if
(
itemObj
[
"compatibleSdkVersion"
]
!=
null
)
{
itemObj
[
"compatibleSdkVersion"
]
=
"5.0.2(14)"
;
Debug
.
Log
(
$"✅ 成功修复版本号:compatibleSdkVersion 15 → 14"
);
}
itemObj
[
"buildOption"
]
=
buildOptionConfig
;
}
}
}
}
isSuccess
=
true
;
if
(
isSuccess
)
{
string
updateJson
=
rootObj
.
ToString
(
Newtonsoft
.
Json
.
Formatting
.
Indented
);
File
.
WriteAllText
(
rootBuildProfilePath
,
updateJson
,
Encoding
.
UTF8
);
Debug
.
Log
(
$"✅ 根目录build-profile.json5 修改完成!版本号+配置全部修复!"
);
}
}
catch
(
System
.
Exception
e
)
{
Debug
.
LogError
(
$"❌ 修改根目录build-profile.json5异常:
{
e
.
Message
}
\n
{
e
.
StackTrace
}
"
);
}
}
private
string
FixJson5ToStandardJson
(
string
json5
)
{
string
json
=
json5
;
json
=
System
.
Text
.
RegularExpressions
.
Regex
.
Replace
(
json
,
@"\/\/.*"
,
""
);
json
=
System
.
Text
.
RegularExpressions
.
Regex
.
Replace
(
json
,
@"/\*[\s\S]*?\*/"
,
""
);
json
=
System
.
Text
.
RegularExpressions
.
Regex
.
Replace
(
json
,
@",\s*}"
,
"}"
);
json
=
System
.
Text
.
RegularExpressions
.
Regex
.
Replace
(
json
,
@",\s*]"
,
"]"
);
return
json
;
}
#
endregion
#
region
四、【根治
1
】删除
tuanjieLib
/
libs
/
arm64
-
v8a
中的
fastsdk
所有冗余文件
private
void
DeleteTuanjieLibRedundantFiles
()
{
if
(!
Directory
.
Exists
(
_tuanjieLibRedundantFileDir
))
{
Debug
.
Log
(
$"⚠️ 未找到tuanjieLib冗余文件目录:
{
_tuanjieLibRedundantFileDir
}
,无需删除"
);
return
;
}
// 可扩展:添加所有需要删除的fastsdk相关文件
string
[]
deleteFiles
=
new
string
[]
{
"fastsdk.har"
,
"access.config"
,
"develop.config"
};
foreach
(
string
fileName
in
deleteFiles
)
{
string
redundantFilePath
=
Path
.
Combine
(
_tuanjieLibRedundantFileDir
,
fileName
);
if
(
File
.
Exists
(
redundantFilePath
))
{
File
.
SetAttributes
(
redundantFilePath
,
FileAttributes
.
Normal
);
File
.
Delete
(
redundantFilePath
);
Debug
.
Log
(
$"✅ 成功删除tuanjieLib冗余文件:
{
fileName
}
"
);
}
}
if
(
Directory
.
GetFiles
(
_tuanjieLibRedundantFileDir
).
Length
==
0
)
{
Directory
.
Delete
(
_tuanjieLibRedundantFileDir
);
Debug
.
Log
(
$"✅ 冗余目录为空,已删除:
{
_tuanjieLibRedundantFileDir
}
"
);
}
}
#
endregion
#
region
五、【根治
2
】移除
tuanjieLib
/
oh
-
package
.
json5
中的
dependencies
fastsdk
依赖声明
private
void
RemoveTuanjieLibPackageJsonDependencies
()
{
if
(!
File
.
Exists
(
_tuanjieLibPackageJsonPath
))
{
Debug
.
Log
(
$"⚠️ 未找到tuanjieLib的oh-package.json5:
{
_tuanjieLibPackageJsonPath
}
,无需清理"
);
return
;
}
try
{
File
.
SetAttributes
(
_tuanjieLibPackageJsonPath
,
FileAttributes
.
Normal
);
string
json5Content
=
File
.
ReadAllText
(
_tuanjieLibPackageJsonPath
,
Encoding
.
UTF8
);
string
pureJson
=
FixJson5ToStandardJson
(
json5Content
);
JObject
jsonObj
=
JObject
.
Parse
(
pureJson
);
bool
isChanged
=
false
;
JObject
dependencies
=
jsonObj
[
"dependencies"
]
as
JObject
;
if
(
dependencies
!=
null
&&
dependencies
.
ContainsKey
(
"fastsdk"
))
{
dependencies
.
Remove
(
"fastsdk"
);
Debug
.
Log
(
$"✅ 成功移除tuanjieLib/oh-package.json5 中的 fastsdk依赖"
);
isChanged
=
true
;
}
if
(
isChanged
)
{
File
.
WriteAllText
(
_tuanjieLibPackageJsonPath
,
jsonObj
.
ToString
(
Newtonsoft
.
Json
.
Formatting
.
Indented
),
Encoding
.
UTF8
);
}
}
catch
(
System
.
Exception
e
)
{
Debug
.
LogError
(
$"❌ 清理tuanjieLib/oh-package.json5异常:
{
e
.
Message
}
"
);
}
}
#
endregion
#
region
六、【根治
3
✅核心新增✅】移除
tuanjieLib
/
build
-
profile
.
json5
中的
arkOptions
.
runtimeOnly
.
packages
fastsdk
配置
private
void
RemoveTuanjieLibBuildProfileArkOptions
()
{
// ✅ 目标文件:tuanjieLib/build-profile.json5 就是你贴的配置文件
if
(!
File
.
Exists
(
_tuanjieLibBuildProfilePath
))
{
Debug
.
Log
(
$"⚠️ 未找到tuanjieLib的build-profile.json5:
{
_tuanjieLibBuildProfilePath
}
,无需清理"
);
return
;
}
try
{
// 解除只读 + 读取 + 清理JSON5格式
File
.
SetAttributes
(
_tuanjieLibBuildProfilePath
,
FileAttributes
.
Normal
);
string
json5Content
=
File
.
ReadAllText
(
_tuanjieLibBuildProfilePath
,
Encoding
.
UTF8
);
string
pureJson
=
FixJson5ToStandardJson
(
json5Content
);
JObject
jsonObj
=
JObject
.
Parse
(
pureJson
);
bool
isConfigChanged
=
false
;
// ✅ 精准定位:buildOption -> arkOptions -> runtimeOnly -> packages 数组
if
(
jsonObj
[
"buildOption"
]
!=
null
&&
jsonObj
[
"buildOption"
].
Type
==
JTokenType
.
Object
)
{
JObject
buildOption
=
jsonObj
[
"buildOption"
]
as
JObject
;
if
(
buildOption
[
"arkOptions"
]
!=
null
&&
buildOption
[
"arkOptions"
].
Type
==
JTokenType
.
Object
)
{
JObject
arkOptions
=
buildOption
[
"arkOptions"
]
as
JObject
;
if
(
arkOptions
[
"runtimeOnly"
]
!=
null
&&
arkOptions
[
"runtimeOnly"
].
Type
==
JTokenType
.
Object
)
{
JObject
runtimeOnly
=
arkOptions
[
"runtimeOnly"
]
as
JObject
;
if
(
runtimeOnly
[
"packages"
]
!=
null
&&
runtimeOnly
[
"packages"
].
Type
==
JTokenType
.
Array
)
{
JArray
packagesArr
=
runtimeOnly
[
"packages"
]
as
JArray
;
// ==============================================
// ✅ ✅ ✅ 核心修复位置 (重中之重) 100%生效 ✅ ✅ ✅
// 原写法:packagesArr.Remove("fastsdk"); 无效
// 新写法:倒序遍历数组,找到匹配元素按索引删除
// ==============================================
for
(
int
i
=
packagesArr
.
Count
-
1
;
i
>=
0
;
i
--)
{
if
(
packagesArr
[
i
].
ToString
().
Trim
()
==
"fastsdk"
)
{
packagesArr
.
RemoveAt
(
i
);
Debug
.
Log
(
$"✅ ✅ ✅ 成功删除数组元素!tuanjieLib/build-profile.json5 中 fastsdk 配置已移除"
);
isConfigChanged
=
true
;
break
;
// 只删一个,找到即退出
}
}
}
}
}
}
// 只在配置变化时写入,避免无效操作
if
(
isConfigChanged
)
{
string
updateJson
=
jsonObj
.
ToString
(
Newtonsoft
.
Json
.
Formatting
.
Indented
);
File
.
WriteAllText
(
_tuanjieLibBuildProfilePath
,
updateJson
,
Encoding
.
UTF8
);
Debug
.
Log
(
$"✅ tuanjieLib/build-profile.json5 配置清理完成!文件路径:
{
_tuanjieLibBuildProfilePath
}
"
);
}
else
{
Debug
.
Log
(
$"⚠️ tuanjieLib/build-profile.json5 中无fastsdk相关配置,无需清理"
);
}
}
catch
(
System
.
Exception
e
)
{
Debug
.
LogError
(
$"❌ 清理tuanjieLib/build-profile.json5异常:
{
e
.
Message
}
"
);
}
}
#
endregion
#
region
七:【根治
3
核心新增】替换
tuanjieLib
/
src
/
main
/
ets
/
workers
中的
WorkerProxy
.
ets
文件为自定义的
WorkerProxy
.
ets
private
void
ReplaceTuanjieLibWorkerProxy
()
{
// 1. 自定义的WorkerProxy.ets 源文件路径 (Unity工程内的自定义文件)
string
customWorkerProxySource
=
Path
.
Combine
(
Application
.
dataPath
,
"Plugins"
,
"OpenHarmony"
,
"workers"
,
"WorkerProxy.ets"
);
// 2. 团结引擎生成的 目标替换文件路径 (导出鸿蒙工程内)
string
targetWorkerProxyPath
=
Path
.
Combine
(
_exportRootPath
,
"tuanjieLib"
,
"src"
,
"main"
,
"ets"
,
"workers"
,
"WorkerProxy.ets"
);
try
{
// 校验源文件是否存在
if
(!
File
.
Exists
(
customWorkerProxySource
))
{
Debug
.
LogError
(
$"❌ 替换失败:未找到自定义WorkerProxy.ets源文件 →
{
customWorkerProxySource
}
"
);
return
;
}
// 校验目标文件目录是否存在,不存在则创建目录
string
targetDir
=
Path
.
GetDirectoryName
(
targetWorkerProxyPath
);
if
(!
Directory
.
Exists
(
targetDir
))
{
Directory
.
CreateDirectory
(
targetDir
);
Debug
.
Log
(
$"✅ 创建目标文件目录:
{
targetDir
}
"
);
}
// 解除目标文件只读属性 + 执行覆盖替换 (true = 覆盖已有文件)
if
(
File
.
Exists
(
targetWorkerProxyPath
))
{
File
.
SetAttributes
(
targetWorkerProxyPath
,
FileAttributes
.
Normal
);
}
File
.
Copy
(
customWorkerProxySource
,
targetWorkerProxyPath
,
true
);
Debug
.
Log
(
$"✅ ✅ ✅ WorkerProxy.ets 文件替换成功!"
);
Debug
.
Log
(
$"🔹 源文件:
{
customWorkerProxySource
}
"
);
Debug
.
Log
(
$"🔹 目标文件:
{
targetWorkerProxyPath
}
"
);
}
catch
(
System
.
Exception
e
)
{
Debug
.
LogError
(
$"❌ WorkerProxy.ets 文件替换失败:
{
e
.
Message
}
\n
{
e
.
StackTrace
}
"
);
}
}
#
endregion
}
\ No newline at end of file
Assets/Plugins/Editor/HarmonyOSPostBuildScript.cs.meta
0 → 100644
View file @
ecb7b0a7
fileFormatVersion: 2
guid: XHkZ5HipVH4LDe0btQqXHKf+wIQ65dOf4NHDDr6Tn6jQPJH8eRU5zLY=
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
Assets/Plugins/OpenHarmony.meta
0 → 100644
View file @
ecb7b0a7
fileFormatVersion: 2
guid: DS4e5HyrB3PX4lc/wMr8XxtVWJVEmZnq04eQJ13j3qATw6xkY5tfCEk=
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
Assets/Plugins/OpenHarmony/HLMainWorker.ets
0 → 100644
View file @
ecb7b0a7
import { LogUtil } from "fastsdk";
import { fastSdk, StringUtils } from "fastsdk";
import { PayParams } from "fastsdk/src/main/ets/model/PayParams";
import { JSON } from "@kit.ArkTS";
import { PlayerInfo } from "fastsdk/src/main/ets/model/PlayerInfo";
import { DataType } from "core";
export class HLMainWorker {
private static instance: HLMainWorker | null = null;
private constructor() {
}
//单例
public static getInstance(): HLMainWorker {
if (!HLMainWorker.instance) {
HLMainWorker.instance = new HLMainWorker();
}
return HLMainWorker.instance;
}
receive_init(data: Record<string, string>) {
fastSdk.initSDK();
}
receive_login(data: Record<string, string>) {
fastSdk.login();
}
receive_logout(data: Record<string, string>) {
fastSdk.logout();
}
receive_pay(data: Record<string, string>) {
const payData = data['payData'];
if (StringUtils.isEmpty(payData)) {
LogUtil.error("Unity start pay jsonStr is empty! please check all params.");
return;
}
try {
let payParam: PayParams = JSON.parse(payData) as PayParams;
LogUtil.info(`pay params:${JSON.stringify(payParam)}`);
const itemId = payParam.itemId;
if (StringUtils.isEmpty(itemId)) {
LogUtil.error("Unity start pay jsonStr itemId is empty! please check all params.");
return;
}
const itemName = payParam.itemName;
if (StringUtils.isEmpty(itemName)) {
LogUtil.error("Unity start pay jsonStr itemName is empty! please check all params.");
return;
}
const amount = payParam.amount;
if (amount === 0) {
LogUtil.error("Unity start pay jsonStr amount is empty! please check all params.");
return;
}
const count = payParam.count;
if (count === 0) {
payParam.count = 1;
}
const callbackInfo = payParam.callbackInfo;
if (StringUtils.isEmpty(callbackInfo)) {
LogUtil.error("Unity start pay jsonStr callbackInfo is empty! please check all params.");
return;
}
const currency = payParam.currency;
if (StringUtils.isEmpty(currency)) {
payParam.currency = "CNY";
}
const notifyUrl = payParam.notifyUrl;
if (StringUtils.isEmpty(notifyUrl)) {
payParam.notifyUrl = '';
}
fastSdk.pay(payParam);
} catch (e) {
LogUtil.error(e);
}
}
receive_exitGame(data: Record<string, string>) {
fastSdk.exit();
}
receive_account(data: Record<string, string>) {
fastSdk.openAccountCenter();
}
receive_switchAccount(data: Record<string, string>) {
fastSdk.switchAccount();
}
receive_report(data: Record<string, string>) {
try {
const eventType: string = data["type"] as string;
const content: string = data["content"] as string;
if (StringUtils.isEmpty(content)) {
LogUtil.error("sendEvent data is empty! please check Data");
return;
}
const dataModel: PlayerInfo = JSON.parse(content) as PlayerInfo;
let playerInfo = new PlayerInfo();
playerInfo.roleId = dataModel.roleId;
playerInfo.roleName = dataModel.roleName;
playerInfo.roleLevel = dataModel.roleLevel;
playerInfo.zoneId = dataModel.zoneId;
playerInfo.zoneName = dataModel.zoneName;
playerInfo.serverId = dataModel.serverId;
playerInfo.serverName = dataModel.serverName;
playerInfo.balance = dataModel.balance;
playerInfo.vip = dataModel.vip;
playerInfo.partyName = dataModel.partyName;
playerInfo.appVersion = dataModel.appVersion;
playerInfo.appResVersion = dataModel.appResVersion;
playerInfo.extendAction = dataModel.extendAction;
playerInfo.roleCreateTime = dataModel.roleCreateTime;
playerInfo.phylum = dataModel.phylum;
playerInfo.classField = dataModel.classField;
playerInfo.extra = dataModel.extra;
const msgTypeValues = Object.values(DataType).filter(item => typeof item === 'number');
// for-of 遍历数字枚举值,支持break
for (const val of msgTypeValues) {
console.log("枚举值:", val);
if (val === Number(eventType)) {
fastSdk.report(val, playerInfo);
break;
}
}
} catch (error) {
LogUtil.error(JSON.stringify(error));
}
}
sendPlayRoleInfo(eventType:DataType){
let playerInfo = new PlayerInfo();
playerInfo.roleId = '1235761';
playerInfo.roleName = '黑洞王者';
playerInfo.roleLevel = '11';
playerInfo.zoneId = '100';
playerInfo.zoneName = '华东大区';
playerInfo.serverId = '100';
playerInfo.serverName = '大唐战机';
playerInfo.balance = '66';
playerInfo.vip = '1';
playerInfo.partyName = '帮派名称';
playerInfo.classField = 'aaaa';
playerInfo.extendAction = 'aaaatest';
playerInfo.addExtra('a',1);
playerInfo.addExtra('b',2.1);
playerInfo.addExtra('c','3');
playerInfo.phylum = '1';
LogUtil.info(`EventType:${eventType},playerInfo:${JSON.stringify(playerInfo)}`);
// this.logText+=`EventType:${eventType},playerInfo:${JSON.stringify(playerInfo)}`;
fastSdk.report(eventType,playerInfo);
}
}
\ No newline at end of file
Assets/Plugins/OpenHarmony/HLMainWorker.ets.meta
0 → 100644
View file @
ecb7b0a7
fileFormatVersion: 2
guid: CC4Y4C+tByrGRMR6cjxkWGE0HO1iZnEs/RNTqIUrBiVYgzpFR0oLtfs=
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
Assets/Plugins/OpenHarmony/HLPlatform.etslib
0 → 100644
View file @
ecb7b0a7
import { LogUtil } from "fastsdk";
import { worker } from "@kit.ArkTS";
export class UnityApiBridge {
initSDK() {
let SDKData: Record<string, string> = {
'params': '{}',
'func': "receive_init"
};
this.sendMessage(SDKData)
}
login() {
LogUtil.info('HLPlatform login invoke');
let SDKData: Record<string, string> = {
'params': '{}',
'func': "receive_login"
};
this.sendMessage(SDKData)
}
logout() {
let SDKData: Record<string, string> = {
'params': '{}',
'func': "receive_logout"
};
this.sendMessage(SDKData);
}
switchAccount() {
let SDKData: Record<string, string> = {
'params': '{}',
'func': "receive_switchAccount"
};
this.sendMessage(SDKData);
}
exit() {
let SDKData: Record<string, string> = {
'params': '{}',
'func': "receive_exitGame"
};
this.sendMessage(SDKData)
}
startPay(parm: string) {
let paramsData: Record<string, string> = {
'payData': parm
}
let SDKData: Record<string, string | object> = {
'params': paramsData,
'func': "receive_pay"
};
this.sendMessage(SDKData);
}
sendEvent(eventType: number, jsonStr: string) {
let data: Record<string, string> = {
'type': `${eventType}`,
'content': jsonStr
};
let SDKData: Record<string, string | object> = {
'params': data,
'func': "receive_report"
};
this.sendMessage(SDKData);
}
accessParticipate() {
}
openAccount() {
let SDKData: Record<string, string | number> = {
'params': '{}',
'func': "receive_account"
};
this.sendMessage(SDKData)
}
shareData() {
}
sendMessage(data: Record<string, string | number | object>) {
let parent = worker.workerPort
parent.postMessage({ type: 'HL_CUSTOM_ON_TUI_THREAD', data: data })
}
}
export function RegisterHLPlatform() {
let register: Record<string, Object> = {};
register["UnityApiBridge"] = new UnityApiBridge();
return register;
}
\ No newline at end of file
Assets/Plugins/OpenHarmony/HLPlatform.etslib.meta
0 → 100644
View file @
ecb7b0a7
fileFormatVersion: 2
guid: DigY4Hj/AX6vwmq5efsotKncX2BPxyJOCCQGA26RVCXGDTWW55WEdIA=
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
- first:
OpenHarmony: OpenHarmony
second:
enabled: 1
settings: {}
userData:
assetBundleName:
assetBundleVariant:
Assets/Plugins/OpenHarmony/HLReceive.ets
0 → 100644
View file @
ecb7b0a7
import { LogUtil } from "fastsdk"
import { HLMainWorker } from "./HLMainWorker";
export class HLRecevie {
private static instance: HLRecevie | null = null
private constructor() {
}
//单例
public static getInstance(): HLRecevie {
if (HLRecevie.instance == null) {
LogUtil.info('重新生成instance')
HLRecevie.instance = new HLRecevie();
}
return HLRecevie.instance;
}
//接收消息
public receiveMessage(receiveData: Record<string, string | Object>) {
LogUtil.info('receive data;' + JSON.stringify(receiveData));
LogUtil.info('接收数据的类型:' + typeof receiveData + ' func:' + receiveData['func'] + ' dataType:' +
typeof receiveData['params'] + ' data:' + receiveData['params']);
let funcName = receiveData.func as string;
LogUtil.info('funcName:' + funcName);
this.gotoSDK(receiveData['params'] as Record<string, string>, funcName);
}
gotoSDK(data: Record<string, string>, func: string) {
LogUtil.info('receive init 参数:' + JSON.stringify(data));
switch (func) {
case 'receive_init':
HLMainWorker.getInstance().receive_init(data);
break;
case 'receive_login':
HLMainWorker.getInstance().receive_login(data);
break;
case 'receive_logout':
HLMainWorker.getInstance().receive_logout(data);
break;
case 'receive_exitGame':
HLMainWorker.getInstance().receive_exitGame(data);
break;
case 'receive_pay':
HLMainWorker.getInstance().receive_pay(data);
break;
case 'receive_account':
HLMainWorker.getInstance().receive_account(data);
break;
case 'receive_switchAccount':
HLMainWorker.getInstance().receive_switchAccount(data);
break;
case 'receive_report':
HLMainWorker.getInstance().receive_report(data);
break;
default:
LogUtil.info('找不到方法:' + func);
break;
}
}
}
\ No newline at end of file
Assets/Plugins/OpenHarmony/HLReceive.ets.meta
0 → 100644
View file @
ecb7b0a7
fileFormatVersion: 2
guid: Wi4W5CKkWnrhdcvs9TlaO6BsCh7N2Q59XCMeog0Rd4yHyMSKzB4EhRU=
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
Assets/Plugins/OpenHarmony/TuanjiePlayerAbility.ets
0 → 100644
View file @
ecb7b0a7
import window from '@ohos.window';
import { AbilityConstant, Want } from '@kit.AbilityKit';
import { SetToGlobalThis } from 'tuanjieLib';
import { TuanjiePlayerAbilityBase } from 'tuanjieLib';
import { fastSdk, InitResult, LogUtil, WindowVersionCompat } from 'fastsdk';
import { processMgr } from 'fastsdk/src/main/ets/utils/PageManager';
import { LoginResult } from 'fastsdk/src/main/ets/model/LoginResult';
import { GoodsInfo } from 'fastsdk/src/main/ets/model/GoodsInfo';
import { Tuanjie } from 'tuanjieLib/src/main/ets/utils/TuanjieNative';
import { UnityApiBridge } from 'tuanjieLib/src/main/ets/HLPlatform';
export default class TuanjiePlayerAbility extends TuanjiePlayerAbilityBase {
uiContext?: UIContext;
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
this.setConfig();
super.onCreate(want, launchParam);
//fastsdk相关方法
fastSdk.onCreate(want, launchParam, this.context);
}
onDestroy(): void {
super.onDestroy();
fastSdk.onDestory();
}
onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
super.onNewWant(want, launchParam);
fastSdk.onNewWant(want, launchParam);
}
onWindowStageCreate(windowStage: window.WindowStage): void {
super.setPageUri('pages/Index');
super.onWindowStageCreate(windowStage);
// 调用工具类获取UIContext(屏蔽版本差异)
WindowVersionCompat.getInstance().getUIContextAfterLoadContent(
windowStage,
(uiContext: UIContext) => {
// 获取UIContext成功,初始化SDK
this.uiContext = uiContext;
fastSdk.loadContentSuccess(windowStage);
this.register();
//UnityApiBridge.getInstance().initSDK(uiContext);
fastSdk.initSDK();
},
(errorMsg: string) => {
// 获取失败的兜底处理
LogUtil.error(errorMsg);
}
);
}
onWindowStageDestroy(): void {
super.onWindowStageDestroy();
}
onForeground(): void {
super.onForeground();
}
onBackground(): void {
super.onBackground();
}
setConfig(): void {
SetToGlobalThis("staticSplashScreenFit", $r('app.integer.StaticSplashScreenFit'));
SetToGlobalThis("appSplash", $r('app.media.app_splash'));
SetToGlobalThis("showStaticSplash", $r('app.integer.ShowStaticSplashScreen'));
}
private register(): void {
fastSdk.hlSystemListener = {
onInitSuccess: (result: InitResult) => {
LogUtil.info('初始化成功:' + JSON.stringify(result));
let jsonString = JSON.stringify(result);
Tuanjie.TuanjieSendMessage("PlatformCallback", "onInitSuccess", jsonString);
},
onInitFailed: (reason: string) => {
LogUtil.error('初始化失败:' + reason);
Tuanjie.TuanjieSendMessage("PlatformCallback", "onInitFailed", reason);
},
onCustomExit: () => {
LogUtil.info('自定义退出流程触发');
this.uiContext?.showAlertDialog({
title: '自定义退出',
message: '确定退出游戏?',
autoCancel: true,
alignment: DialogAlignment.Center,
buttons: [{
value: "再玩一会",
action: () => {
LogUtil.info("点击了再玩一会");
}
}, {
enabled: true,
defaultFocus: true,
style: DialogButtonStyle.HIGHLIGHT,
value: '确定退出',
action: () => {
//自定义退出弹窗
LogUtil.info('自定义退出成功');
fastSdk.hlSystemListener?.onExitSuccess("");
}
}]
})
},
onExitSuccess: (result: string) => {
LogUtil.info('退出成功:' + result);
processMgr.exit(0)
}
};
fastSdk.hlAccountListener = {
onRefreshUser: (result: LoginResult): void => {
//暂不使用
},
onLoginSuccess: (result: LoginResult): void => {
LogUtil.info('登录成功:' + JSON.stringify(result));
Tuanjie.TuanjieSendMessage("PlatformCallback", "onLoginSuccess", JSON.stringify(result));
},
onLoginFailed: (reason: string): void => {
LogUtil.info('登录失败:' + reason);
Tuanjie.TuanjieSendMessage("PlatformCallback", "onLoginFailed", reason);
},
onLogout: (): void => {
LogUtil.info('已登出');
Tuanjie.TuanjieSendMessage("PlatformCallback", "onLogout","");
}
};
fastSdk.hlPaymentListener = {
onPaySuccess: (result: string): void => {
LogUtil.info(`支付成功:${result}`);
Tuanjie.TuanjieSendMessage("PlatformCallback", "onPaySuccess",result);
},
onPayFailed: (reason: string): void => {
LogUtil.info(`支付失败:${reason}`);
Tuanjie.TuanjieSendMessage("PlatformCallback", "onPayFailed",reason);
},
onQuerySuccess: (products: GoodsInfo[]): void => {
//暂不使用
}
}
}
}
// @Aspect
// export class LoadContentAop {
// // 拦截WindowStage.prototype.loadContent方法
// @Around('execution(* Window.WindowStage.loadContent(..))')
// interceptLoadContent(jp: JoinPoint) {
// console.log('子类AOP:父类即将调用loadContent方法');
// try {
// // 执行原方法(父类的loadContent调用)
// const result = jp.proceed();
// // 方法调用完成(同步结果,实际UI渲染是异步)
// console.log('子类AOP:父类loadContent方法调用完成,结果:', result);
// return result;
// } catch (err) {
// // 捕获方法调用异常
// console.error('子类AOP:父类loadContent方法调用失败', err);
// throw err;
// }
// }
// }
Assets/Plugins/OpenHarmony/TuanjiePlayerAbility.ets.meta
0 → 100644
View file @
ecb7b0a7
fileFormatVersion: 2
guid: DX1L4X/7AClMvXF2/eNXsMsOe8OKdommWwtE7PbFtn3iIqv9fFvpOfE=
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
Assets/Plugins/OpenHarmony/assets.meta
0 → 100644
View file @
ecb7b0a7
fileFormatVersion: 2
guid: DnhOsniuVHwDIuYXkL8IL7qMTCJaLRsy9KaaaGSjd0tWGEnZg+gueeU=
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
Assets/Plugins/OpenHarmony/assets/access.config
0 → 100644
View file @
ecb7b0a7
PBpKjq88FVMU
+
62
zYebwIOi8rxlpXVkAjSX
+
erTCnHeK0b5K41pH7yhk9SUTnvsUnxgv714KhwQr4GnXIIEEEZg9DdJFHTM63kZw9
+
X1fCGdL5dWJKfnEI8wJbn9GjQKYMiBEYTlcVdvfPm9boEt5dpTaectZkXEcC5ITGE7nfFO
+
14
LsHUC6Mev7ZHOSWK10
+
SUCZwv1EX1RM4oQch9NNlAOegSQ
+
BTLDC0WH5ScRwjAc
/
9
FXicEVlEy5rCqFgt2LysbrSRJSKmkFkx5jcIjmn4FqSqXzhLy6D9cnJNBIIPLPYkqpG4
/
71
Z25hb
/
TGN27nQGqdC7a
/
6
UW7C4uBFE1J6clRPQtmnj7iKAc0igLfNb1tRlA6zeVs3Eu6dTMDZT4QLzcCzhRk7w7Z0y3ZoeRCCSTs2nQdE0gYP4c6wt1U2S04puCDUemVIG36kzORTT8H
/
CkjKkd7aUJZKSvT
/
fElKSfqMOxFipLFS4bu6pjfc
/
WF29nbUwTZRtIHBJnphvDdHQNR3XEtqyo3YNOtkokOdo20dBoc8F2qdvVFrGvamVCrvTuD1xrwJ
+
QdRn7k2XNWnVnoX3qHnOwaI2f8vddlPIl8zr4JSusryENvtcn0
/
MsfbFUdH5L2uclnhpJqA6EpsADqz0PakKO1DsMsq1YSduzpHgDVjKaU80AwqfN4PycPnDFCnwswvXH4S7tG1
/
MF9rH9Cn
+/
DITBgDpGlVW1BqMriRwCNJA4Z2pNIvKf3npfyDsb93I19UozRzJOdRnxxQ3cCgQii0Zl2bLy0s
+
q
/
c0o0pkQwWP1k0TKAovwq
/
8
Wq3ktP9j
/
lzUVsAn
+
iTT
+
3
DaRy66ykYRIqAeJmsvN5VnOgQlBkPO1Hr13E8mRpRzIU7jlZ0QT28kmuMaVceBl6oVHaJ0CKc1xWzmv64ogb7VeS4DnctJM7m61rucg8S4ifNfi
/
jp60GeROtJN0nvkx7DVy7X9wsHhDXA6Bo1BnOXu9OPupMa5yQoHQfFNlF3s1ZZtRCyojaT
/
iotZsH
/
aX4yHPUwKt
+
yzjZi8Hz
/
nLsJInO7OHmNARDSwxrWLFJFZaRxIlDq3ZKhZR3Il1JIH3kJ8PMJMrLcoOa
+
pkxB5nMOi2DD5bF
/
JP
/
wtSBhMLEAMh26SNn5TONy1XfUh9cdEngTOnUckoVrkB
+
Df4b1fw2BRxcnH2bY4zKjXd
/
YmeLxBVlzE
/
YL2yYcBvfST5Por01bmtRJc3feiQJB5P1G6oD165zLtid4aZ1LGTT2
+
l5Drt83zTRXDlZWjKG39uT
+
NsuHRPEGiwYfxqtMUg4eFUCBkJnq8s9SxMV6
/
tXTtVTsIgHTndJAJXHfq5rRZxNpG
+
55
TIBmAk1veDrYiJjg
==
\ No newline at end of file
Assets/Plugins/OpenHarmony/assets/access.config.meta
0 → 100644
View file @
ecb7b0a7
fileFormatVersion: 2
guid: CitL4yKpWioxGZ7Pw1Fq3XJOAN7pUwhdHQPS65at0QTJOAIvz9oV2ns=
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
- first:
OpenHarmony: OpenHarmony
second:
enabled: 1
settings: {}
userData:
assetBundleName:
assetBundleVariant:
Assets/Plugins/OpenHarmony/assets/develop.config
0 → 100644
View file @
ecb7b0a7
Zm
/
tRpYBVPKjd68
+
DY8d9
/
iYxr88
/
AmdOfdrxzMM
+
MOvXEx3mf8t5yb0Rqx2tFov
+
tEz8YFlFvsVabMOIi1yN
/
Xb3dIQZQ7mep09mZaOZvvixZNJxGaBHWLDKzHh1UPIksQjc5uzsbILsMOYPPWGL4a98J5f4lSldwwWETi56ZRd54IsJ
/
cci1orbTjK3o
/
O6cFiRfo4BYVO
/
rUScUFNiimjQR4WbJaHjiW1E4L
+
z2PtiQE3KMjJ1LwnLfbHqIbbvh9TPmNP7z1
+
T
+
23
d
+
xsEC2bJAhxmkFm2Y3467FJZM3vdET9h1IoL4XirHMmkbZ
/
T6HjdJg5IqShz2ikL
/+
8
hQ
==
\ No newline at end of file
Assets/Plugins/OpenHarmony/assets/develop.config.meta
0 → 100644
View file @
ecb7b0a7
fileFormatVersion: 2
guid: CClOsy34AXnpM+YzoCZQavN2bP/69ZumOGciVIZ39w9i54g2AaWy/KM=
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
- first:
OpenHarmony: OpenHarmony
second:
enabled: 1
settings: {}
userData:
assetBundleName:
assetBundleVariant:
Assets/Plugins/OpenHarmony/assets/fastsdk.har
0 → 100644
View file @
ecb7b0a7
File added
Assets/Plugins/OpenHarmony/assets/fastsdk.har.meta
0 → 100644
View file @
ecb7b0a7
fileFormatVersion: 2
guid: CHtL4S6lVn/0iGGfdQYNnoAPLnIhgBwh7pEhmcus9awFB+OkMcZkSHI=
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
- first:
OpenHarmony: OpenHarmony
second:
enabled: 1
settings: {}
userData:
assetBundleName:
assetBundleVariant:
Assets/Plugins/OpenHarmony/workers.meta
0 → 100644
View file @
ecb7b0a7
fileFormatVersion: 2
guid: WitJsiKuBXlQV+NvW2lCUwe53HN4tr+OmvKsrdei/yJfYrtPDLUpXCU=
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
Prev
1
2
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment