app-info 2.8.5 → 3.0.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +12 -7
  3. data/.github/workflows/ci.yml +3 -1
  4. data/.rubocop.yml +33 -11
  5. data/CHANGELOG.md +34 -1
  6. data/Gemfile +7 -1
  7. data/README.md +68 -13
  8. data/Rakefile +11 -0
  9. data/app_info.gemspec +12 -3
  10. data/lib/app_info/aab.rb +48 -108
  11. data/lib/app_info/android/signature.rb +114 -0
  12. data/lib/app_info/android/signatures/base.rb +53 -0
  13. data/lib/app_info/android/signatures/info.rb +158 -0
  14. data/lib/app_info/android/signatures/v1.rb +63 -0
  15. data/lib/app_info/android/signatures/v2.rb +121 -0
  16. data/lib/app_info/android/signatures/v3.rb +131 -0
  17. data/lib/app_info/android/signatures/v4.rb +18 -0
  18. data/lib/app_info/android.rb +162 -0
  19. data/lib/app_info/apk.rb +54 -111
  20. data/lib/app_info/apple.rb +192 -0
  21. data/lib/app_info/certificate.rb +175 -0
  22. data/lib/app_info/const.rb +75 -0
  23. data/lib/app_info/core_ext/object/try.rb +3 -1
  24. data/lib/app_info/core_ext/string/inflector.rb +2 -0
  25. data/lib/app_info/dsym/debug_info.rb +72 -0
  26. data/lib/app_info/dsym/macho.rb +55 -0
  27. data/lib/app_info/dsym.rb +31 -135
  28. data/lib/app_info/error.rb +2 -2
  29. data/lib/app_info/file.rb +49 -0
  30. data/lib/app_info/helper/archive.rb +37 -0
  31. data/lib/app_info/helper/file_size.rb +25 -0
  32. data/lib/app_info/helper/generate_class.rb +29 -0
  33. data/lib/app_info/helper/protobuf.rb +12 -0
  34. data/lib/app_info/helper/signatures.rb +229 -0
  35. data/lib/app_info/helper.rb +5 -126
  36. data/lib/app_info/info_plist.rb +66 -29
  37. data/lib/app_info/ipa/framework.rb +4 -4
  38. data/lib/app_info/ipa.rb +61 -135
  39. data/lib/app_info/macos.rb +54 -102
  40. data/lib/app_info/mobile_provision.rb +67 -49
  41. data/lib/app_info/pe.rb +260 -0
  42. data/lib/app_info/png_uncrush.rb +24 -4
  43. data/lib/app_info/proguard.rb +29 -16
  44. data/lib/app_info/protobuf/manifest.rb +6 -3
  45. data/lib/app_info/protobuf/models/Configuration_pb.rb +1 -0
  46. data/lib/app_info/protobuf/models/README.md +7 -0
  47. data/lib/app_info/protobuf/models/Resources_pb.rb +2 -0
  48. data/lib/app_info/protobuf/resources.rb +5 -5
  49. data/lib/app_info/version.rb +1 -1
  50. data/lib/app_info.rb +90 -46
  51. metadata +48 -35
@@ -4,20 +4,37 @@ require 'openssl'
4
4
  require 'cfpropertylist'
5
5
 
6
6
  module AppInfo
7
- # .mobileprovision file parser
8
- class MobileProvision
9
- def initialize(path)
10
- @path = path
7
+ # Apple code signing: provisioning profile parser
8
+ # @see https://developer.apple.com/documentation/technotes/tn3125-inside-code-signing-provisioning-profiles
9
+ class MobileProvision < File
10
+ # @return [Symbol] {Platform}
11
+ def platform
12
+ Platform::APPLE
13
+ end
14
+
15
+ # @return [Symbol] {OperaSystem}
16
+ def opera_system
17
+ case opera_systems[0]
18
+ when :macos
19
+ OperaSystem::MACOS
20
+ when :ios
21
+ OperaSystem::IOS
22
+ else
23
+ raise NotImplementedError, "Unkonwn opera_system: #{opera_systems[0]}"
24
+ end
11
25
  end
