pindo 5.1.1 → 5.1.3

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.
@@ -3,6 +3,7 @@ require_relative 'base_helper'
3
3
  require_relative 'gradle_helper'
4
4
  require_relative 'so_helper'
5
5
  require_relative 'apk_helper'
6
+ require 'fileutils'
6
7
 
7
8
  module Pindo
8
9
  class AndroidBuildHelper
@@ -12,12 +13,81 @@ module Pindo
12
13
  include ApkHelper
13
14
  include Singleton
14
15
 
16
+
17
+
15
18
  class << self
16
19
  def share_instance
17
20
  instance
18
21
  end
19
22
  end
20
23
 
24
+ def add_test_scheme(project_dir:nil, scheme_name:nil)
25
+ puts "正在为Android项目添加自定义scheme: #{scheme_name}"
26
+ puts "项目路径: #{project_dir}, 当前工作目录: #{Dir.pwd}"
27
+
28
+ # 参数验证
29
+ return puts "错误: 需要提供scheme名称" if scheme_name.nil?
30
+ return puts "错误: 需要提供项目路径" if project_dir.nil?
31
+ return puts "错误: 项目路径不存在: #{project_dir}" unless File.directory?(project_dir)
32
+
33
+ scheme_name = scheme_name.to_s.downcase.strip.gsub(/[\s\-_]/, '')
34
+ # 查找所有可能的模块
35
+ main_module = get_main_module(project_dir)
36
+ search_modules = ["app", "unityLibrary"]
37
+ search_modules << main_module if main_module
38
+ search_modules = search_modules.compact.uniq
39
+
40
+ puts "搜索的模块: #{search_modules.join(', ')}"
41
+
42
+ # 查找所有可能的AndroidManifest.xml文件
43
+ manifest_candidates = find_manifests(project_dir, search_modules)
44
+
45
+ if manifest_candidates.empty?
46
+ return puts "错误: 未找到任何AndroidManifest.xml文件"
47
+ end
48
+
49
+ puts "找到#{manifest_candidates.size}个AndroidManifest.xml文件"
50
+
51
+ # 寻找最佳的AndroidManifest.xml和Activity
52
+ manifest_path, main_activity, android_prefix = find_best_activity(manifest_candidates)
53
+
54
+ if manifest_path.nil? || main_activity.nil?
55
+ return puts "错误: 无法找到合适的Activity"
56
+ end
57
+
58
+ puts "已选择 #{manifest_path} 文件中的 #{main_activity['android:name'] || '主Activity'}"
59
+
60
+ # 检查scheme是否已存在
61
+ scheme_exists, existing_scheme = check_scheme_exists(main_activity, android_prefix, scheme_name)
62
+
63
+ # 如果scheme已存在,检查是否需要更新格式
64
+ if scheme_exists
65
+ activity_name = main_activity["#{android_prefix}:name"] || "主Activity"
66
+ if existing_scheme != scheme_name
67
+ # 格式不同,需要更新
68
+ update_scheme = update_existing_scheme(manifest_path, main_activity, android_prefix, existing_scheme, scheme_name)
69
+ if update_scheme
70
+ puts "已将scheme从'#{existing_scheme}'更新为'#{scheme_name}'"
71
+ else
72
+ puts "尝试更新scheme格式失败,但scheme已存在: #{existing_scheme}"
73
+ end
74
+ else
75
+ puts "scheme: #{scheme_name}已存在于#{activity_name}"
76
+ end
77
+ return
78
+ end
79
+
80
+ # 尝试添加scheme
81
+ success = add_scheme_with_dom(manifest_path, main_activity, android_prefix, scheme_name)
82
+
83
+ # 如果DOM操作失败,使用文本替换
84
+ unless success
85
+ add_scheme_with_text_replace(manifest_path, scheme_name)
86
+ end
87
+
88
+ puts "添加scheme操作完成,项目路径: #{project_dir}"
89
+ end
90
+
21
91
  def auto_build_apk(project_dir, debug = false, ignore_sub = false)
22
92
  # 检查 gradle.properties 中是否设置了 Java Home
23
93
  gradle_properties_path = File.join(project_dir, "gradle.properties")
@@ -108,5 +178,260 @@ module Pindo
108
178
  rescue StandardError => e
