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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/lib/pindo/base/aeshelper.rb +23 -2
  3. data/lib/pindo/base/pindocontext.rb +476 -0
  4. data/lib/pindo/client/pgyer_feishu_oauth_cli.rb +343 -80
  5. data/lib/pindo/client/pgyerclient.rb +30 -20
  6. data/lib/pindo/command/android/autobuild.rb +58 -22
  7. data/lib/pindo/command/android/build.rb +27 -16
  8. data/lib/pindo/command/android/debug.rb +25 -15
  9. data/lib/pindo/command/dev/debug.rb +2 -51
  10. data/lib/pindo/command/dev/feishu.rb +19 -2
  11. data/lib/pindo/command/ios/adhoc.rb +2 -1
  12. data/lib/pindo/command/ios/autobuild.rb +39 -9
  13. data/lib/pindo/command/ios/build.rb +7 -0
  14. data/lib/pindo/command/ios/debug.rb +2 -132
  15. data/lib/pindo/command/lib/lint.rb +24 -1
  16. data/lib/pindo/command/setup.rb +24 -4
  17. data/lib/pindo/command/unity/apk.rb +20 -0
  18. data/lib/pindo/command/unity/ipa.rb +27 -3
  19. data/lib/pindo/command/unity/web.rb +15 -10
  20. data/lib/pindo/command/web/autobuild.rb +5 -0
  21. data/lib/pindo/command.rb +58 -1
  22. data/lib/pindo/module/android/android_build_config_helper.rb +427 -0
  23. data/lib/pindo/module/android/apk_helper.rb +23 -25
  24. data/lib/pindo/module/android/base_helper.rb +572 -0
  25. data/lib/pindo/module/android/build_helper.rb +8 -318
  26. data/lib/pindo/module/android/gp_compliance_helper.rb +668 -0
  27. data/lib/pindo/module/android/gradle_helper.rb +746 -3
  28. data/lib/pindo/module/appselect.rb +18 -5
  29. data/lib/pindo/module/build/buildhelper.rb +120 -29
  30. data/lib/pindo/module/build/unityhelper.rb +674 -18
  31. data/lib/pindo/module/build/versionhelper.rb +146 -0
  32. data/lib/pindo/module/cert/certhelper.rb +33 -2
  33. data/lib/pindo/module/cert/xcodecerthelper.rb +3 -1
  34. data/lib/pindo/module/pgyer/pgyerhelper.rb +114 -31
  35. data/lib/pindo/module/xcode/xcodebuildconfig.rb +232 -0
  36. data/lib/pindo/module/xcode/xcodebuildhelper.rb +0 -1
  37. data/lib/pindo/version.rb +356 -86
  38. data/lib/pindo.rb +72 -3
  39. 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 IPA包编译和上传命令
18
- self.summary = '编译Unity工程生成iOS IPA并支持上传到测试平台'
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工程生成iOS IPA并支持上传到测试平台。
27
+ 编译Unity工程生成WebGL并支持上传到测试平台。
23
28
 
24
29
  支持功能:
25
30
 
26
- * 编译生成IPA
31
+ * 编译生成WebGL
27
32
 
28
- * 上传到测试平台
33
+ * 上传到测试平台
29
34
 
30
35
  * 发送测试通知
31
36
 
32
37
  使用示例:
33
38
 
34
- $ pindo unity ipa # 编译IPA
39
+ $ pindo unity web # 编译WebGL
35
40
 
36
- $ pindo unity ipa --upload # 编译并上传
41
+ $ pindo unity web --upload # 编译并上传
37
42
 
38
- $ pindo unity ipa --send # 编译上传并发送通知
43
+ $ pindo unity web --send # 编译上传并发送通知
39
44
 
40
- $ pindo unity ipa --proj=myapp # 指定项目名称
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', '上传编译后的IPA到测试平台'],
57
+ ['--upload', '上传编译后的WebGL到测试平台'],
53
58
  ['--send', '上传成功后发送测试通知'],
54
59
  ['--base', 'Unity工程编译lib模式'],
55
60
  ['--unity-version', '切换Unity版本']
@@ -16,6 +16,11 @@ module Pindo
16
16
 
17
17
  self.summary = '打包Unity WebGL包'
18
18
 
19
+ # 启用缓存机制
20
+ def self.use_cache?
21
+ true # 此命令启用缓存
22
+ end
23
+
19
24
  # 命令的详细说明,包含用法示例
20
25
  self.description = <<-DESC
21
26
  编译WebGL包并支持上传到测试平台。
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