12
26
 
27
+ # @return [String, nil]
13
28
  def name
14
29
  mobileprovision.try(:[], 'Name')
15
30
  end
16
31
 
32
+ # @return [String, nil]
17
33
  def app_name
18
34
  mobileprovision.try(:[], 'AppIDName')
19
35
  end
20
36
 
37
+ # @return [Symbol, nil]
21
38
  def type
22
39
  return :development if development?
23
40
  return :adhoc if adhoc?
@@ -25,7 +42,8 @@ module AppInfo
25
42
  return :enterprise if enterprise?
26
43
  end
27
44
 
28
- def platforms
45
+ # @return [Array<Symbol>]
46
+ def opera_systems
29
47
  return unless platforms = mobileprovision.try(:[], 'Platform')
30
48
 
31
49
  platforms.map do |v|
@@ -34,83 +52,100 @@ module AppInfo
34
52
  end
35
53
  end
36
54
 
37
- def platform
38
- platforms[0]
39
- end
40
-
55
+ # @return [Array<String>, nil]
41
56
  def devices
42
57
  mobileprovision.try(:[], 'ProvisionedDevices')
43
58
  end
44
59
 
60
+ # @return [String, nil]
45
61
  def team_identifier
46
62
  mobileprovision.try(:[], 'TeamIdentifier')
47
63
  end
48
64
 
65
+ # @return [String, nil]
49
66
  def team_name
50
67
  mobileprovision.try(:[], 'TeamName')
51
68
  end
52
69
 
70
+ # @return [String, nil]
53
71
  def profile_name
54
72
  mobileprovision.try(:[], 'Name')
55
73
  end
56
74
 
75
+ # @return [String, nil]
57
76
  def created_date
58
77
  mobileprovision.try(:[], 'CreationDate')
59
78
  end
60
79
 
80
+ # @return [String, nil]
61
81
  def expired_date
62
82
  mobileprovision.try(:[], 'ExpirationDate')
63
83
  end
64
84
 
85
+ # @return [Array<String>, nil]
65
86
  def entitlements
66
87
  mobileprovision.try(:[], 'Entitlements')
67
88
  end
68
89
 
90
+ # return developer certificates.
91
+ #
92
+ # @deprecated Use {#certificates} instead of this method.
69
93
  def developer_certs
94
+ certificates
95
+ end
96
+
97
+ # return developer certificates.
98
+ #
99
+ # @return [Array<Certificate>]
100
+ def certificates
70
101
  certs = mobileprovision.try(:[], 'DeveloperCertificates')
71
102
  return if certs.empty?
72
103
 
73
- certs.each_with_object([]) do |cert, obj|
74
- obj << DeveloperCertificate.new(cert)
104
+ certs.each_with_object([]) do |cert_data, obj|
105
+ obj << Certificate.parse(cert_data)
75
106
  end
76
107
  end
77
108
 
78
109
  # Detect is development type of mobileprovision
79
110
  #
80
- # related link: https://stackoverflow.com/questions/1003066/what-does-get-task-allow-do-in-xcode
111
+ # @see https://stackoverflow.com/questions/1003066/what-does-get-task-allow-do-in-xcode
112
+ # @return [Boolea]
81
113
  def development?
82
- case platform.downcase.to_sym
83
- when :ios
114
+ case opera_system
115
+ when OperaSystem::IOS
84
116
  entitlements['get-task-allow'] == true
85
- when :macos
117
+ when OperaSystem::MACOS
86
118
  !devices.nil?
87
119
  else
88
- raise Error, "Not implement with platform: #{platform}"
120
+ raise NotImplementedError, "Unknown opera_system: #{opera_system}"
89
121
  end
90
122
  end
91
123
 
92
124
  # Detect app store type
93
125
  #
