app-info 2.8.2 → 3.0.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/.github/dependabot.yml +12 -7
- data/.github/workflows/ci.yml +7 -5
- data/.github/workflows/create_release.yml +15 -0
- data/.rubocop.yml +33 -11
- data/CHANGELOG.md +107 -1
- data/Gemfile +10 -5
- data/README.md +82 -15
- data/Rakefile +11 -0
- data/app_info.gemspec +14 -5
- data/lib/app_info/aab.rb +76 -110
- data/lib/app_info/android/signature.rb +114 -0
- data/lib/app_info/android/signatures/base.rb +53 -0
- data/lib/app_info/android/signatures/info.rb +158 -0
- data/lib/app_info/android/signatures/v1.rb +63 -0
- data/lib/app_info/android/signatures/v2.rb +121 -0
- data/lib/app_info/android/signatures/v3.rb +131 -0
- data/lib/app_info/android/signatures/v4.rb +18 -0
- data/lib/app_info/android.rb +181 -0
- data/lib/app_info/apk.rb +77 -112
- data/lib/app_info/apple.rb +192 -0
- data/lib/app_info/certificate.rb +176 -0
- data/lib/app_info/const.rb +76 -0
- data/lib/app_info/core_ext/object/try.rb +3 -1
- data/lib/app_info/core_ext/string/inflector.rb +2 -0
- data/lib/app_info/dsym/debug_info.rb +81 -0
- data/lib/app_info/dsym/macho.rb +62 -0
- data/lib/app_info/dsym.rb +35 -135
- data/lib/app_info/error.rb +3 -1
- data/lib/app_info/file.rb +49 -0
- data/lib/app_info/helper/archive.rb +37 -0
- data/lib/app_info/helper/file_size.rb +25 -0
- data/lib/app_info/helper/generate_class.rb +29 -0
- data/lib/app_info/helper/protobuf.rb +12 -0
- data/lib/app_info/helper/signatures.rb +229 -0
- data/lib/app_info/helper.rb +5 -128
- data/lib/app_info/info_plist.rb +66 -29
- data/lib/app_info/ipa/framework.rb +4 -4
- data/lib/app_info/ipa.rb +61 -135
- data/lib/app_info/macos.rb +54 -102
- data/lib/app_info/mobile_provision.rb +66 -48
- data/lib/app_info/pe.rb +322 -0
- data/lib/app_info/png_uncrush.rb +25 -5
- data/lib/app_info/proguard.rb +39 -22
- data/lib/app_info/protobuf/manifest.rb +22 -11
- data/lib/app_info/protobuf/models/Configuration_pb.rb +1 -0
- data/lib/app_info/protobuf/models/README.md +8 -1
- data/lib/app_info/protobuf/models/Resources.proto +51 -0
- data/lib/app_info/protobuf/models/Resources_pb.rb +42 -0
- data/lib/app_info/protobuf/resources.rb +5 -5
- data/lib/app_info/version.rb +1 -1
- data/lib/app_info.rb +93 -43
- metadata +57 -37
data/lib/app_info/helper.rb
CHANGED
@@ -1,130 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
ANDROID = 'Android'
|
9
|
-
DSYM = 'dSYM'
|
10
|
-
PROGUARD = 'Proguard'
|
11
|
-
end
|
12
|
-
|
13
|
-
# Device Type
|
14
|
-
module Device
|
15
|
-
MACOS = 'macOS'
|
16
|
-
IPHONE = 'iPhone'
|
17
|
-
IPAD = 'iPad'
|
18
|
-
UNIVERSAL = 'Universal'
|
19
|
-
end
|
20
|
-
|
21
|
-
module AndroidDevice
|
22
|
-
PHONE = 'Phone'
|
23
|
-
TABLET = 'Tablet'
|
24
|
-
WATCH = 'Watch'
|
25
|
-
TV = 'Television'
|
26
|
-
end
|
27
|
-
|
28
|
-
# Icon Key
|
29
|
-
ICON_KEYS = {
|
30
|
-
Device::IPHONE => ['CFBundleIcons'],
|
31
|
-
Device::IPAD => ['CFBundleIcons~ipad'],
|
32
|
-
Device::UNIVERSAL => ['CFBundleIcons', 'CFBundleIcons~ipad'],
|
33
|
-
Device::MACOS => %w[CFBundleIconFile CFBundleIconName]
|
34
|
-
}.freeze
|
35
|
-
|
36
|
-
module Helper
|
37
|
-
module HumanFileSize
|
38
|
-
def file_to_human_size(file, human_size:)
|
39
|
-
number = File.size(file)
|
40
|
-
human_size ? number_to_human_size(number) : number
|
41
|
-
end
|
42
|
-
|
43
|
-
FILE_SIZE_UNITS = %w[B KB MB GB TB].freeze
|
44
|
-
|
45
|
-
def number_to_human_size(number)
|
46
|
-
if number.to_i < 1024
|
47
|
-
exponent = 0
|
48
|
-
else
|
49
|
-
max_exp = FILE_SIZE_UNITS.size - 1
|
50
|
-
exponent = (Math.log(number) / Math.log(1024)).to_i
|
51
|
-
exponent = max_exp if exponent > max_exp
|
52
|
-
number = format('%<number>.2f', number: (number / (1024**exponent.to_f)))
|
53
|
-
end
|
54
|
-
|
55
|
-
"#{number} #{FILE_SIZE_UNITS[exponent]}"
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
module Archive
|
60
|
-
require 'zip'
|
61
|
-
require 'fileutils'
|
62
|
-
require 'securerandom'
|
63
|
-
|
64
|
-
# Unarchive zip file
|
65
|
-
#
|
66
|
-
# source: https://github.com/soffes/lagunitas/blob/master/lib/lagunitas/ipa.rb
|
67
|
-
def unarchive(file, path: nil)
|
68
|
-
path = path ? "#{path}-" : ''
|
69
|
-
root_path = "#{Dir.mktmpdir}/AppInfo-#{path}#{SecureRandom.hex}"
|
70
|
-
Zip::File.open(file) do |zip_file|
|
71
|
-
if block_given?
|
72
|
-
yield root_path, zip_file
|
73
|
-
else
|
74
|
-
zip_file.each do |f|
|
75
|
-
f_path = File.join(root_path, f.name)
|
76
|
-
FileUtils.mkdir_p(File.dirname(f_path))
|
77
|
-
zip_file.extract(f, f_path) unless File.exist?(f_path)
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
root_path
|
83
|
-
end
|
84
|
-
|
85
|
-
def tempdir(file, prefix:)
|
86
|
-
dest_path ||= File.join(File.dirname(file), prefix)
|
87
|
-
dest_file = File.join(dest_path, File.basename(file))
|
88
|
-
|
89
|
-
Dir.mkdir(dest_path, 0_700) unless Dir.exist?(dest_path)
|
90
|
-
|
91
|
-
dest_file
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
module Defines
|
96
|
-
def create_class(klass_name, parent_class, namespace:)
|
97
|
-
klass = Class.new(parent_class) do
|
98
|
-
yield if block_given?
|
99
|
-
end
|
100
|
-
|
101
|
-
name = namespace.to_s.empty? ? klass_name : "#{namespace}::#{klass_name}"
|
102
|
-
if Object.const_get(namespace).const_defined?(klass_name)
|
103
|
-
Object.const_get(namespace).const_get(klass_name)
|
104
|
-
elsif Object.const_defined?(name)
|
105
|
-
Object.const_get(name)
|
106
|
-
else
|
107
|
-
Object.const_get(namespace).const_set(klass_name, klass)
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
def define_instance_method(key, value)
|
112
|
-
instance_variable_set("@#{key}", value)
|
113
|
-
self.class.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
114
|
-
def #{key}
|
115
|
-
@#{key}
|
116
|
-
end
|
117
|
-
RUBY
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
module ReferenceParser
|
122
|
-
def reference_segments(value)
|
123
|
-
new_value = value.is_a?(Aapt::Pb::Reference) ? value.name : value
|
124
|
-
return new_value.split('/', 2) if new_value.include?('/')
|
125
|
-
|
126
|
-
[nil, new_value]
|
127
|
-
end
|
128
|
-
end
|
129
|
-
end
|
130
|
-
end
|
3
|
+
require 'app_info/helper/archive'
|
4
|
+
require 'app_info/helper/file_size'
|
5
|
+
require 'app_info/helper/generate_class'
|
6
|
+
require 'app_info/helper/protobuf'
|
7
|
+
require 'app_info/helper/signatures'
|
data/lib/app_info/info_plist.rb
CHANGED
@@ -6,97 +6,131 @@ require 'app_info/png_uncrush'
|
|
6
6
|
|
7
7
|
module AppInfo
|
8
8
|
# iOS Info.plist parser
|
9
|
-
class InfoPlist
|
9
|
+
class InfoPlist < File
|
10
10
|
extend Forwardable
|
11
11
|
|
12
|
-
|
13
|
-
|
12
|
+
# Icon Key
|
13
|
+
ICON_KEYS = {
|
14
|
+
Device::IPHONE => ['CFBundleIcons'],
|
15
|
+
Device::IPAD => ['CFBundleIcons~ipad'],
|
16
|
+
Device::UNIVERSAL => ['CFBundleIcons', 'CFBundleIcons~ipad'],
|
17
|
+
Device::MACOS => %w[CFBundleIconFile CFBundleIconName]
|
18
|
+
}.freeze
|
19
|
+
|
20
|
+
# @return [Symbol] {Manufacturer}
|
21
|
+
def manufacturer
|
22
|
+
Manufacturer::APPLE
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [Symbol] {Platform}
|
26
|
+
def platform
|
27
|
+
case device
|
28
|
+
when Device::MACOS
|
29
|
+
Platform::MACOS
|
30
|
+
when Device::IPHONE, Device::IPAD, Device::UNIVERSAL
|
31
|
+
Platform::IOS
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# @return [Symbol] {Device}
|
36
|
+
def device
|
37
|
+
if device_family == [1]
|
38
|
+
Device::IPHONE
|
39
|
+
elsif device_family == [2]
|
40
|
+
Device::IPAD
|
41
|
+
elsif device_family == [1, 2]
|
42
|
+
Device::UNIVERSAL
|
43
|
+
elsif !info.try(:[], 'DTSDKName').nil? || !info.try(:[], 'DTManufacturerName').nil?
|
44
|
+
Device::MACOS
|
45
|
+
else
|
46
|
+
raise NotImplementedError, "Unkonwn device: #{device_family}"
|
47
|
+
end
|
14
48
|
end
|
15
49
|
|
50
|
+
# @return [String, nil]
|
16
51
|
def version
|
17
52
|
release_version || build_version
|
18
53
|
end
|
19
54
|
|
55
|
+
# @return [String, nil]
|
20
56
|
def build_version
|
21
57
|
info.try(:[], 'CFBundleVersion')
|
22
58
|
end
|
23
59
|
|
60
|
+
# @return [String, nil]
|
24
61
|
def release_version
|
25
62
|
info.try(:[], 'CFBundleShortVersionString')
|
26
63
|
end
|
27
64
|
|
65
|
+
# @return [String, nil]
|
28
66
|
def identifier
|
29
67
|
info.try(:[], 'CFBundleIdentifier')
|
30
68
|
end
|
31
69
|
alias bundle_id identifier
|
32
70
|
|
71
|
+
# @return [String, nil]
|
33
72
|
def name
|
34
73
|
display_name || bundle_name
|
35
74
|
end
|
36
75
|
|
76
|
+
# @return [String, nil]
|
37
77
|
def display_name
|
38
78
|
info.try(:[], 'CFBundleDisplayName')
|
39
79
|
end
|
40
80
|
|
81
|
+
# @return [String, nil]
|
41
82
|
def bundle_name
|
42
83
|
info.try(:[], 'CFBundleName')
|
43
84
|
end
|
44
85
|
|
86
|
+
# @return [String, nil]
|
45
87
|
def min_os_version
|
46
88
|
min_sdk_version || min_system_version
|
47
89
|
end
|
48
90
|
|
49
|
-
#
|
50
91
|
# Extract the Minimum OS Version from the Info.plist (iOS Only)
|
51
|
-
#
|
92
|
+
# @return [String, nil]
|
52
93
|
def min_sdk_version
|
53
94
|
info.try(:[], 'MinimumOSVersion')
|
54
95
|
end
|
55
96
|
|
56
|
-
#
|
57
97
|
# Extract the Minimum OS Version from the Info.plist (macOS Only)
|
58
|
-
#
|
98
|
+
# @return [String, nil]
|
59
99
|
def min_system_version
|
60
100
|
info.try(:[], 'LSMinimumSystemVersion')
|
61
101
|
end
|
62
102
|
|
103
|
+
# @return [Array<String>]
|
63
104
|
def icons
|
64
|
-
@icons ||= ICON_KEYS[
|
65
|
-
end
|
66
|
-
|
67
|
-
def device_type
|
68
|
-
device_family = info.try(:[], 'UIDeviceFamily')
|
69
|
-
if device_family == [1]
|
70
|
-
Device::IPHONE
|
71
|
-
elsif device_family == [2]
|
72
|
-
Device::IPAD
|
73
|
-
elsif device_family == [1, 2]
|
74
|
-
Device::UNIVERSAL
|
75
|
-
elsif !info.try(:[], 'DTSDKName').nil? || !info.try(:[], 'DTPlatformName').nil?
|
76
|
-
Device::MACOS
|
77
|
-
end
|
105
|
+
@icons ||= ICON_KEYS[device]
|
78
106
|
end
|
79
107
|
|
108
|
+
# @return [Boolean]
|
80
109
|
def iphone?
|
81
|
-
|
110
|
+
device == Device::IPHONE
|
82
111
|
end
|
83
112
|
|
113
|
+
# @return [Boolean]
|
84
114
|
def ipad?
|
85
|
-
|
115
|
+
device == Device::IPAD
|
86
116
|
end
|
87
117
|
|
118
|
+
# @return [Boolean]
|
88
119
|
def universal?
|
89
|
-
|
120
|
+
device == Device::UNIVERSAL
|
90
121
|
end
|
91
122
|
|
123
|
+
# @return [Boolean]
|
92
124
|
def macos?
|
93
|
-
|
125
|
+
device == Device::MACOS
|
94
126
|
end
|
95
127
|
|
128
|
+
# @return [Array<String>]
|
96
129
|
def device_family
|
97
130
|
info.try(:[], 'UIDeviceFamily') || []
|
98
131
|
end
|
99
132
|
|
133
|
+
# @return [String]
|
100
134
|
def release_type
|
101
135
|
if stored?
|
102
136
|
'Store'
|
@@ -105,10 +139,13 @@ module AppInfo
|
|
105
139
|
end
|
106
140
|
end
|
107
141
|
|
142
|
+
# @return [String, nil]
|
108
143
|
def [](key)
|
109
144
|
info.try(:[], key.to_s)
|
110
145
|
end
|
111
146
|
|
147
|
+
# @!method to_h
|
148
|
+
# @see CFPropertyList#to_h
|
112
149
|
def_delegators :info, :to_h
|
113
150
|
|
114
151
|
def method_missing(method_name, *args, &block)
|
@@ -126,17 +163,17 @@ module AppInfo
|
|
126
163
|
private
|
127
164
|
|
128
165
|
def info
|
129
|
-
return unless File.file?(@file)
|
166
|
+
return unless ::File.file?(@file)
|
130
167
|
|
131
168
|
@info ||= CFPropertyList.native_types(CFPropertyList::List.new(file: @file).value)
|
132
169
|
end
|
133
170
|
|
134
171
|
def app_path
|
135
|
-
@app_path ||= case
|
172
|
+
@app_path ||= case device
|
136
173
|
when Device::MACOS
|
137
|
-
File.dirname(@file)
|
174
|
+
::File.dirname(@file)
|
138
175
|
else
|
139
|
-
File.expand_path('../', @file)
|
176
|
+
::File.expand_path('../', @file)
|
140
177
|
end
|
141
178
|
end
|
142
179
|
end
|
@@ -8,7 +8,7 @@ module AppInfo
|
|
8
8
|
extend Forwardable
|
9
9
|
|
10
10
|
def self.parse(path, name = 'Frameworks')
|
11
|
-
files = Dir.glob(File.join(path, name.to_s, '*'))
|
11
|
+
files = Dir.glob(::File.join(path, name.to_s, '*'))
|
12
12
|
return [] if files.empty?
|
13
13
|
|
14
14
|
files.sort.each_with_object([]) do |file, obj|
|
@@ -26,7 +26,7 @@ module AppInfo
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def name
|
29
|
-
File.basename(file)
|
29
|
+
::File.basename(file)
|
30
30
|
end
|
31
31
|
|
32
32
|
def macho
|
@@ -37,11 +37,11 @@ module AppInfo
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def lib?
|
40
|
-
File.file?(file)
|
40
|
+
::File.file?(file)
|
41
41
|
end
|
42
42
|
|
43
43
|
def info
|
44
|
-
@info ||= InfoPlist.new(File.join(file, 'Info.plist'))
|
44
|
+
@info ||= InfoPlist.new(::File.join(file, 'Info.plist'))
|
45
45
|
end
|
46
46
|
|
47
47
|
def to_s
|
data/lib/app_info/ipa.rb
CHANGED
@@ -6,179 +6,106 @@ require 'forwardable'
|
|
6
6
|
require 'cfpropertylist'
|
7
7
|
|
8
8
|
module AppInfo
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
#
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
def initialize(file)
|
29
|
-
@file = file
|
30
|
-
end
|
31
|
-
|
32
|
-
def size(human_size: false)
|
33
|
-
file_to_human_size(@file, human_size: human_size)
|
34
|
-
end
|
35
|
-
|
36
|
-
def os
|
37
|
-
Platform::IOS
|
38
|
-
end
|
39
|
-
alias file_type os
|
40
|
-
|
41
|
-
def_delegators :info, :iphone?, :ipad?, :universal?, :build_version, :name,
|
42
|
-
:release_version, :identifier, :bundle_id, :display_name,
|
43
|
-
:bundle_name, :min_sdk_version, :min_os_version, :device_type
|
44
|
-
|
45
|
-
def_delegators :mobileprovision, :devices, :team_name, :team_identifier,
|
46
|
-
:profile_name, :expired_date
|
47
|
-
|
48
|
-
def distribution_name
|
49
|
-
"#{profile_name} - #{team_name}" if profile_name && team_name
|
50
|
-
end
|
51
|
-
|
52
|
-
def release_type
|
53
|
-
if stored?
|
54
|
-
ExportType::RELEASE
|
55
|
-
else
|
56
|
-
build_type
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
def build_type
|
61
|
-
if mobileprovision?
|
62
|
-
if devices
|
63
|
-
ExportType::ADHOC
|
64
|
-
else
|
65
|
-
ExportType::ENTERPRISE
|
66
|
-
end
|
67
|
-
else
|
68
|
-
ExportType::DEBUG
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
def archs
|
73
|
-
return unless File.exist?(bundle_path)
|
74
|
-
|
75
|
-
file = MachO.open(bundle_path)
|
76
|
-
case file
|
77
|
-
when MachO::MachOFile
|
78
|
-
[file.cpusubtype]
|
79
|
-
else
|
80
|
-
file.machos.each_with_object([]) do |arch, obj|
|
81
|
-
obj << arch.cpusubtype
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
alias architectures archs
|
86
|
-
|
9
|
+
class IPA < Apple
|
10
|
+
# Full icons metadata
|
11
|
+
# @example
|
12
|
+
# aab.icons
|
13
|
+
# # => [
|
14
|
+
# # {
|
15
|
+
# # name: 'icon.png',
|
16
|
+
# # file: '/path/to/icon.png',
|
17
|
+
# # uncrushed_file: '/path/to/uncrushed_icon.png',
|
18
|
+
# # dimensions: [64, 64]
|
19
|
+
# # },
|
20
|
+
# # {
|
21
|
+
# # name: 'icon1.png',
|
22
|
+
# # file: '/path/to/icon1.png',
|
23
|
+
# # uncrushed_file: '/path/to/uncrushed_icon1.png',
|
24
|
+
# # dimensions: [120, 120]
|
25
|
+
# # }
|
26
|
+
# # ]
|
27
|
+
# @return [Array<Hash{Symbol => String, Array<Integer>}>] icons paths of icons
|
87
28
|
def icons(uncrush: true)
|
88
29
|
@icons ||= icons_path.each_with_object([]) do |file, obj|
|
89
30
|
obj << build_icon_metadata(file, uncrush: uncrush)
|
90
31
|
end
|
91
32
|
end
|
92
33
|
|
34
|
+
# @return [Boolean]
|
93
35
|
def stored?
|
94
36
|
!!metadata?
|
95
37
|
end
|
96
38
|
|
39
|
+
# @return [Array<Plugin>]
|
97
40
|
def plugins
|
98
41
|
@plugins ||= Plugin.parse(app_path)
|
99
42
|
end
|
100
43
|
|
44
|
+
# @return [Array<Framework>]
|
101
45
|
def frameworks
|
102
46
|
@frameworks ||= Framework.parse(app_path)
|
103
47
|
end
|
104
48
|
|
105
|
-
|
106
|
-
mobileprovision.delete('DeveloperCertificates') if mobileprovision?
|
107
|
-
end
|
108
|
-
|
109
|
-
def mobileprovision
|
110
|
-
return unless mobileprovision?
|
111
|
-
return @mobileprovision if @mobileprovision
|
112
|
-
|
113
|
-
@mobileprovision = MobileProvision.new(mobileprovision_path)
|
114
|
-
end
|
115
|
-
|
116
|
-
def mobileprovision?
|
117
|
-
File.exist?(mobileprovision_path)
|
118
|
-
end
|
119
|
-
|
49
|
+
# @return [String]
|
120
50
|
def mobileprovision_path
|
121
51
|
filename = 'embedded.mobileprovision'
|
122
|
-
@mobileprovision_path ||= File.join(@file, filename)
|
123
|
-
unless File.exist?(@mobileprovision_path)
|
124
|
-
@mobileprovision_path = File.join(app_path, filename)
|
52
|
+
@mobileprovision_path ||= ::File.join(@file, filename)
|
53
|
+
unless ::File.exist?(@mobileprovision_path)
|
54
|
+
@mobileprovision_path = ::File.join(app_path, filename)
|
125
55
|
end
|
126
56
|
|
127
57
|
@mobileprovision_path
|
128
58
|
end
|
129
59
|
|
60
|
+
# @return [CFPropertyList]
|
130
61
|
def metadata
|
131
62
|
return unless metadata?
|
132
63
|
|
133
64
|
@metadata ||= CFPropertyList.native_types(CFPropertyList::List.new(file: metadata_path).value)
|
134
65
|
end
|
135
66
|
|
67
|
+
# @return [Boolean]
|
136
68
|
def metadata?
|
137
|
-
File.exist?(metadata_path)
|
69
|
+
::File.exist?(metadata_path)
|
138
70
|
end
|
139
71
|
|
72
|
+
# @return [String]
|
140
73
|
def metadata_path
|
141
|
-
@metadata_path ||= File.join(contents, 'iTunesMetadata.plist')
|
74
|
+
@metadata_path ||= ::File.join(contents, 'iTunesMetadata.plist')
|
142
75
|
end
|
143
76
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
def info
|
149
|
-
@info ||= InfoPlist.new(info_path)
|
77
|
+
# @return [String]
|
78
|
+
def binary_path
|
79
|
+
@binary_path ||= ::File.join(app_path, info.bundle_name)
|
150
80
|
end
|
151
81
|
|
82
|
+
# @return [String]
|
152
83
|
def info_path
|
153
|
-
@info_path ||= File.join(app_path, 'Info.plist')
|
84
|
+
@info_path ||= ::File.join(app_path, 'Info.plist')
|
154
85
|
end
|
155
86
|
|
87
|
+
# @return [String]
|
156
88
|
def app_path
|
157
|
-
@app_path ||= Dir.glob(File.join(contents, 'Payload', '*.app')).first
|
89
|
+
@app_path ||= Dir.glob(::File.join(contents, 'Payload', '*.app')).first
|
158
90
|
end
|
159
91
|
|
160
|
-
|
161
|
-
IPAD_KEY = 'CFBundleIcons~ipad'
|
162
|
-
|
92
|
+
# @return [Array<String>]
|
163
93
|
def icons_path
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
@icons_path << file
|
94
|
+
@icons_path ||= lambda {
|
95
|
+
icon_keys.each_with_object([]) do |name, icons|
|
96
|
+
filenames = info.try(:[], name)
|
97
|
+
.try(:[], 'CFBundlePrimaryIcon')
|
98
|
+
.try(:[], 'CFBundleIconFiles')
|
99
|
+
|
100
|
+
next if filenames.nil? || filenames.empty?
|
101
|
+
|
102
|
+
filenames.each do |filename|
|
103
|
+
Dir.glob(::File.join(app_path, "#{filename}*")).find_all.each do |file|
|
104
|
+
icons << file
|
105
|
+
end
|
177
106
|
end
|
178
107
|
end
|
179
|
-
|
180
|
-
|
181
|
-
@icons_path
|
108
|
+
}.call
|
182
109
|
end
|
183
110
|
|
184
111
|
def clear!
|
@@ -196,17 +123,13 @@ module AppInfo
|
|
196
123
|
@icons = nil
|
197
124
|
end
|
198
125
|
|
199
|
-
def contents
|
200
|
-
@contents ||= unarchive(@file, path: 'ios')
|
201
|
-
end
|
202
|
-
|
203
126
|
private
|
204
127
|
|
205
128
|
def build_icon_metadata(file, uncrush: true)
|
206
129
|
uncrushed_file = uncrush ? uncrush_png(file) : nil
|
207
130
|
|
208
131
|
{
|
209
|
-
name: File.basename(file),
|
132
|
+
name: ::File.basename(file),
|
210
133
|
file: file,
|
211
134
|
uncrushed_file: uncrushed_file,
|
212
135
|
dimensions: PngUncrush.dimensions(file)
|
@@ -217,16 +140,19 @@ module AppInfo
|
|
217
140
|
def uncrush_png(src_file)
|
218
141
|
dest_file = tempdir(src_file, prefix: 'uncrushed')
|
219
142
|
PngUncrush.decompress(src_file, dest_file)
|
220
|
-
File.exist?(dest_file) ? dest_file : nil
|
143
|
+
::File.exist?(dest_file) ? dest_file : nil
|
221
144
|
end
|
222
145
|
|
146
|
+
IPHONE_KEY = 'CFBundleIcons'
|
147
|
+
IPAD_KEY = 'CFBundleIcons~ipad'
|
148
|
+
|
223
149
|
def icon_keys
|
224
|
-
@icon_keys ||= case
|
225
|
-
when
|
150
|
+
@icon_keys ||= case device
|
151
|
+
when Device::IPHONE
|
226
152
|
[IPHONE_KEY]
|
227
|
-
when
|
153
|
+
when Device::IPAD
|
228
154
|
[IPAD_KEY]
|
229
|
-
when
|
155
|
+
when Device::UNIVERSAL
|
230
156
|
[IPHONE_KEY, IPAD_KEY]
|
231
157
|
end
|
232
158
|
end
|