pindo 5.2.4 → 5.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/pindo/base/aeshelper.rb +23 -2
- data/lib/pindo/base/pindocontext.rb +476 -0
- data/lib/pindo/client/pgyer_feishu_oauth_cli.rb +343 -80
- data/lib/pindo/client/pgyerclient.rb +30 -20
- data/lib/pindo/command/android/autobuild.rb +58 -22
- data/lib/pindo/command/android/build.rb +27 -16
- data/lib/pindo/command/android/debug.rb +25 -15
- data/lib/pindo/command/dev/debug.rb +2 -51
- data/lib/pindo/command/dev/feishu.rb +19 -2
- data/lib/pindo/command/ios/adhoc.rb +2 -1
- data/lib/pindo/command/ios/autobuild.rb +39 -9
- data/lib/pindo/command/ios/build.rb +7 -0
- data/lib/pindo/command/ios/debug.rb +2 -132
- data/lib/pindo/command/lib/lint.rb +24 -1
- data/lib/pindo/command/setup.rb +24 -4
- data/lib/pindo/command/unity/apk.rb +20 -0
- data/lib/pindo/command/unity/ipa.rb +27 -3
- data/lib/pindo/command/unity/web.rb +15 -10
- data/lib/pindo/command/web/autobuild.rb +5 -0
- data/lib/pindo/command.rb +58 -1
- data/lib/pindo/module/android/android_build_config_helper.rb +427 -0
- data/lib/pindo/module/android/apk_helper.rb +23 -25
- data/lib/pindo/module/android/base_helper.rb +572 -0
- data/lib/pindo/module/android/build_helper.rb +8 -318
- data/lib/pindo/module/android/gp_compliance_helper.rb +668 -0
- data/lib/pindo/module/android/gradle_helper.rb +746 -3
- data/lib/pindo/module/appselect.rb +18 -5
- data/lib/pindo/module/build/buildhelper.rb +120 -29
- data/lib/pindo/module/build/unityhelper.rb +674 -18
- data/lib/pindo/module/build/versionhelper.rb +146 -0
- data/lib/pindo/module/cert/certhelper.rb +33 -2
- data/lib/pindo/module/cert/xcodecerthelper.rb +3 -1
- data/lib/pindo/module/pgyer/pgyerhelper.rb +114 -31
- data/lib/pindo/module/xcode/xcodebuildconfig.rb +232 -0
- data/lib/pindo/module/xcode/xcodebuildhelper.rb +0 -1
- data/lib/pindo/version.rb +356 -86
- data/lib/pindo.rb +72 -3
- metadata +7 -3
@@ -14,6 +14,11 @@ module Pindo
|
|
14
14
|
# 命令的简要说明 - 编译Unity工程生成Android APK
|
15
15
|
self.summary = '编译Unity工程生成Android APK'
|
16
16
|
|
17
|
+
# 启用缓存机制
|
18
|
+
def self.use_cache?
|
19
|
+
true # 此命令启用缓存
|
20
|
+
end
|
21
|
+
|
17
22
|
# 命令的详细说明,包含用法示例
|
18
23
|
self.description = <<-DESC
|
19
24
|
编译Unity工程生成Android APK。
|
@@ -108,6 +113,21 @@ module Pindo
|
|
108
113
|
if isLibrary
|
109
114
|
pindo_android_project_dir = android_export_lib_dir
|
110
115
|
end
|
116
|
+
|
117
|
+
# 验证构建输出目录是否存在
|
118
|
+
unless File.directory?(pindo_android_project_dir)
|
119
|
+
puts "\e[31m错误: Unity 构建输出目录不存在: #{pindo_android_project_dir}\e[0m"
|
120
|
+
puts "\e[33m可能的原因:\e[0m"
|
121
|
+
puts "• Unity 构建失败但未正确报告错误"
|
122
|
+
puts "• GoodUnityBuild 配置的输出路径不正确"
|
123
|
+
puts "• Unity 项目配置问题"
|
124
|
+
puts "\e[33m建议检查:\e[0m"
|
125
|
+
puts "• Unity 控制台日志"
|
126
|
+
puts "• GoodUnityBuild 的 AndroidBuildConfiguration.cs 配置"
|
127
|
+
puts "• 项目路径和权限设置"
|
128
|
+
raise "Unity 构建输出目录不存在,构建可能失败"
|
129
|
+
end
|
130
|
+
|
111
131
|
puts "开始构建Unity项目..."
|
112
132
|
|
113
133
|
unity_helper.build_project(
|
@@ -5,6 +5,7 @@ require 'fileutils'
|
|
5
5
|
require 'pindo/base/executable'
|
6
6
|
require 'pindo/module/build/unityhelper'
|
7
7
|
require 'pindo/module/build/buildhelper'
|
8
|
+
require 'pindo/base/pindocontext'
|
8
9
|
|
9
10
|
module Pindo
|
10
11
|
class Command
|
@@ -12,11 +13,18 @@ module Pindo
|
|
12
13
|
class Ipa < Unity
|
13
14
|
|
14
15
|
include Appselect
|
15
|
-
|
16
|
+
|
16
17
|
include Pindo::Githelper
|
17
18
|
# Unity IPA包编译和上传命令
|
18
19
|
self.summary = '编译Unity工程生成iOS IPA并支持上传到测试平台'
|
19
20
|
|
21
|
+
# 启用缓存机制
|
22
|
+
def self.use_cache?
|
23
|
+
true # 此命令启用缓存
|
24
|
+
end
|
25
|
+
# 缓存组已在 PindoContext#get_command_group 中定义
|
26
|
+
# unity:ipa 与 ios:autobuild 共享 'ios:autobuild' 缓存组
|
27
|
+
|
20
28
|
# 详细说明
|
21
29
|
self.description = <<-DESC
|
22
30
|
编译Unity工程生成iOS IPA并支持上传到测试平台。
|
@@ -129,6 +137,21 @@ module Pindo
|
|
129
137
|
if isLibrary
|
130
138
|
pindo_ios_project_dir = ios_export_lib_dir
|
131
139
|
end
|
140
|
+
|
141
|
+
# 验证构建输出目录是否存在
|
142
|
+
unless File.directory?(pindo_ios_project_dir)
|
143
|
+
puts "\e[31m错误: Unity 构建输出目录不存在: #{pindo_ios_project_dir}\e[0m"
|
144
|
+
puts "\e[33m可能的原因:\e[0m"
|
145
|
+
puts "• Unity 构建失败但未正确报告错误"
|
146
|
+
puts "• GoodUnityBuild 配置的输出路径不正确"
|
147
|
+
puts "• Unity 项目配置问题"
|
148
|
+
puts "\e[33m建议检查:\e[0m"
|
149
|
+
puts "• Unity 控制台日志"
|
150
|
+
puts "• GoodUnityBuild 的 IOSBuildConfiguration.cs 配置"
|
151
|
+
puts "• 项目路径和权限设置"
|
152
|
+
raise "Unity 构建输出目录不存在,构建可能失败"
|
153
|
+
end
|
154
|
+
|
132
155
|
unity_helper.build_project(unity_exe_full_path:unity_exe_path, project_path:pindo_project_dir, platform:'iOS', isLibrary:isLibrary)
|
133
156
|
|
134
157
|
|
@@ -142,9 +165,10 @@ module Pindo
|
|
142
165
|
if @args_send_flag
|
143
166
|
args_temp << "--send"
|
144
167
|
end
|
145
|
-
|
168
|
+
|
169
|
+
|
146
170
|
Dir.chdir(pindo_ios_project_dir)
|
147
|
-
|
171
|
+
|
148
172
|
Pindo::Command::Ios::Autobuild::run(args_temp)
|
149
173
|
|
150
174
|
|
@@ -14,30 +14,35 @@ module Pindo
|
|
14
14
|
include Appselect
|
15
15
|
|
16
16
|
include Pindo::Githelper
|
17
|
-
# Unity
|
18
|
-
self.summary = '编译Unity工程生成
|
17
|
+
# Unity WebGL包编译和上传命令
|
18
|
+
self.summary = '编译Unity工程生成WebGL并支持上传到测试平台'
|
19
|
+
|
20
|
+
# 启用缓存机制
|
21
|
+
def self.use_cache?
|
22
|
+
true # 此命令启用缓存
|
23
|
+
end
|
19
24
|
|
20
25
|
# 详细说明
|
21
26
|
self.description = <<-DESC
|
22
|
-
编译Unity工程生成
|
27
|
+
编译Unity工程生成WebGL并支持上传到测试平台。
|
23
28
|
|
24
29
|
支持功能:
|
25
30
|
|
26
|
-
* 编译生成
|
31
|
+
* 编译生成WebGL
|
27
32
|
|
28
|
-
* 上传到测试平台
|
33
|
+
* 上传到测试平台
|
29
34
|
|
30
35
|
* 发送测试通知
|
31
36
|
|
32
37
|
使用示例:
|
33
38
|
|
34
|
-
$ pindo unity
|
39
|
+
$ pindo unity web # 编译WebGL
|
35
40
|
|
36
|
-
$ pindo unity
|
41
|
+
$ pindo unity web --upload # 编译并上传
|
37
42
|
|
38
|
-
$ pindo unity
|
43
|
+
$ pindo unity web --send # 编译上传并发送通知
|
39
44
|
|
40
|
-
$ pindo unity
|
45
|
+
$ pindo unity web --proj=myapp # 指定项目名称
|
41
46
|
DESC
|
42
47
|
|
43
48
|
# 命令参数
|
@@ -49,7 +54,7 @@ module Pindo
|
|
49
54
|
def self.options
|
50
55
|
[
|
51
56
|
['--proj', '指定上传到测试平台的项目名称'],
|
52
|
-
['--upload', '上传编译后的
|
57
|
+
['--upload', '上传编译后的WebGL到测试平台'],
|
53
58
|
['--send', '上传成功后发送测试通知'],
|
54
59
|
['--base', 'Unity工程编译lib模式'],
|
55
60
|
['--unity-version', '切换Unity版本']
|
data/lib/pindo/command.rb
CHANGED
@@ -7,6 +7,7 @@ require 'pindo/base/funlog'
|
|
7
7
|
require 'pindo/base/hashhelper'
|
8
8
|
require 'pindo/base/plaininformative'
|
9
9
|
require 'pindo/base/githelper'
|
10
|
+
require 'pindo/base/pindocontext'
|
10
11
|
require 'pindo/client/giteeclient'
|
11
12
|
require 'pindo/config/pindoconfig'
|
12
13
|
|
@@ -59,6 +60,13 @@ module Pindo
|
|
59
60
|
self.version = VERSION
|
60
61
|
self.description = 'pindo, make it easy!'
|
61
62
|
|
63
|
+
# 缓存配置:子类可以重写此方法来启用缓存
|
64
|
+
def self.use_cache?
|
65
|
+
false # 默认不使用缓存
|
66
|
+
end
|
67
|
+
# 注意:缓存组的定义已经移至 PindoContext#get_command_group 中统一管理
|
68
|
+
# 使用命令名作为组名,如 ios:autobuild, and:autobuild, web:autobuild
|
69
|
+
|
62
70
|
|
63
71
|
DEFAULT_ROOT_OPTIONS = [
|
64
72
|
['--version', '查看pindo版本'],
|
@@ -89,13 +97,62 @@ module Pindo
|
|
89
97
|
end
|
90
98
|
|
91
99
|
def self.run(argv)
|
92
|
-
|
100
|
+
# 直接调用父类的 run 方法
|
101
|
+
# PindoContext 的设置在 initialize 中处理
|
93
102
|
super(argv)
|
94
103
|
end
|
95
104
|
|
96
105
|
def initialize(argv)
|
97
106
|
super
|
98
107
|
@args_help_flag = argv.flag?('help', false)
|
108
|
+
|
109
|
+
# 在非抽象命令初始化时设置上下文
|
110
|
+
if !self.class.abstract_command? && !@args_help_flag
|
111
|
+
context = Pindo::PindoContext.instance
|
112
|
+
|
113
|
+
# 只在上下文未设置时才设置(避免重复设置)
|
114
|
+
if context.instance_variable_get(:@current_command).nil?
|
115
|
+
# 获取完整的命令路径
|
116
|
+
command_parts = []
|
117
|
+
current = self.class
|
118
|
+
while current < Pindo::Command
|
119
|
+
if current.command && current.command != 'pindo'
|
120
|
+
command_parts.unshift(current.command)
|
121
|
+
end
|
122
|
+
current = current.superclass
|
123
|
+
end
|
124
|
+
command_name = command_parts.empty? ? self.class.command : command_parts.join(':')
|
125
|
+
|
126
|
+
# 配置选项
|
127
|
+
cache_enabled_value = self.class.respond_to?(:use_cache?) ? self.class.use_cache? : false
|
128
|
+
options = {
|
129
|
+
cache_enabled: cache_enabled_value
|
130
|
+
}
|
131
|
+
|
132
|
+
# 获取项目根目录(Git仓库根目录或当前目录)
|
133
|
+
project_dir = get_project_root_directory(Dir.pwd)
|
134
|
+
|
135
|
+
context.set_context(command_name, project_dir, options)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
|
142
|
+
# 获取项目根目录(优先使用Git仓库根目录)
|
143
|
+
def get_project_root_directory(current_dir)
|
144
|
+
# 尝试获取Git仓库根目录
|
145
|
+
begin
|
146
|
+
git_root = `cd "#{current_dir}" && git rev-parse --show-toplevel 2>/dev/null`.strip
|
147
|
+
if $?.success? && !git_root.empty?
|
148
|
+
return git_root
|
149
|
+
end
|
150
|
+
rescue
|
151
|
+
# git命令失败,继续使用当前目录
|
152
|
+
end
|
153
|
+
|
154
|
+
# 如果不是Git仓库,返回当前目录
|
155
|
+
current_dir
|
99
156
|
end
|
100
157
|
|
101
158
|
end
|
@@ -0,0 +1,427 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require_relative '../../base/funlog'
|
3
|
+
require_relative 'base_helper'
|
4
|
+
|
5
|
+
module Pindo
|
6
|
+
|
7
|
+
class AndroidBuildConfigHelper
|
8
|
+
include BaseAndroidHelper
|
9
|
+
|
10
|
+
# 更新Android工程版本号
|
11
|
+
# @param project_dir [String] Android项目目录路径
|
12
|
+
# @param version_name [String] 版本名
|
13
|
+
# @param version_code [Integer] 版本号
|
14
|
+
# @return [Boolean] 是否成功更新
|
15
|
+
def self.update_android_project_version(project_dir: nil, version_name: nil, version_code: nil)
|
16
|
+
raise ArgumentError, "项目目录不能为空" if project_dir.nil?
|
17
|
+
raise ArgumentError, "版本名不能为空" if version_name.nil?
|
18
|
+
raise ArgumentError, "版本号不能为空" if version_code.nil?
|
19
|
+
|
20
|
+
# 验证version_code的有效性
|
21
|
+
unless valid_build_number?(version_code)
|
22
|
+
Funlog.instance.fancyinfo_error("Android versionCode必须在1到#{2**31-1}之间,当前值:#{version_code}")
|
23
|
+
return false
|
24
|
+
end
|
25
|
+
|
26
|
+
Funlog.instance.fancyinfo_start("正在更新Android工程版本信息...")
|
27
|
+
|
28
|
+
begin
|
29
|
+
# 创建helper实例以使用基类方法
|
30
|
+
helper = self.new
|
31
|
+
|
32
|
+
# 获取主模块路径
|
33
|
+
main_module = helper.get_main_module(project_dir)
|
34
|
+
|
35
|
+
if main_module.nil?
|
36
|
+
# 如果无法获取主模块,尝试常见的模块名
|
37
|
+
["app", "application", "main"].each do |module_name|
|
38
|
+
module_path = File.join(project_dir, module_name)
|
39
|
+
if File.exist?(File.join(module_path, "build.gradle")) ||
|
40
|
+
File.exist?(File.join(module_path, "build.gradle.kts"))
|
41
|
+
main_module = module_path
|
42
|
+
break
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
if main_module.nil?
|
48
|
+
Funlog.instance.fancyinfo_error("未找到Android主模块")
|
49
|
+
return false
|
50
|
+
end
|
51
|
+
|
52
|
+
# 查找build.gradle或build.gradle.kts文件
|
53
|
+
gradle_file = nil
|
54
|
+
if File.exist?(File.join(main_module, "build.gradle"))
|
55
|
+
gradle_file = File.join(main_module, "build.gradle")
|
56
|
+
elsif File.exist?(File.join(main_module, "build.gradle.kts"))
|
57
|
+
gradle_file = File.join(main_module, "build.gradle.kts")
|
58
|
+
end
|
59
|
+
|
60
|
+
if gradle_file.nil?
|
61
|
+
Funlog.instance.fancyinfo_error("未找到build.gradle文件")
|
62
|
+
return false
|
63
|
+
end
|
64
|
+
|
65
|
+
# 读取并更新gradle文件
|
66
|
+
content = File.read(gradle_file)
|
67
|
+
original_content = content.dup
|
68
|
+
|
69
|
+
# 更新versionCode
|
70
|
+
if content =~ /versionCode\s+\d+/
|
71
|
+
content.gsub!(/versionCode\s+\d+/, "versionCode #{version_code}")
|
72
|
+
elsif content =~ /versionCode\s*=\s*\d+/
|
73
|
+
content.gsub!(/versionCode\s*=\s*\d+/, "versionCode = #{version_code}")
|
74
|
+
end
|
75
|
+
|
76
|
+
# 更新versionName
|
77
|
+
if content =~ /versionName\s+["'][^"']*["']/
|
78
|
+
content.gsub!(/versionName\s+["'][^"']*["']/, "versionName \"#{version_name}\"")
|
79
|
+
elsif content =~ /versionName\s*=\s*["'][^"']*["']/
|
80
|
+
content.gsub!(/versionName\s*=\s*["'][^"']*["']/, "versionName = \"#{version_name}\"")
|
81
|
+
end
|
82
|
+
|
83
|
+
# 如果内容有变化,写回文件
|
84
|
+
if content != original_content
|
85
|
+
File.write(gradle_file, content)
|
86
|
+
Funlog.instance.fancyinfo_success("Android版本更新完成!")
|
87
|
+
puts " ✓ versionName已更新: #{version_name}"
|
88
|
+
puts " ✓ versionCode已更新: #{version_code}"
|
89
|
+
puts " ✓ 更新的文件: #{gradle_file}"
|
90
|
+
return true
|
91
|
+
else
|
92
|
+
# 如果没有找到版本字段,尝试在defaultConfig块中添加
|
93
|
+
if content =~ /defaultConfig\s*\{([^}]*)\}/m
|
94
|
+
default_config = $1
|
95
|
+
if !default_config.include?("versionCode") || !default_config.include?("versionName")
|
96
|
+
Funlog.instance.fancyinfo_update("gradle文件中未找到版本字段,尝试添加...")
|
97
|
+
|
98
|
+
# 在defaultConfig块中添加版本信息
|
99
|
+
new_default_config = default_config
|
100
|
+
unless default_config.include?("versionCode")
|
101
|
+
new_default_config += "\n versionCode #{version_code}"
|
102
|
+
end
|
103
|
+
unless default_config.include?("versionName")
|
104
|
+
new_default_config += "\n versionName \"#{version_name}\""
|
105
|
+
end
|
106
|
+
|
107
|
+
content.gsub!(/defaultConfig\s*\{[^}]*\}/m, "defaultConfig {#{new_default_config}\n }")
|
108
|
+
File.write(gradle_file, content)
|
109
|
+
|
110
|
+
Funlog.instance.fancyinfo_success("Android版本更新完成!")
|
111
|
+
puts " ✓ versionName已添加: #{version_name}"
|
112
|
+
puts " ✓ versionCode已添加: #{version_code}"
|
113
|
+
return true
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
Funlog.instance.fancyinfo_error("无法在gradle文件中找到或添加版本信息")
|
118
|
+
return false
|
119
|
+
end
|
120
|
+
|
121
|
+
rescue StandardError => e
|
122
|
+
Funlog.instance.fancyinfo_error("更新Android版本失败: #{e.message}")
|
123
|
+
return false
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# 验证Build号是否在有效范围内
|
128
|
+
# @param build_number [Integer] Build号
|
129
|
+
# @return [Boolean] 是否有效
|
130
|
+
def self.valid_build_number?(build_number)
|
131
|
+
return false if build_number.nil?
|
132
|
+
|
133
|
+
# Android versionCode的有效范围是1到2^31-1
|
134
|
+
build_number >= 1 && build_number <= 2**31 - 1
|
135
|
+
end
|
136
|
+
|
137
|
+
# 添加测试scheme到Android工程
|
138
|
+
# @param project_dir [String] Android项目目录路径
|
139
|
+
# @param scheme_name [String] 要添加的scheme名称
|
140
|
+
# @return [Boolean] 是否成功添加
|
141
|
+
def self.add_test_scheme(project_dir: nil, scheme_name: nil)
|
142
|
+
# 参数验证
|
143
|
+
if scheme_name.nil?
|
144
|
+
Funlog.instance.fancyinfo_error("需要提供scheme名称")
|
145
|
+
return false
|
146
|
+
end
|
147
|
+
|
148
|
+
if project_dir.nil? || !File.directory?(project_dir)
|
149
|
+
Funlog.instance.fancyinfo_error("项目路径无效: #{project_dir}")
|
150
|
+
return false
|
151
|
+
end
|
152
|
+
|
153
|
+
scheme_name = scheme_name.to_s.gsub(/[^a-zA-Z0-9]/, '').downcase
|
154
|
+
puts "正在为Android项目添加自定义scheme: #{scheme_name}"
|
155
|
+
|
156
|
+
# 创建helper实例以使用基类方法
|
157
|
+
helper = self.new
|
158
|
+
|
159
|
+
# 查找所有可能的模块
|
160
|
+
main_module = helper.get_main_module(project_dir)
|
161
|
+
search_modules = ["app", "unityLibrary"]
|
162
|
+
search_modules << main_module if main_module
|
163
|
+
search_modules = search_modules.compact.uniq
|
164
|
+
|
165
|
+
puts "搜索的模块: #{search_modules.join(', ')}"
|
166
|
+
|
167
|
+
# 查找所有可能的AndroidManifest.xml文件
|
168
|
+
manifest_candidates = find_manifests(project_dir, search_modules)
|
169
|
+
|
170
|
+
if manifest_candidates.empty?
|
171
|
+
Funlog.instance.fancyinfo_error("未找到任何AndroidManifest.xml文件")
|
172
|
+
return false
|
173
|
+
end
|
174
|
+
|
175
|
+
puts "找到#{manifest_candidates.size}个AndroidManifest.xml文件"
|
176
|
+
|
177
|
+
# 寻找最佳的AndroidManifest.xml和Activity
|
178
|
+
manifest_path, main_activity, android_prefix = find_best_activity(manifest_candidates)
|
179
|
+
|
180
|
+
if manifest_path.nil? || main_activity.nil?
|
181
|
+
Funlog.instance.fancyinfo_error("无法找到合适的Activity")
|
182
|
+
return false
|
183
|
+
end
|
184
|
+
|
185
|
+
puts "已选择 #{manifest_path} 文件中的 #{main_activity['android:name'] || '主Activity'}"
|
186
|
+
|
187
|
+
# 检查scheme是否已存在
|
188
|
+
scheme_exists, existing_scheme = check_scheme_exists(main_activity, android_prefix, scheme_name)
|
189
|
+
|
190
|
+
# 如果scheme已存在,检查是否需要更新格式
|
191
|
+
if scheme_exists
|
192
|
+
activity_name = main_activity["#{android_prefix}:name"] || "主Activity"
|
193
|
+
if existing_scheme != scheme_name
|
194
|
+
# 格式不同,需要更新
|
195
|
+
if update_existing_scheme(manifest_path, main_activity, android_prefix, existing_scheme, scheme_name)
|
196
|
+
Funlog.instance.fancyinfo_update("已将scheme从'#{existing_scheme}'更新为'#{scheme_name}'")
|
197
|
+
else
|
198
|
+
Funlog.instance.fancyinfo_update("scheme已存在: #{existing_scheme}")
|
199
|
+
end
|
200
|
+
else
|
201
|
+
puts " ✓ scheme已存在: #{scheme_name}"
|
202
|
+
end
|
203
|
+
return true
|
204
|
+
end
|
205
|
+
|
206
|
+
# 尝试使用DOM添加
|
207
|
+
success = add_scheme_with_dom(manifest_path, main_activity, android_prefix, scheme_name)
|
208
|
+
|
209
|
+
unless success
|
210
|
+
# DOM方法失败,尝试文本替换
|
211
|
+
success = add_scheme_with_text_replace(manifest_path, scheme_name)
|
212
|
+
end
|
213
|
+
|
214
|
+
if success
|
215
|
+
puts " ✓ 成功添加scheme: #{scheme_name}"
|
216
|
+
else
|
217
|
+
Funlog.instance.fancyinfo_error("无法添加scheme: #{scheme_name}")
|
218
|
+
end
|
219
|
+
|
220
|
+
return success
|
221
|
+
end
|
222
|
+
|
223
|
+
private
|
224
|
+
|
225
|
+
# 查找所有可能的AndroidManifest.xml文件
|
226
|
+
def self.find_manifests(project_dir, modules)
|
227
|
+
manifest_candidates = []
|
228
|
+
|
229
|
+
modules.each do |mod|
|
230
|
+
mod_path = mod.is_a?(String) && !mod.start_with?(project_dir) ? File.join(project_dir, mod) : mod
|
231
|
+
next unless File.directory?(mod_path)
|
232
|
+
|
233
|
+
manifest_paths = [
|
234
|
+
File.join(mod_path, "src", "main", "AndroidManifest.xml"),
|
235
|
+
File.join(mod_path, "AndroidManifest.xml")
|
236
|
+
]
|
237
|
+
|
238
|
+
manifest_paths.each do |path|
|
239
|
+
if File.exist?(path)
|
240
|
+
manifest_candidates << path
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
manifest_candidates
|
246
|
+
end
|
247
|
+
|
248
|
+
# 查找最佳的Activity
|
249
|
+
def self.find_best_activity(manifest_candidates)
|
250
|
+
require 'nokogiri'
|
251
|
+
|
252
|
+
best_manifest = nil
|
253
|
+
best_activity = nil
|
254
|
+
android_prefix = nil
|
255
|
+
|
256
|
+
manifest_candidates.each do |manifest_path|
|
257
|
+
begin
|
258
|
+
doc = Nokogiri::XML(File.read(manifest_path))
|
259
|
+
doc.remove_namespaces!
|
260
|
+
|
261
|
+
# 查找包含MAIN和LAUNCHER的Activity
|
262
|
+
activities = doc.xpath("//activity")
|
263
|
+
activities.each do |activity|
|
264
|
+
intent_filters = activity.xpath("intent-filter")
|
265
|
+
intent_filters.each do |intent_filter|
|
266
|
+
# 修复:移除命名空间后,应该使用 @name 而不是 @android:name
|
267
|
+
has_main = intent_filter.xpath("action[@name='android.intent.action.MAIN']").any?
|
268
|
+
has_launcher = intent_filter.xpath("category[@name='android.intent.category.LAUNCHER']").any?
|
269
|
+
|
270
|
+
if has_main && has_launcher
|
271
|
+
best_manifest = manifest_path
|
272
|
+
best_activity = activity
|
273
|
+
android_prefix = "android"
|
274
|
+
break
|
275
|
+
end
|
276
|
+
end
|
277
|
+
break if best_activity
|
278
|
+
end
|
279
|
+
rescue => e
|
280
|
+
# 静默处理解析错误,继续尝试下一个文件
|
281
|
+
puts "解析 #{manifest_path} 时出错: #{e.message}" if ENV['PINDO_DEBUG']
|
282
|
+
end
|
283
|
+
|
284
|
+
break if best_activity
|
285
|
+
end
|
286
|
+
|
287
|
+
[best_manifest, best_activity, android_prefix]
|
288
|
+
end
|
289
|
+
|
290
|
+
# 检查scheme是否已存在
|
291
|
+
def self.check_scheme_exists(activity, android_prefix, scheme_name)
|
292
|
+
scheme_exists = false
|
293
|
+
existing_scheme = nil
|
294
|
+
|
295
|
+
# 首先检查完全一致的scheme(修复:移除命名空间后使用 @scheme)
|
296
|
+
activity.xpath("intent-filter/data[@scheme='#{scheme_name}']").each do |node|
|
297
|
+
scheme_exists = true
|
298
|
+
existing_scheme = scheme_name
|
299
|
+
break
|
300
|
+
end
|
301
|
+
|
302
|
+
# 如果没有找到完全一致的,检查可能格式不同的scheme
|
303
|
+
if !scheme_exists
|
304
|
+
activity.xpath("intent-filter/data[@scheme]").each do |node|
|
305
|
+
current_scheme = node["scheme"]
|
306
|
+
normalized_current = current_scheme.to_s.downcase.strip.gsub(/[\s\-_]/, '')
|
307
|
+
|
308
|
+
if normalized_current == scheme_name
|
309
|
+
scheme_exists = true
|
310
|
+
existing_scheme = current_scheme
|
311
|
+
break
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
[scheme_exists, existing_scheme]
|
317
|
+
end
|
318
|
+
|
319
|
+
# 使用DOM操作添加scheme
|
320
|
+
def self.add_scheme_with_dom(manifest_path, activity, android_prefix, scheme_name)
|
321
|
+
begin
|
322
|
+
doc = Nokogiri::XML(File.read(manifest_path))
|
323
|
+
|
324
|
+
# 创建intent-filter
|
325
|
+
intent_filter = doc.create_element('intent-filter')
|
326
|
+
|
327
|
+
# 添加子元素
|
328
|
+
intent_filter.add_child(create_element(doc, 'action', "#{android_prefix}:name", 'android.intent.action.VIEW'))
|
329
|
+
intent_filter.add_child(create_element(doc, 'category', "#{android_prefix}:name", 'android.intent.category.DEFAULT'))
|
330
|
+
intent_filter.add_child(create_element(doc, 'category', "#{android_prefix}:name", 'android.intent.category.BROWSABLE'))
|
331
|
+
intent_filter.add_child(create_element(doc, 'data', "#{android_prefix}:scheme", scheme_name))
|
332
|
+
|
333
|
+
# 添加空白和缩进
|
334
|
+
activity.add_child(doc.create_text_node("\n "))
|
335
|
+
activity.add_child(intent_filter)
|
336
|
+
activity.add_child(doc.create_text_node("\n "))
|
337
|
+
|
338
|
+
# 保存修改
|
339
|
+
xml_content = doc.to_xml(indent: 2, encoding: 'UTF-8')
|
340
|
+
|
341
|
+
# 验证修改是否成功
|
342
|
+
if xml_content.include?("android:scheme=\"#{scheme_name}\"")
|
343
|
+
File.write(manifest_path, xml_content)
|
344
|
+
return true
|
345
|
+
end
|
346
|
+
|
347
|
+
return false
|
348
|
+
rescue => e
|
349
|
+
return false
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
# 创建XML元素并设置属性
|
354
|
+
def self.create_element(doc, name, attr_name, attr_value)
|
355
|
+
element = doc.create_element(name)
|
356
|
+
element[attr_name] = attr_value
|
357
|
+
element
|
358
|
+
end
|
359
|
+
|
360
|
+
# 使用文本替换添加scheme
|
361
|
+
def self.add_scheme_with_text_replace(manifest_path, scheme_name)
|
362
|
+
begin
|
363
|
+
# 读取原始内容
|
364
|
+
xml_content = File.read(manifest_path)
|
365
|
+
|
366
|
+
# 定义要添加的intent-filter
|
367
|
+
scheme_intent_filter = %Q{
|
368
|
+
<intent-filter>
|
369
|
+
<action android:name="android.intent.action.VIEW"/>
|
370
|
+
<category android:name="android.intent.category.DEFAULT"/>
|
371
|
+
<category android:name="android.intent.category.BROWSABLE"/>
|
372
|
+
<data android:scheme="#{scheme_name}"/>
|
373
|
+
</intent-filter>}
|
374
|
+
|
375
|
+
# 在</activity>前添加intent-filter
|
376
|
+
if xml_content.match(/<\/activity>/)
|
377
|
+
modified_xml = xml_content.gsub(/<\/activity>/) do |match|
|
378
|
+
"#{scheme_intent_filter}\n #{match}"
|
379
|
+
end
|
380
|
+
|
381
|
+
# 保存修改
|
382
|
+
File.write(manifest_path, modified_xml)
|
383
|
+
return true
|
384
|
+
end
|
385
|
+
|
386
|
+
return false
|
387
|
+
rescue => e
|
388
|
+
return false
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
def self.update_existing_scheme(manifest_path, activity, android_prefix, existing_scheme, scheme_name)
|
393
|
+
begin
|
394
|
+
doc = Nokogiri::XML(File.read(manifest_path))
|
395
|
+
|
396
|
+
# 查找所有intent-filter
|
397
|
+
intent_filters = doc.xpath("//intent-filter")
|
398
|
+
|
399
|
+
intent_filters.each do |intent_filter|
|
400
|
+
# 检查intent-filter中的scheme
|
401
|
+
intent_filter.xpath("data[@#{android_prefix}:scheme]").each do |data|
|
402
|
+
current_scheme = data["#{android_prefix}:scheme"]
|
403
|
+
if current_scheme == existing_scheme
|
404
|
+
# 找到intent-filter,更新scheme
|
405
|
+
data["#{android_prefix}:scheme"] = scheme_name
|
406
|
+
end
|
407
|
+
end
|
408
|
+
end
|
409
|
+
|
410
|
+
# 保存修改
|
411
|
+
xml_content = doc.to_xml(indent: 2, encoding: 'UTF-8')
|
412
|
+
|
413
|
+
# 验证修改是否成功
|
414
|
+
if xml_content.include?("android:scheme=\"#{scheme_name}\"")
|
415
|
+
File.write(manifest_path, xml_content)
|
416
|
+
return true
|
417
|
+
end
|
418
|
+
|
419
|
+
return false
|
420
|
+
rescue => e
|
421
|
+
return false
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
end
|
426
|
+
|
427
|
+
end
|