94
- # related link: https://developer.apple.com/library/archive/qa/qa1830/_index.html
126
+ # @see https://developer.apple.com/library/archive/qa/qa1830/_index.html
127
+ # @return [Boolea]
95
128
  def appstore?
96
- case platform.downcase.to_sym
97
- when :ios
129
+ case opera_system
130
+ when OperaSystem::IOS
98
131
  !development? && entitlements.key?('beta-reports-active')
99
- when :macos
132
+ when OperaSystem::MACOS
100
133
  !development?
101
134
  else
102
- raise Error, "Not implement with platform: #{platform}"
135
+ raise NotImplementedError, "Unknown opera_system: #{opera_system}"
103
136
  end
104
137
  end
105
138
 
139
+ # @return [Boolea]
106
140
  def adhoc?
107
- return false if platform == :macos # macOS no need adhoc
141
+ return false if opera_system == OperaSystem::MACOS # macOS no need adhoc
108
142
 
109
143
  !development? && !devices.nil?
110
144
  end
111
145
 
146
+ # @return [Boolea]
112
147
  def enterprise?
113
- return false if platform == :macos # macOS no need adhoc
148
+ return false if opera_system == OperaSystem::MACOS # macOS no need adhoc
114
149
 
115
150
  !development? && !adhoc? && !appstore?
116
151
  end
@@ -118,7 +153,8 @@ module AppInfo
118
153
 
119
154
  # Enabled Capabilites
120
155
  #
121
- # Related link: https://developer.apple.com/support/app-capabilities/
156
+ # @see https://developer.apple.com/support/app-capabilities/
157
+ # @return [Array<String>]
122
158
  def enabled_capabilities
123
159
  capabilities = []
124
160
  capabilities << 'In-App Purchase' << 'GameKit' if adhoc? || appstore?
@@ -142,10 +178,10 @@ module AppInfo
142
178
  when 'com.apple.developer.networking.vpn.api'
143
179
  capabilities << 'Personal VPN'
144
180
  when 'com.apple.developer.healthkit',
145
- 'com.apple.developer.healthkit.access'
181
+ 'com.apple.developer.healthkit.access'
146
182
  capabilities << 'HealthKit' unless capabilities.include?('HealthKit')
147
183
  when 'com.apple.developer.icloud-services',
148
- 'com.apple.developer.icloud-container-identifiers'
184
+ 'com.apple.developer.icloud-container-identifiers'
149
185
  capabilities << 'iCloud' unless capabilities.include?('iCloud')
150
186
  when 'com.apple.developer.in-app-payments'
151
187
  capabilities << 'Apple Pay'
@@ -192,18 +228,21 @@ module AppInfo
192
228
  capabilities
193
229
  end
194
230
 
231
+ # @return [String, nil]
195
232
  def [](key)
196
233
  mobileprovision.try(:[], key.to_s)
197
234
  end
198
235
 
236
+ # @return [Boolea]
199
237
  def empty?
200
238
  mobileprovision.nil?
201
239
  end
202
240
 
241
+ # @return [CFPropertyList]
203
242
  def mobileprovision
204
- return @mobileprovision = nil unless File.exist?(@path)
243
+ return @mobileprovision = nil unless ::File.exist?(@file)
205
244
 
206
- data = File.read(@path)
245
+ data = ::File.read(@file)
207
246
  data = strip_plist_wrapper(data) unless bplist?(data)
208
247
  list = CFPropertyList::List.new(data: data).value
209
248
  @mobileprovision = CFPropertyList.native_types(list)
@@ -235,26 +274,5 @@ module AppInfo
235
274
  end_point = raw.index(end_tag) + end_tag.size - 1
236
275
  raw[start_point..end_point]
237
276
  end
238
-
239
- # Developer Certificate
240
- class DeveloperCertificate
241
- attr_reader :raw
242
-
243
- def initialize(data)
244
- @raw = OpenSSL::X509::Certificate.new(data)
245
- end
246
-
247
- def name
248
- @raw.subject.to_a.find { |name, _, _| name == 'CN' }[1].force_encoding('UTF-8')
249
- end
250
-
251
- def created_date
252
- @raw.not_after
253
- end
254
-
255
- def expired_date
256
- @raw.not_before
257
- end
258
- end
259
277
  end