109
179
  raise Informative, "准备项目失败: #{e.message}"
110
180
  end
181
+
182
+ # 查找所有可能的AndroidManifest.xml文件
183
+ def find_manifests(project_dir, modules)
184
+ manifest_candidates = []
185
+
186
+ # 搜索每个模块中的AndroidManifest.xml
187
+ modules.each do |mod|
188
+ mod_path = mod.is_a?(String) && !mod.start_with?(project_dir) ? File.join(project_dir, mod) : mod
189
+
190
+ # 标准位置
191
+ standard_manifest = File.join(mod_path, "src", "main", "AndroidManifest.xml")
192
+ manifest_candidates << standard_manifest if File.exist?(standard_manifest)
193
+
194
+ # 根目录
195
+ root_manifest = File.join(mod_path, "AndroidManifest.xml")
196
+ manifest_candidates << root_manifest if File.exist?(root_manifest)
197
+ end
198
+
199
+ # 项目根目录
200
+ manifest_candidates << File.join(project_dir, "AndroidManifest.xml") if File.exist?(File.join(project_dir, "AndroidManifest.xml"))
201
+ manifest_candidates << File.join(project_dir, "src/main/AndroidManifest.xml") if File.exist?(File.join(project_dir, "src/main/AndroidManifest.xml"))
202
+
203
+ manifest_candidates.uniq
204
+ end
205
+
206
+ # 查找最佳的Activity
207
+ def find_best_activity(manifest_candidates)
208
+ require 'nokogiri'
209
+
210
+ best_manifest = nil
211
+ best_activity = nil
212
+ android_prefix = nil
213
+
214
+ manifest_candidates.each do |manifest_path|
215
+ begin
216
+ puts "检查: #{manifest_path}"
217
+ doc = Nokogiri::XML(File.read(manifest_path))
218
+
219
+ # 处理命名空间
220
+ android_ns = 'http://schemas.android.com/apk/res/android'
221
+ prefix = nil
222
+
223
+ doc.root.namespace_definitions.each do |ns|
224
+ if ns.href == android_ns
225
+ prefix = ns.prefix
226
+ break
227
+ end
228
+ end
229
+
230
+ prefix ||= 'android'
231
+
232
+ # 构建XPath查询
233
+ ns_xpath = lambda do |xpath|
234
+ xpath.gsub(/@android:/, "@#{prefix}:")
235
+ .gsub(/\[@android:/, "[@#{prefix}:")
236
+ end
237
+
238
+ # 查找LAUNCHER Activity
239
+ launcher_activities = doc.xpath(ns_xpath.call('//activity[intent-filter/category[@android:name="android.intent.category.LAUNCHER"]]'))
240
+
241
+ if !launcher_activities.empty?
242
+ best_manifest = manifest_path
243
+ best_activity = launcher_activities.first
244
+ android_prefix = prefix
245
+ puts "找到带有LAUNCHER的Activity: #{best_activity['android:name']}"
246
+ break
247
+ elsif best_activity.nil?
248
+ activities = doc.xpath('//activity')
249
+ if !activities.empty?
250
+ best_manifest = manifest_path
251
+ best_activity = activities.first
252
+ android_prefix = prefix
253
+ puts "找到Activity(无LAUNCHER): #{best_activity['android:name']}"
254
+ end
255
+ end
256
+ rescue => e
257
+ puts "解析失败: #{e.message}"
258
+ end
259
+ end
260
+
261
+ [best_manifest, best_activity, android_prefix]
262
+ end
263
+
264
+ # 检查scheme是否已存在
265
+ def check_scheme_exists(activity, android_prefix, scheme_name)
266
+ scheme_exists = false
267
+ existing_scheme = nil
268
+
269
+ # 首先检查完全一致的scheme
270
+ activity.xpath("intent-filter/data[@#{android_prefix}:scheme='#{scheme_name}']").each do |node|
271
+ scheme_exists = true
272
+ existing_scheme = scheme_name
273
+ break
274
+ end
275
+
276
+ # 如果没有找到完全一致的,检查可能格式不同的scheme
277
+ if !scheme_exists
278
+ # 查找所有scheme属性
279
+ activity.xpath("intent-filter/data[@#{android_prefix}:scheme]").each do |node|
280
+ current_scheme = node["#{android_prefix}:scheme"]
281
+ normalized_current = current_scheme.to_s.downcase.strip.gsub(/[\s\-_]/, '')
282
+
283
+ if normalized_current == scheme_name
284
+ scheme_exists = true
285
+ existing_scheme = current_scheme
286
+ puts "发现格式不同但实质相同的scheme: '#{current_scheme}'"
287
+ break
288
+ end
289
+ end
290
+ end
291
+
292
+ [scheme_exists, existing_scheme]
293
+ end
294
+
295
+ # 使用DOM操作添加scheme
296
+ def add_scheme_with_dom(manifest_path, activity, android_prefix, scheme_name)
297
+ begin
298
+ doc = Nokogiri::XML(File.read(manifest_path))
299
+
300
+ # 创建intent-filter
301
+ intent_filter = doc.create_element('intent-filter')
302
+
303
+ # 添加子元素
304
+ intent_filter.add_child(create_element(doc, 'action', "#{android_prefix}:name", 'android.intent.action.VIEW'))
305
+ intent_filter.add_child(create_element(doc, 'category', "#{android_prefix}:name", 'android.intent.category.DEFAULT'))
306
+ intent_filter.add_child(create_element(doc, 'category', "#{android_prefix}:name", 'android.intent.category.BROWSABLE'))
307
+ intent_filter.add_child(create_element(doc, 'data', "#{android_prefix}:scheme", scheme_name))
308
+
309
+ # 添加空白和缩进
310
+ activity.add_child(doc.create_text_node("\n "))
311
+ activity.add_child(intent_filter)
312
+ activity.add_child(doc.create_text_node("\n "))
313
+
314
+ # 保存修改
315
+ xml_content = doc.to_xml(indent: 2, encoding: 'UTF-8')
316
+
317
+ # 验证修改是否成功
318
+ if xml_content.include?("android:scheme=\"#{scheme_name}\"")
319
+ File.write(manifest_path, xml_content)
320
+ puts "DOM操作成功添加了scheme: #{scheme_name}"
321
+ return true
322
+ end
323
+
324
+ puts "DOM操作未能添加scheme"
325
+ return false
326
+ rescue => e
327
+ puts "DOM操作失败: #{e.message}"
328
+ return false
329
+ end
330
+ end
331
+
332
+ # 创建XML元素并设置属性
333
+ def create_element(doc, name, attr_name, attr_value)
334
+ element = doc.create_element(name)
335
+ element[attr_name] = attr_value
336
+ element
337
+ end
338
+
339
+ # 使用文本替换添加scheme
340
+ def add_scheme_with_text_replace(manifest_path, scheme_name)
341
+ begin
342
+ # 读取原始内容
343
+ xml_content = File.read(manifest_path)
344
+
345
+ # 定义要添加的intent-filter
346
+ scheme_intent_filter = %Q{
347
+ <intent-filter>
348
+ <action android:name="android.intent.action.VIEW"/>
349
+ <category android:name="android.intent.category.DEFAULT"/>
350
+ <category android:name="android.intent.category.BROWSABLE"/>
351
+ <data android:scheme="#{scheme_name}"/>
352
+ </intent-filter>}
353
+
354
+ # 在</activity>前添加intent-filter
355
+ if xml_content.match(/<\/activity>/)
356
+ modified_xml = xml_content.gsub(/<\/activity>/) do |match|
357
+ "#{scheme_intent_filter}\n #{match}"
358
+ end
359
+
360
+ # 保存修改
361
+ File.write(manifest_path, modified_xml)
362
+ puts "文本替换成功添加了scheme: #{scheme_name}"
363
+ return true
364
+ else
365
+ puts "在XML中找不到</activity>标签"
366
+ return false
367
+ end
368
+ rescue => e
369
+ puts "文本替换失败: #{e.message}"
370
+ return false
371
+ end
372
+ end
373
+
374
+ def update_existing_scheme(manifest_path, activity, android_prefix, existing_scheme, scheme_name)
375
+ begin
376
+ doc = Nokogiri::XML(File.read(manifest_path))
377
+
378
+ # 查找所有intent-filter
379
+ intent_filters = doc.xpath("//intent-filter")
380
+
381
+ intent_filters.each do |intent_filter|
382
+ # 检查intent-filter中的scheme
383
+ intent_filter.xpath("data[@#{android_prefix}:scheme]").each do |data|
384
+ current_scheme = data["#{android_prefix}:scheme"]
385
+ if current_scheme == existing_scheme
386
+ # 找到intent-filter,更新scheme
387
+ data["#{android_prefix}:scheme"] = scheme_name
388
+ end
389
+ end
390
+ end
391
+
392
+ # 保存修改
393
+ xml_content = doc.to_xml(indent: 2, encoding: 'UTF-8')
394
+
395
+ # 验证修改是否成功
396
+ if xml_content.include?("android:scheme=\"#{scheme_name}\"")
397
+ File.write(manifest_path, xml_content)
398
+ puts "DOM操作成功更新了scheme: #{scheme_name}"
399
+ return true
400
+ end
401
+
402
+ puts "DOM操作未能更新scheme,尝试文本替换方法"
403
+ return update_scheme_with_text_replace(manifest_path, existing_scheme, scheme_name)
404
+ rescue => e
405
+ puts "DOM操作失败: #{e.message},尝试文本替换方法"
406
+ return update_scheme_with_text_replace(manifest_path, existing_scheme, scheme_name)
407
+ end
408
+ end
409
+
410
+ # 使用文本替换方法更新scheme
411
+ def update_scheme_with_text_replace(manifest_path, existing_scheme, scheme_name)
412
+ begin
413
+ # 读取原始内容
414
+ xml_content = File.read(manifest_path)
415
+
416
+ # 使用正则表达式替换scheme值
417
+ attribute_pattern = /android:scheme=["']#{Regexp.escape(existing_scheme)}["']/
418
+
419
+ if xml_content.match(attribute_pattern)
420
+ modified_xml = xml_content.gsub(attribute_pattern, "android:scheme=\"#{scheme_name}\"")
421
+
422
+ # 保存修改
423
+ File.write(manifest_path, modified_xml)
424
+ puts "文本替换成功更新scheme: #{scheme_name}"
425
+ return true
426
+ else
427
+ puts "在XML中找不到匹配的scheme属性"
428
+ return false
429
+ end
430
+ rescue => e
431
+ puts "文本替换更新失败: #{e.message}"
432
+ return false
433
+ end
434
+ end
111
435
  end
112
436
  end
437
+
@@ -0,0 +1,57 @@
1
+ require 'highline/import'
2
+ require 'xcodeproj'
3
+ require 'find'
4
+ require 'fileutils'
5
+ require 'pindo/base/executable'
6
+ require 'webrick' # 添加WebRick HTTP服务器
7
+ require 'socket'
8
+
9
+ module Pindo
10
+ module WebServer
11
+
12
+ # 处理.br扩展名请求的servlet
13
+ class BrotliFileHandler < WEBrick::HTTPServlet::AbstractServlet
14
+ def initialize(server, root_dir, debug=false)
15
+ super(server)
16
+ @root_dir = root_dir
17
+ @debug = debug
18
+ end
19
+
20
+ def do_GET(req, res)
21
+ # 只处理.br结尾的文件
22
+ unless req.path.end_with?('.br')
23
+ res.status = 404
24
+ return
25
+ end
26
+
27
+ file_path = File.join(@root_dir, req.path[1..-1])
28
+
29
+ if File.exist?(file_path)
30
+ # 确定原始文件类型
31
+ base_name = File.basename(req.path, ".br")
32
+ ext = File.extname(base_name)
33
+
34
+ content_type = case ext
35
+ when ".js" then "application/javascript"
36
+ when ".wasm" then "application/wasm"
37
+ when ".data" then "application/octet-stream"
38
+ when ".json" then "application/json"
39
+ else "application/octet-stream"
40
+ end
41
+
42
+ # 读取文件
43
+ file_content = File.binread(file_path)
44
+
45
+ # 设置响应
46
+ res.status = 200
47
+ res.header["Content-Encoding"] = "br"
48
+ res.content_type = content_type
49
+ res.body = file_content
50
+ else
51
+ res.status = 404
52
+ end
53
+ end
54
+ end
55
+
56
+ end
57
+ end