Module: Pindo::AndroidResHelper
- Extended by:
- Executable
- Defined in:
- lib/pindo/module/android/android_res_helper.rb
Constant Summary collapse
- ANDROID_ICON_DENSITIES =
Android icon 各密度尺寸定义
{ 'mdpi' => 48, 'hdpi' => 72, 'xhdpi' => 96, 'xxhdpi' => 144, 'xxxhdpi' => 192 }.freeze
- ADAPTIVE_FOREGROUND_DENSITIES =
Android Adaptive Icon 前景层尺寸(108dp,按密度换算)参考: Android 官方 Adaptive Icon 资源尺寸建议
{ 'mdpi' => 108, 'hdpi' => 162, 'xhdpi' => 216, 'xxhdpi' => 324, 'xxxhdpi' => 432 }.freeze
Class Method Summary collapse
-
.adaptive_icon_xml_resource_dir?(dir_path) ⇒ Boolean
mipmap-anydpi-v* 为 Adaptive Icon 的 layer XML 目录,清理时不可按 basename 删文件,否则会移除 ic_launcher.xml 等.
-
.clean_old_icons(res_dir: nil, old_icon_refs: []) ⇒ Boolean
清理所有旧的 icon 文件和自定义配置.
-
.color_resource_exists?(res_dir:, resource_name:) ⇒ Boolean
检查 colors.xml 中是否声明了指定 color 资源(任意 values 变体目录).
-
.create_icon_for_density(icon_name: nil, new_icon_dir: nil, density: nil, size: nil) ⇒ Hash
创建单个密度的Android icon(同时生成方形和圆形命名).
-
.create_icons(icon_name: nil, new_icon_dir: nil) ⇒ Hash
创建所有密度的Android icons.
-
.drawable_resource_exists?(res_dir:, resource_name:) ⇒ Boolean
检查 drawable 资源是否存在(任意 drawable 变体目录、任意常见后缀).
-
.ensure_standard_adaptive_icon_xml(res_dir: nil) ⇒ Boolean
确保存在标准 adaptive icon XML(Android 8+) 若项目已存在 mipmap-anydpi-v26,则保持原文件不覆盖.
-
.extract_icon_resource_refs_from_manifest(manifest_path: nil) ⇒ Array<Hash>
从 AndroidManifest.xml 中提取 application 的 icon 资源引用.
-
.find_best_icon_source_file(source_dir:, round: false) ⇒ String?
在目录内查找最匹配的 icon 文件.
-
.install_icon(proj_dir: nil, new_icon_dir: nil) ⇒ Boolean
安装Android icon到项目中(完全标准化方案).
-
.mipmap_resource_exists?(res_dir:, resource_name:) ⇒ Boolean
检查 mipmap 资源是否存在(任意 mipmap 变体目录、任意常见后缀).
-
.remove_icon_files_with_same_basename(mipmap_dir:, basename:) ⇒ Integer
删除目录下同名不同后缀图标(例如 ic_launcher.png / ic_launcher.webp).
-
.update_all_xml_icon_references(proj_dir: nil, old_icon_refs: []) ⇒ Boolean
更新所有 XML 文件中的图标引用(从旧名称更新为标准名称).
- .update_manifest_icon_attributes_in_place(xml_content) ⇒ Object
-
.update_manifest_to_standard(manifest_path: nil) ⇒ Boolean
更新 AndroidManifest.xml 为标准配置.
Methods included from Executable
capture_command, executable, execute_command, popen3, reader, which, which!
Class Method Details
.adaptive_icon_xml_resource_dir?(dir_path) ⇒ Boolean
mipmap-anydpi-v* 为 Adaptive Icon 的 layer XML 目录,清理时不可按 basename 删文件,否则会移除 ic_launcher.xml 等
147 148 149 |
# File 'lib/pindo/module/android/android_res_helper.rb', line 147 def self.adaptive_icon_xml_resource_dir?(dir_path) File.basename(dir_path.to_s).match?(/\Amipmap-anydpi-v\d+\z/i) end |
.clean_old_icons(res_dir: nil, old_icon_refs: []) ⇒ Boolean
清理所有旧的 icon 文件和自定义配置
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 |
# File 'lib/pindo/module/android/android_res_helper.rb', line 155 def self.clean_old_icons(res_dir:nil, old_icon_refs: []) return false unless res_dir && File.directory?(res_dir) begin cleaned_items = [] # 1. 删除配置中引用的旧 icon 文件(根据资源类型与名称精确定位) configured_icon_count = 0 old_icon_refs.each do |ref| next unless ref.is_a?(Hash) resource_type = ref[:type].to_s basename = ref[:name].to_s next if resource_type.empty? || basename.empty? candidate_dirs = Dir.glob(File.join(res_dir, "#{resource_type}-*")) candidate_dirs.each do |resource_dir| next unless File.directory?(resource_dir) next if adaptive_icon_xml_resource_dir?(resource_dir) configured_icon_count += remove_icon_files_with_same_basename( mipmap_dir: resource_dir, basename: basename ) end end cleaned_items << "配置icon文件(#{configured_icon_count}个)" if configured_icon_count > 0 # 2. 删除所有 mipmap 目录中的常见旧 icon 文件(兼容历史逻辑) old_png_count = 0 mipmap_dirs = Dir.glob(File.join(res_dir, "mipmap-*")) mipmap_dirs.each do |mipmap_dir| next unless File.directory?(mipmap_dir) next if adaptive_icon_xml_resource_dir?(mipmap_dir) old_icon_basenames = [ 'app_icon', 'app_icon_round', 'ic_launcher', 'ic_launcher_round' ] old_icon_basenames.each do |basename| old_png_count += remove_icon_files_with_same_basename( mipmap_dir: mipmap_dir, basename: basename ) end end cleaned_items << "旧icon文件(#{old_png_count}个)" if old_png_count > 0 if cleaned_items.any? puts " ✓ 已清理: #{cleaned_items.join(', ')}" end return true rescue => e Funlog.instance.("清理旧 icon 失败: #{e.}") return false end end |
.color_resource_exists?(res_dir:, resource_name:) ⇒ Boolean
检查 colors.xml 中是否声明了指定 color 资源(任意 values 变体目录)
291 292 293 294 295 296 297 298 299 300 301 302 303 304 |
# File 'lib/pindo/module/android/android_res_helper.rb', line 291 def self.color_resource_exists?(res_dir:, resource_name:) values_dirs = Dir.glob(File.join(res_dir, "values*")).select { |d| File.directory?(d) } return false if values_dirs.empty? values_dirs.any? do |dir| colors_file = File.join(dir, "colors.xml") next false unless File.file?(colors_file) content = File.read(colors_file) content.match?(/<color\s+name=["']#{Regexp.escape(resource_name)}["']/) end rescue false end |
.create_icon_for_density(icon_name: nil, new_icon_dir: nil, density: nil, size: nil) ⇒ Hash
创建单个密度的Android icon(同时生成方形和圆形命名)
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/pindo/module/android/android_res_helper.rb', line 37 def self.create_icon_for_density(icon_name:nil, new_icon_dir:nil, density:nil, size:nil) unless File.exist?(icon_name) Funlog.instance.("文件不存在: #{icon_name}") return nil end begin density_dir = File.join(new_icon_dir, "mipmap-#{density}") FileUtils.mkdir_p(density_dir) result = {} # 生成两个 legacy launcher 文件: # ic_launcher.png 和 ic_launcher_round.png # 注意:两个都是方形,不切圆角,只是文件名不同 ['ic_launcher.png', 'ic_launcher_round.png'].each do |filename| output_path = File.join(density_dir, filename) command = [ 'sips', # 强制输出 PNG,避免源图为 JPEG/WebP 时仅缩放仍写出 JPEG 导致扩展名与内容不符(AAPT2 报错) '-s', 'format', 'png', '--matchTo', '/System/Library/ColorSync/Profiles/sRGB Profile.icc', '-z', size.to_s, size.to_s, icon_name, '--out', output_path ] Executable.capture_command('sips', command, capture: :out) unless File.exist?(output_path) Funlog.instance.("生成icon失败: #{output_path}") return nil end result[filename] = output_path end # 生成 adaptive icon 前景层(用于 mipmap-anydpi-v26/ic_launcher*.xml 引用) adaptive_size = ADAPTIVE_FOREGROUND_DENSITIES[density] if adaptive_size foreground_filename = 'ic_launcher_foreground.png' foreground_output_path = File.join(density_dir, foreground_filename) foreground_command = [ 'sips', '-s', 'format', 'png', '--matchTo', '/System/Library/ColorSync/Profiles/sRGB Profile.icc', '-z', adaptive_size.to_s, adaptive_size.to_s, icon_name, '--out', foreground_output_path ] Executable.capture_command('sips', foreground_command, capture: :out) unless File.exist?(foreground_output_path) Funlog.instance.("生成icon失败: #{foreground_output_path}") return nil end result[foreground_filename] = foreground_output_path end return result rescue => e Funlog.instance.("生成#{density}密度icon时出错: #{e.}") return nil end end |
.create_icons(icon_name: nil, new_icon_dir: nil) ⇒ Hash
创建所有密度的Android icons
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/pindo/module/android/android_res_helper.rb', line 110 def self.create_icons(icon_name:nil, new_icon_dir:nil) unless File.exist?(icon_name) Funlog.instance.("文件不存在: #{icon_name}") return nil end begin FileUtils.mkdir_p(new_icon_dir) rescue => e Funlog.instance.("创建输出目录失败: #{e.}") return nil end all_icons = {} success_count = 0 ANDROID_ICON_DENSITIES.each do |density, size| icons = create_icon_for_density( icon_name: icon_name, new_icon_dir: new_icon_dir, density: density, size: size ) if icons all_icons[density] = icons success_count += 1 else Funlog.instance.("#{density}密度icon生成失败,跳过") end end # 如果所有密度都失败了,返回 nil return success_count > 0 ? all_icons : nil end |
.drawable_resource_exists?(res_dir:, resource_name:) ⇒ Boolean
检查 drawable 资源是否存在(任意 drawable 变体目录、任意常见后缀)
310 311 312 313 314 315 316 317 318 319 |
# File 'lib/pindo/module/android/android_res_helper.rb', line 310 def self.drawable_resource_exists?(res_dir:, resource_name:) drawable_dirs = Dir.glob(File.join(res_dir, "drawable*")).select { |d| File.directory?(d) } return false if drawable_dirs.empty? drawable_dirs.any? do |dir| Dir.glob(File.join(dir, "#{resource_name}.*")).any? { |f| File.file?(f) } end rescue false end |
.ensure_standard_adaptive_icon_xml(res_dir: nil) ⇒ Boolean
确保存在标准 adaptive icon XML(Android 8+)若项目已存在 mipmap-anydpi-v26,则保持原文件不覆盖
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 |
# File 'lib/pindo/module/android/android_res_helper.rb', line 223 def self.ensure_standard_adaptive_icon_xml(res_dir:nil) return false unless res_dir && File.directory?(res_dir) begin adaptive_dir = File.join(res_dir, "mipmap-anydpi-v26") if Dir.exist?(adaptive_dir) puts " ✓ 检测到现有 adaptive icon 配置,保持不覆盖: mipmap-anydpi-v26" return true end FileUtils.mkdir_p(adaptive_dir) # Android 官方建议: # background 使用 @color/ic_launcher_background # foreground 使用 @drawable/ic_launcher_foreground # 若项目缺失上述资源,则回退到可用引用,避免构建失败。 background_drawable = color_resource_exists?(res_dir: res_dir, resource_name: "ic_launcher_background") ? "@color/ic_launcher_background" : "@android:color/white" # 优先级: # 1) drawable/ic_launcher_foreground(兼容已有项目) # 2) mipmap/ic_launcher_foreground(本工具自动生成) # 3) 回退到 legacy icon,避免构建失败 foreground_drawable = if drawable_resource_exists?(res_dir: res_dir, resource_name: "ic_launcher_foreground") "@drawable/ic_launcher_foreground" elsif mipmap_resource_exists?(res_dir: res_dir, resource_name: "ic_launcher_foreground") "@mipmap/ic_launcher_foreground" else "@mipmap/ic_launcher" end round_foreground_drawable = if drawable_resource_exists?(res_dir: res_dir, resource_name: "ic_launcher_foreground") "@drawable/ic_launcher_foreground" elsif mipmap_resource_exists?(res_dir: res_dir, resource_name: "ic_launcher_foreground") "@mipmap/ic_launcher_foreground" else "@mipmap/ic_launcher_round" end launcher_xml = <<~XML <?xml version="1.0" encoding="utf-8"?> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <background android:drawable="#{background_drawable}" /> <foreground android:drawable="#{foreground_drawable}" /> </adaptive-icon> XML launcher_round_xml = <<~XML <?xml version="1.0" encoding="utf-8"?> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <background android:drawable="#{background_drawable}" /> <foreground android:drawable="#{round_foreground_drawable}" /> </adaptive-icon> XML File.write(File.join(adaptive_dir, "ic_launcher.xml"), launcher_xml) File.write(File.join(adaptive_dir, "ic_launcher_round.xml"), launcher_round_xml) puts " ✓ 缺失时自动创建 adaptive icon XML: mipmap-anydpi-v26" true rescue => e Funlog.instance.("处理 adaptive icon XML 失败: #{e.}") false end end |
.extract_icon_resource_refs_from_manifest(manifest_path: nil) ⇒ Array<Hash>
从 AndroidManifest.xml 中提取 application 的 icon 资源引用
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 |
# File 'lib/pindo/module/android/android_res_helper.rb', line 339 def self.extract_icon_resource_refs_from_manifest(manifest_path:nil) return [] unless manifest_path && File.exist?(manifest_path) begin require 'nokogiri' content = File.read(manifest_path) doc = Nokogiri::XML(content) app_node = doc.at_xpath('//application') return [] unless app_node refs = [] { 'android:icon' => 'icon', 'android:roundIcon' => 'roundIcon' }.each do |attr_name, kind| attr_value = app_node[attr_name] next unless attr_value.is_a?(String) if (matched = attr_value.match(/^@([a-zA-Z0-9_]+)\/([a-zA-Z0-9_]+)$/)) refs << { type: matched[1], name: matched[2], kind: kind } end end refs.uniq { |ref| [ref[:type], ref[:name], ref[:kind]] } rescue => e Funlog.instance.("解析 AndroidManifest icon 配置失败: #{e.}") [] end end |
.find_best_icon_source_file(source_dir:, round: false) ⇒ String?
在目录内查找最匹配的 icon 文件
374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 |
# File 'lib/pindo/module/android/android_res_helper.rb', line 374 def self.find_best_icon_source_file(source_dir:, round: false) return nil unless source_dir && File.directory?(source_dir) exact_names = round ? %w[ic_launcher_round app_icon_round] : %w[ic_launcher app_icon] ext_order = %w[png webp] exact_names.each do |basename| ext_order.each do |ext| candidate = File.join(source_dir, "#{basename}.#{ext}") return candidate if File.file?(candidate) end end image_files = Dir.glob(File.join(source_dir, "*.{png,webp}")).select { |f| File.file?(f) } return nil if image_files.empty? round_match = /(?:^|[_-])(round|launcher_round|ic_round)(?:[_-]|$)/i non_round_files = image_files.reject { |f| File.basename(f, File.extname(f)).match?(round_match) } round_files = image_files.select { |f| File.basename(f, File.extname(f)).match?(round_match) } selected = round ? round_files.first : non_round_files.first selected || image_files.first end |
.install_icon(proj_dir: nil, new_icon_dir: nil) ⇒ Boolean
安装Android icon到项目中(完全标准化方案)
466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 |
# File 'lib/pindo/module/android/android_res_helper.rb', line 466 def self.install_icon(proj_dir:nil, new_icon_dir:nil) unless proj_dir && File.directory?(proj_dir) Funlog.instance.("项目目录不存在: #{proj_dir}") return false end unless new_icon_dir && File.directory?(new_icon_dir) Funlog.instance.("Icon目录不存在: #{new_icon_dir}") return false end begin # 1. 查找 res 目录(支持 launcher、app 和 unityLibrary) possible_res_dirs = [ File.join(proj_dir, "launcher/src/main/res"), File.join(proj_dir, "app/src/main/res"), File.join(proj_dir, "unityLibrary/src/main/res") ] res_dir = possible_res_dirs.find { |dir| File.directory?(dir) } # 如果没找到,创建默认目录 unless res_dir res_dir = File.join(proj_dir, "launcher/src/main/res") FileUtils.mkdir_p(res_dir) end # 2. 查找 AndroidManifest.xml 并提取旧 icon 引用 manifest_paths = [ File.join(proj_dir, "launcher/src/main/AndroidManifest.xml"), File.join(proj_dir, "app/src/main/AndroidManifest.xml"), File.join(proj_dir, "src/main/AndroidManifest.xml") ] manifest_path = manifest_paths.find { |path| File.exist?(path) } old_icon_refs = extract_icon_resource_refs_from_manifest(manifest_path: manifest_path) # 3. 清理所有旧的 icon 资源 clean_old_icons(res_dir: res_dir, old_icon_refs: old_icon_refs) # 4. 更新 AndroidManifest.xml 为标准 icon 配置 if manifest_path update_manifest_to_standard(manifest_path: manifest_path) end # 4.5 更新所有 XML 文件中的图标引用 update_all_xml_icon_references(proj_dir: proj_dir, old_icon_refs: old_icon_refs) # 5. 安装标准 icon 文件 success_count = 0 densities_installed = [] ANDROID_ICON_DENSITIES.keys.each do |density| source_dir = File.join(new_icon_dir, "mipmap-#{density}") target_dir = File.join(res_dir, "mipmap-#{density}") next unless File.directory?(source_dir) # 创建目标目录 FileUtils.mkdir_p(target_dir) unless File.directory?(target_dir) # 先清理同名不同后缀,避免 aapt2 Duplicate resources (png/webp 并存) remove_icon_files_with_same_basename(mipmap_dir: target_dir, basename: "ic_launcher") remove_icon_files_with_same_basename(mipmap_dir: target_dir, basename: "ic_launcher_round") remove_icon_files_with_same_basename(mipmap_dir: target_dir, basename: "ic_launcher_foreground") icon_source = find_best_icon_source_file(source_dir: source_dir, round: false) round_source = find_best_icon_source_file(source_dir: source_dir, round: true) foreground_source = find_best_icon_source_file(source_dir: source_dir, round: false) explicit_foreground_png = File.join(source_dir, "ic_launcher_foreground.png") explicit_foreground_webp = File.join(source_dir, "ic_launcher_foreground.webp") foreground_source = explicit_foreground_png if File.file?(explicit_foreground_png) foreground_source = explicit_foreground_webp if File.file?(explicit_foreground_webp) files_to_copy = [ { source_file: icon_source, target: 'ic_launcher.png' }, { source_file: round_source, target: 'ic_launcher_round.png' }, { source_file: foreground_source, target: 'ic_launcher_foreground.png' } ] density_success = 0 files_to_copy.each do |file_map| source_file = file_map[:source_file] target_file = File.join(target_dir, file_map[:target]) if source_file && File.exist?(source_file) FileUtils.cp(source_file, target_file) success_count += 1 density_success += 1 else Funlog.instance.("源文件不存在: #{file_map[:target]}") end end densities_installed << density if density_success > 0 end if success_count > 0 puts " ✓ 已安装标准 icon: #{densities_installed.join(', ')} (共#{success_count}个文件)" end # 6. 确保 adaptive icon XML 存在(已有配置不覆盖,缺失时补齐) ensure_standard_adaptive_icon_xml(res_dir: res_dir) return success_count > 0 rescue => e Funlog.instance.("安装icon时出错: #{e.}") puts e.backtrace.join("\n") if ENV['PINDO_DEBUG'] return false end end |
.mipmap_resource_exists?(res_dir:, resource_name:) ⇒ Boolean
检查 mipmap 资源是否存在(任意 mipmap 变体目录、任意常见后缀)
325 326 327 328 329 330 331 332 333 334 |
# File 'lib/pindo/module/android/android_res_helper.rb', line 325 def self.mipmap_resource_exists?(res_dir:, resource_name:) mipmap_dirs = Dir.glob(File.join(res_dir, "mipmap*")).select { |d| File.directory?(d) } return false if mipmap_dirs.empty? mipmap_dirs.any? do |dir| Dir.glob(File.join(dir, "#{resource_name}.*")).any? { |f| File.file?(f) } end rescue false end |
.remove_icon_files_with_same_basename(mipmap_dir:, basename:) ⇒ Integer
删除目录下同名不同后缀图标(例如 ic_launcher.png / ic_launcher.webp)
402 403 404 405 406 407 408 409 410 411 412 413 414 |
# File 'lib/pindo/module/android/android_res_helper.rb', line 402 def self.remove_icon_files_with_same_basename(mipmap_dir:, basename:) return 0 unless File.directory?(mipmap_dir) return 0 if basename.nil? || basename.empty? removed = 0 Dir.glob(File.join(mipmap_dir, "#{basename}.*")).each do |file_path| next unless File.file?(file_path) File.delete(file_path) removed += 1 end removed end |
.update_all_xml_icon_references(proj_dir: nil, old_icon_refs: []) ⇒ Boolean
更新所有 XML 文件中的图标引用(从旧名称更新为标准名称)
583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 |
# File 'lib/pindo/module/android/android_res_helper.rb', line 583 def self.update_all_xml_icon_references(proj_dir: nil, old_icon_refs: []) return false unless proj_dir && File.directory?(proj_dir) begin updated_files = [] # 查找所有 XML 文件 xml_files = Dir.glob(File.join(proj_dir, "**/*.xml")) .reject { |f| f.include?("/build/") || f.include?("/.gradle/") } xml_files.each do |xml_file| content = File.read(xml_file) original_content = content.dup # 替换 manifest 中已解析到的旧图标引用 old_icon_refs.each do |ref| next unless ref.is_a?(Hash) old_name = ref[:name].to_s kind = ref[:kind].to_s next if old_name.empty? new_name = kind == "roundIcon" ? "ic_launcher_round" : "ic_launcher" content.gsub!(/@mipmap\/#{Regexp.escape(old_name)}(?=["'\s>])/, "@mipmap/#{new_name}") content.gsub!(/@drawable\/#{Regexp.escape(old_name)}(?=["'\s>])/, "@mipmap/#{new_name}") end # 替换常见旧图标引用为标准名称 # app_icon -> ic_launcher # app_icon_round -> ic_launcher_round # 使用正则表达式精确匹配,避免替换 app_icon_topleft 等变体 # (?=["'\s>]) 表示后面必须是引号、空格或尖括号(但不包含在替换结果中) content.gsub!(/@mipmap\/app_icon_round(?=["'\s>])/, '@mipmap/ic_launcher_round') content.gsub!(/@mipmap\/app_icon(?=["'\s>])/, '@mipmap/ic_launcher') content.gsub!(/@drawable\/app_icon_round(?=["'\s>])/, '@mipmap/ic_launcher_round') content.gsub!(/@drawable\/app_icon(?=["'\s>])/, '@mipmap/ic_launcher') # 如果内容有变化,保存文件 if content != original_content File.write(xml_file, content) relative_path = xml_file.sub(proj_dir + '/', '') updated_files << relative_path puts " ✓ 更新图标引用: #{relative_path}" end end if updated_files.empty? puts " ✓ 所有 XML 文件的图标引用已是标准配置" else puts " ✓ 已更新 #{updated_files.length} 个文件的图标引用" end return true rescue => e Funlog.instance.("更新 XML 图标引用失败: #{e.}") return false end end |
.update_manifest_icon_attributes_in_place(xml_content) ⇒ Object
438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 |
# File 'lib/pindo/module/android/android_res_helper.rb', line 438 def self.update_manifest_icon_attributes_in_place(xml_content) icon_value = "@mipmap/ic_launcher" round_value = "@mipmap/ic_launcher_round" # 仅修改 <application ...> 开始标签内的 android:icon/android:roundIcon,避免整体格式化。 xml_content.sub(/<application\b[^>]*>/m) do |app_tag| updated_tag = app_tag.dup if updated_tag.match?(/\bandroid:icon\s*=\s*["'][^"']*["']/) updated_tag.gsub!(/\bandroid:icon\s*=\s*(["'])[^"']*\1/, %(android:icon="#{icon_value}")) else updated_tag.sub!(/<application\b/, %(<application android:icon="#{icon_value}")) end if updated_tag.match?(/\bandroid:roundIcon\s*=\s*["'][^"']*["']/) updated_tag.gsub!(/\bandroid:roundIcon\s*=\s*(["'])[^"']*\1/, %(android:roundIcon="#{round_value}")) else updated_tag.sub!(/<application\b/, %(<application android:roundIcon="#{round_value}")) end updated_tag end end |
.update_manifest_to_standard(manifest_path: nil) ⇒ Boolean
更新 AndroidManifest.xml 为标准配置
419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 |
# File 'lib/pindo/module/android/android_res_helper.rb', line 419 def self.update_manifest_to_standard(manifest_path:nil) return false unless manifest_path && File.exist?(manifest_path) begin content = File.read(manifest_path) updated = update_manifest_icon_attributes_in_place(content) return false unless updated File.write(manifest_path, updated) puts " ✓ AndroidManifest.xml 已更新为标准 icon 配置" return true rescue => e Funlog.instance.("更新 AndroidManifest.xml 失败: #{e.}") return false end end |