260
278
  end
@@ -0,0 +1,260 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pedump'
4
+ require 'fileutils'
5
+ require 'forwardable'
6
+
7
+ module AppInfo
8
+ # Windows PE parser
9
+ #
10
+ # @see https://learn.microsoft.com/zh-cn/windows/win32/debug/pe-format Microsoft PE Format
11
+ class PE < File
12
+ include Helper::HumanFileSize
13
+ include Helper::Archive
14
+ extend Forwardable
15
+
16
+ ARCH = {
17
+ 0x014c => 'x86',
18
+ 0x0200 => 'Intel Itanium',
19
+ 0x8664 => 'x64',
20
+ 0x1c0 => 'arm',
21
+ 0xaa64 => 'arm64',
22
+ 0x5032 => 'RISC-v 32',
23
+ 0x5064 => 'RISC-v 64',
24
+ 0x5128 => 'RISC-v 128'
25
+ }.freeze
26
+
27
+ # @return [Symbol] {Platform}
28
+ def platform
29
+ Platform::WINDOWS
30
+ end
31
+
32
+ # @return [Symbol] {OperaSystem}
33
+ def opera_system
34
+ OperaSystem::WINDOWS
35
+ end
36
+
37
+ # @return [Symbol] {Device}
38
+ def device
39
+ Device::WINDOWS
40
+ end
41
+
42
+ # return file size
43
+ # @example Read file size in integer
44
+ # aab.size # => 3618865
45
+ #
46
+ # @example Read file size in human readabale
47
+ # aab.size(human_size: true) # => '3.45 MB'
48
+ #
49
+ # @param [Boolean] human_size Convert integer value to human readable.
50
+ # @return [Integer, String]
51
+ def size(human_size: false)
52
+ file_to_human_size(@file, human_size: human_size)
53
+ end
54
+
55
+ def binary_size(human_size: false)
56
+ file_to_human_size(binary_file, human_size: human_size)
57
+ end
58
+
59
+ # @!method product_name
60
+ # @see VersionInfo#product_name
61
+ # @return [String]
62
+ # @!method product_version
63
+ # @see VersionInfo#product_version
64
+ # @return [String]
65
+ # @!method company_name
66
+ # @see VersionInfo#company_name
67
+ # @return [String]
68
+ # @!method assembly_version
69
+ # @see VersionInfo#assembly_version
70
+ # @return [String]
71
+ def_delegators :version_info, :product_name, :product_version, :company_name, :assembly_version
72
+
73
+ alias name product_name
74
+ alias release_version product_version
75
+ alias build_version assembly_version
76
+
77
+ # @return [String]
78
+ def archs
79
+ ARCH[image_file_header.Machine] || 'unknown'
80
+ end
81
+ alias architectures archs
82
+
83
+ # @return [Hash{String => String}] imports imports of libraries
84
+ def imports
85
+ @imports ||= pe.imports.each_with_object({}) do |import, obj|
86
+ obj[import.module_name] = import.first_thunk.map(&:name).compact
87
+ end
88
+ end
89
+
90
+ # @return [Array{String}] icons paths of bmp image icons
91
+ def icons
92
+ @icons ||= lambda {
93
+ # Fetch the largest size icon
94
+ files = []
95
+ pe.resources&.find_all do |res|
96
+ next unless res.type == 'ICON'
97
+
98
+ filename = "#{::File.basename(file, '.*')}-#{res.type}-#{res.id}.bmp"
99
+ icon_file = tempdir(filename, prefix: 'pe', system: true)
100
+ mask_icon_file = icon_file.sub('.bmp', '.mask.bmp')
101
+
102
+ begin
103
+ ::File.open(icon_file, 'wb') do |f|
104
+ f << res.restore_bitmap(io)
105
+ end
106
+
107
+ if res.bitmap_mask(io)
108
+ mask_icon_file = icon_file.sub('.bmp', '.mask.bmp')
109
+ ::File.open(mask_icon_file, 'wb') do |f|
110
+ f << res.bitmap_mask(io)
111
+ end
112
+ end
113
+ rescue StandardError => e
114
+ # ignore pedump throws any exception.
115
+ raise e unless e.backtrace.first.include?('pedump')
116
+
117
+ FileUtils.rm_f(icon_file)
118
+ ensure
119
+ next unless ::File.exist?(icon_file)
120
+
121
+ mask_file = ::File.exist?(mask_icon_file) ? mask_icon_file : nil
122
+ files << icon_metadata(icon_file, mask_file: mask_file)
123
+ end
124
+ end
125
+
126
+ files
127
+ }.call
128
+ end
129
+
130
+ # @return [PEdump]
131
+ def pe
132
+ @pe ||= lambda {
133
+ pe = PEdump.new(io)
134
+ pe.logger.level = Logger::FATAL # ignore :warn logger output
135
+ pe
136
+ }.call
137
+ end
138
+
139
+ # @return [VersionInfo]
140
+ def version_info
141
+ @version_info ||= VersionInfo.new(pe.version_info)
142
+ end
143
+
144
+ def clear!
145
+ @io = nil
146
+ @pe = nil
147
+ @icons = nil
148
+ @imports = nil
149
+ end
150
+
151
+ # @return [String] binary_file path
152
+ def binary_file
153
+ @binary_file ||= lambda {
154
+ file_io = ::File.open(@file, 'rb')
155
+ return @file unless file_io.read(100) =~ AppInfo::ZIP_RETGEX
156
+
157
+ zip_file = Zip::File.open(@file)
158
+ zip_entry = zip_file.glob('*.exe').first
159
+ raise NotFoundError, 'Not found .exe file in archive file' if zip_entry.nil?
160
+
161
+ exe_file = tempdir(zip_entry.name, prefix: 'pe-exe', system: true)
162
+ zip_entry.extract(exe_file)
163
+ zip_file.close
164
+
165
+ return exe_file
166
+ }.call
167
+ end
168
+
169
+ private
170
+
171
+ def image_file_header
172
+ @image_file_header ||= pe.pe.image_file_header
173
+ end
174
+
175
+ # @return [Hash{Symbol => String}]
176
+ def icon_metadata(file, mask_file: nil)
177
+ {
178
+ name: ::File.basename(file),
179
+ file: file,
180
+ mask: mask_file,
181
+ dimensions: ImageSize.path(file).size
182
+ }
183
+ end
184
+
185
+ # @return [File]
186
+ def io
187
+ @io ||= ::File.open(binary_file, 'rb')
188
+ end
189
+
190
+ # VersionInfo class
191
+ #
192
+ # Ref: https://learn.microsoft.com/zh-cn/windows/win32/menurc/versioninfo-resource
193
+ class VersionInfo
194
+ def initialize(raw)
195
+ @raw = raw
196
+ end
197
+
198
+ # @return [String]
199
+ def company_name
200
+ @company_name ||= value_of('CompanyName')
201
+ end
202
+
203
+ # @return [String]
204
+ def product_name
205
+ @product_name ||= value_of('ProductName')
206
+ end
207
+
208
+ # @return [String]
209
+ def product_version
210
+ @product_version ||= value_of('ProductVersion')
211
+ end
212
+
213
+ # @return [String]
214
+ def assembly_version
215
+ @assembly_version ||= value_of('Assembly Version')
216
+ end
217
+
218
+ # @return [String]
219
+ def file_version
220
+ @file_version ||= value_of('FileVersion')
221
+ end
222
+
223
+ # @return [String]
224
+ def file_description
225
+ @file_description ||= value_of('FileDescription')
226
+ end
227
+
228
+ # @return [String]
229
+ def copyright
230
+ @copyright ||= value_of('LegalCopyright')
231
+ end
232
+
233
+ private
234
+
235
+ def value_of(key)
236
+ info.each do |v|
237
+ return v[:Value] if v[:szKey] == key.to_s
238
+ end
239
+
240
+ nil
241
+ end
242
+
243
+ def info
244
+ return @info if @info
245
+
246
+ @raw.each do |item|
247
+ next unless item.is_a?(PEdump::VS_VERSIONINFO)
248
+
249
+ versions = item[:Children].select { |v| v.is_a?(PEdump::StringFileInfo) }
250
+ next if versions.empty?
251
+
252
+ @info = versions[0][:Children][0][:Children]
253
+ return @info
254
+ end
255
+
256
+ @info = []
257
+ end
258
+ end
259
+ end
260
+ end
@@ -7,9 +7,10 @@ require 'zlib'
7
7
  require 'stringio'
8
8
 
9
9
  module AppInfo
10
+ # Decompress iOS Png image file.
11
+ # @see https://github.com/swcai/iphone-png-normalizer swcai/iphone-png-normalizer
12
+ # @author Wenwei Cai
10
13
  class PngUncrush
11
- class Error < StandardError; end
12
-
13
14
  class FormatError < Error; end
14
15
 
15
16
  class PngReader # :nodoc:
@@ -31,18 +32,24 @@ module AppInfo
31
32
  @data = String.new
32
33
  end
33
34
 
35
+ # @return [Integer]
34
36
  def size
35
37
  @io.size
36
38
  end
37
39
 
40
+ # @param [String] format
41
+ # @return [String]
42
+ # @see IO package data
38
43
  def unpack(format)
39
44
  @io.unpack(format)
40
45
  end
41
46
 
47
+ # @return [String]
42
48
  def header
43
49
  @header ||= self[0, 8]
44
50
  end
45
51
 
52
+ # @return [Boolean]
46
53
  def png?
47
54
  header.bytes == PNG_HEADER
48
55
  end
@@ -60,23 +67,36 @@ module AppInfo
60
67
  end
61
68
  end
62
69
 
70
+ # Decompress crushed png file.
71
+ # @param [String] input path of png file
72
+ # @param [String] output path of output file
73
+ # @return [Boolean] result whether decompress successfully
63
74
  def self.decompress(input, output)
64
75
  new(input).decompress(output)
65
76
  end
66
77
 
78
+ # get png dimensions
79
+ # @param [String] input path of png file
80
+ # @return [Array<Integer>] dimensions width, height value of png file
67
81
  def self.dimensions(input)
68
82
  new(input).dimensions
69
83
  end
70
84
 
71
85
  def initialize(filename)
72
- @io = PngReader.new(File.open(filename))
86
+ @io = PngReader.new(::File.open(filename))
73
87
  raise FormatError, 'not a png file' unless @io.png?
74
88
  end
75
89
 
90
+ # get png dimensions
91
+ # @param [String] input path of png file
92
+ # @return [Array<Integer>] dimensions width, height value of png file
76
93
  def dimensions
77
94
  _dump_sections(dimensions: true)
78
95
  end
79
96
 
97
+ # Decompress crushed png file.
98
+ # @param [String] output path of output file
99
+ # @return [Boolean] result whether decompress successfully
80
100
  def decompress(output)
81
101
  content = _remap(_dump_sections)
82
102
  return false unless content
@@ -125,7 +145,7 @@ module AppInfo
125
145
  end
126
146
 
127
147
  def write_file(path, content)
128
- File.write(path, content, encoding: Encoding::BINARY)
148
+ ::File.write(path, content, encoding: Encoding::BINARY)
129
149
  true
130
150
  end
131
151
 
@@ -5,52 +5,59 @@ require 'rexml/document'
5
5
 
6
6
  module AppInfo
7
7
  # Proguard parser
8
- class Proguard
8
+ class Proguard < File
9
9
  include Helper::Archive
10
10
 
11
11
  NAMESPACE = UUIDTools::UUID.sha1_create(UUIDTools::UUID_DNS_NAMESPACE, 'icyleaf.com')
12
12
 
13
- attr_reader :file
14
-
15
- def initialize(file)
16
- @file = file
13
+ # @return [Symbol] {Platform}
14
+ def platform
15
+ Platform::GOOGLE
17
16
  end
18
17
 
19
- def file_type
20
- Platform::PROGUARD
18
+ # @return [Symbol] {OperaSystem}
19
+ def opera_system
20
+ OperaSystem::ANDROID
21
21
  end
22
22
 
23
+ # @return [String]
23
24
  def uuid
24
25
  # Similar to https://docs.sentry.io/workflow/debug-files/#proguard-uuids
25
- UUIDTools::UUID.sha1_create(NAMESPACE, File.read(mapping_path)).to_s
26
+ UUIDTools::UUID.sha1_create(NAMESPACE, ::File.read(mapping_path)).to_s
26
27
  end
27
28
  alias debug_id uuid
28
29
 
30
+ # @return [Boolean]
29
31
  def mapping?
30
- File.exist?(mapping_path)
32
+ ::File.exist?(mapping_path)
31
33
  end
32
34
 
35
+ # @return [Boolean]
33
36
  def manifest?
34
- File.exist?(manifest_path)
37
+ ::File.exist?(manifest_path)
35
38
  end
36
39
 
40
+ # @return [Boolean]
37
41
  def symbol?
38
- File.exist?(symbol_path)
42
+ ::File.exist?(symbol_path)
39
43
  end
40
44
  alias resource? symbol?
41
45
 
46
+ # @return [String, nil]
42
47
  def package_name
43
48
  return unless manifest?
44
49
 
45
50
  manifest.root.attributes['package']
46
51
  end
47
52
 
53
+ # @return [String, nil]
48
54
  def releasd_version
49
55
  return unless manifest?
50
56
 
51
57
  manifest.root.attributes['package']
52
58
  end
53
59
 
60
+ # @return [String, nil]
54
61
  def version_name
55
62
  return unless manifest?
56
63
 
@@ -58,6 +65,7 @@ module AppInfo
58
65
  end
59
66
  alias release_version version_name
60
67
 
68
+ # @return [String, nil]
61
69
  def version_code
62
70
  return unless manifest?
63
71
 
@@ -65,27 +73,32 @@ module AppInfo
65
73
  end
66
74
  alias build_version version_code
67
75
 
76
+ # @return [REXML::Document]
68
77
  def manifest
69
78
  return unless manifest?
70
79
 
71
- @manifest ||= REXML::Document.new(File.new(manifest_path))
80
+ @manifest ||= REXML::Document.new(::File.new(manifest_path))
72
81
  end
73
82
 
83
+ # @return [String]
74
84
  def mapping_path
75
- @mapping_path ||= Dir.glob(File.join(contents, '*mapping*.txt')).first
85
+ @mapping_path ||= Dir.glob(::File.join(contents, '*mapping*.txt')).first
76
86
  end
77
87
 
88
+ # @return [String]
78
89
  def manifest_path
79
- @manifest_path ||= File.join(contents, 'AndroidManifest.xml')
90
+ @manifest_path ||= ::File.join(contents, 'AndroidManifest.xml')
80
91
  end
81
92
 
93
+ # @return [String]
82
94
  def symbol_path
83
- @symbol_path ||= File.join(contents, 'R.txt')
95
+ @symbol_path ||= ::File.join(contents, 'R.txt')
84
96
  end
85
97
  alias resource_path symbol_path
86
98
 
99
+ # @return [String] contents path of contents
87
100
  def contents
88
- @contents ||= unarchive(@file, path: 'proguard')
101
+ @contents ||= unarchive(@file, prefix: 'proguard')
89
102
  end
90
103
 
91
104
  def clear!