app-info 2.0.0.beta3 → 2.0.0.beta4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AppInfo
4
+ # AppInfo Util
5
+ module Util
6
+ FILE_SIZE_UNITS = %w[B KB MB GB TB].freeze
7
+
8
+ def self.file_size(file, humanable)
9
+ file_size = File.size(file)
10
+ humanable ? size_to_humanable(file_size) : file_size
11
+ end
12
+
13
+ def self.size_to_humanable(number)
14
+ if number.to_i < 1024
15
+ exponent = 0
16
+ else
17
+ max_exp = FILE_SIZE_UNITS.size - 1
18
+ exponent = (Math.log(number) / Math.log(1024)).to_i
19
+ exponent = max_exp if exponent > max_exp
20
+ number = format('%<number>.2f', number: (number / (1024**exponent.to_f)))
21
+ end
22
+
23
+ "#{number} #{FILE_SIZE_UNITS[exponent]}"
24
+ end
25
+ end
26
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AppInfo
4
- VERSION = '2.0.0.beta3'
4
+ VERSION = '2.0.0.beta4'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: app-info
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0.beta3
4
+ version: 2.0.0.beta4
5
5
  platform: ruby
6
6
  authors:
7
7
  - icyleaf
@@ -174,13 +174,13 @@ files:
174
174
  - app_info.gemspec
175
175
  - lib/app-info.rb
176
176
  - lib/app_info.rb
177
+ - lib/app_info/apk.rb
177
178
  - lib/app_info/core_ext/object/try.rb
178
- - lib/app_info/parser.rb
179
- - lib/app_info/parser/apk.rb
180
- - lib/app_info/parser/dsym.rb
181
- - lib/app_info/parser/ipa.rb
182
- - lib/app_info/parser/ipa/info_plist.rb
183
- - lib/app_info/parser/ipa/mobile_provision.rb
179
+ - lib/app_info/dsym.rb
180
+ - lib/app_info/ipa.rb
181
+ - lib/app_info/ipa/info_plist.rb
182
+ - lib/app_info/ipa/mobile_provision.rb
183
+ - lib/app_info/util.rb
184
184
  - lib/app_info/version.rb
185
185
  homepage: http://github.com/icyleaf/app-info
186
186
  licenses:
@@ -1,40 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'app_info/parser/ipa'
4
- require 'app_info/parser/ipa/info_plist'
5
- require 'app_info/parser/ipa/mobile_provision'
6
- require 'app_info/parser/apk'
7
- require 'app_info/parser/dsym'
8
-
9
- module AppInfo
10
- module Parser
11
- # App Platform
12
- module Platform
13
- IOS = 'iOS'
14
- ANDROID = 'Android'
15
- DSYM = 'dSYM'
16
- end
17
-
18
- module Util
19
- FILE_SIZE_UNITS = %w[B KB MB GB TB].freeze
20
-
21
- def self.file_size(file, humanable)
22
- file_size = File.size(file)
23
- humanable ? size_to_humanable(file_size) : file_size
24
- end
25
-
26
- def self.size_to_humanable(number)
27
- if number.to_i < 1024
28
- exponent = 0
29
- else
30
- max_exp = FILE_SIZE_UNITS.size - 1
31
- exponent = (Math.log(number) / Math.log(1024)).to_i
32
- exponent = max_exp if exponent > max_exp
33
- number = format('%.2f', (number / (1024**exponent.to_f)))
34
- end
35
-
36
- "#{number} #{FILE_SIZE_UNITS[exponent]}"
37
- end
38
- end
39
- end
40
- end
@@ -1,183 +0,0 @@
1
- require 'ruby_apk'
2
- require 'image_size'
3
-
4
- module AppInfo
5
- module Parser
6
- # Parse APK file
7
- class APK
8
- attr_reader :file, :apk
9
-
10
- # APK Devices
11
- module Device
12
- PHONE = 'Phone'.freeze
13
- TABLET = 'Tablet'.freeze
14
- WATCH = 'Watch'.freeze
15
- TV = 'Television'.freeze
16
- end
17
-
18
- def initialize(file)
19
- @file = file
20
-
21
- Zip.warn_invalid_date = false # fix invaild date format warnings
22
- @apk = ::Android::Apk.new(file)
23
- end
24
-
25
- def size(humanable = false)
26
- AppInfo::Parser::Util.file_size(@file, humanable)
27
- end
28
-
29
- def os
30
- Parser::Platform::ANDROID
31
- end
32
- alias file_type os
33
-
34
- def build_version
35
- manifest.version_code.to_s
36
- end
37
-
38
- def release_version
39
- manifest.version_name
40
- end
41
-
42
- def name
43
- resource.find('@string/app_name')
44
- end
45
-
46
- def bundle_id
47
- manifest.package_name
48
- end
49
- alias identifier bundle_id
50
- alias package_name bundle_id
51
-
52
- def device_type
53
- if wear?
54
- Device::WATCH
55
- elsif tv?
56
- Device::TV
57
- else
58
- Device::PHONE
59
- end
60
- end
61
-
62
- # TODO: find a way to detect
63
- # def tablet?
64
- # resource
65
- # end
66
-
67
- def wear?
68
- use_features.include?('android.hardware.type.watch')
69
- end
70
-
71
- def tv?
72
- use_features.include?('android.software.leanback')
73
- end
74
-
75
- def min_sdk_version
76
- manifest.min_sdk_ver
77
- end
78
-
79
- def target_sdk_version
80
- manifest.doc
81
- .elements['/manifest/uses-sdk']
82
- .attributes['targetSdkVersion']
83
- .to_i
84
- end
85
-
86
- def use_permissions
87
- manifest.use_permissions
88
- end
89
-
90
- def use_features
91
- manifest_values('/manifest/uses-feature')
92
- end
93
-
94
- def signs
95
- @apk.signs.each_with_object([]) do |(path, sign), obj|
96
- obj << Sign.new(path, sign)
97
- end
98
- end
99
-
100
- def certificates
101
- @apk.certificates.each_with_object([]) do |(path, certificate), obj|
102
- obj << Certificate.new(path, certificate)
103
- end
104
- end
105
-
106
- def activities
107
- components.select { |c| c.type == 'activity' }
108
- end
109
-
110
- def services
111
- components.select { |c| c.type == 'service' }
112
- end
113
-
114
- def components
115
- manifest.components
116
- end
117
-
118
- def manifest
119
- @apk.manifest
120
- end
121
-
122
- def resource
123
- @apk.resource
124
- end
125
-
126
- def dex
127
- @apk.dex
128
- end
129
-
130
- def icons
131
- unless @icons
132
- tmp_path = File.join(Dir.mktmpdir, "AppInfo-android-#{SecureRandom.hex}")
133
-
134
- @icons = @apk.icon.each_with_object([]) do |(path, data), obj|
135
- icon_name = File.basename(path)
136
- icon_path = File.join(tmp_path, File.path(path))
137
- icon_file = File.join(icon_path, icon_name)
138
- FileUtils.mkdir_p icon_path
139
- File.open(icon_file, 'w') do |f|
140
- f.write data
141
- end
142
-
143
- obj << {
144
- name: icon_name,
145
- file: icon_file,
146
- dimensions: ImageSize.path(icon_file).size
147
- }
148
- end
149
- end
150
-
151
- @icons
152
- end
153
-
154
- private
155
-
156
- def manifest_values(path, key = 'name')
157
- values = []
158
- manifest.doc.each_element(path) do |elem|
159
- values << elem.attributes[key]
160
- end
161
- values.uniq
162
- end
163
-
164
- # Android Certificate
165
- class Certificate
166
- attr_reader :path, :certificate
167
- def initialize(path, certificate)
168
- @path = path
169
- @certificate = certificate
170
- end
171
- end
172
-
173
- # Android Sign
174
- class Sign
175
- attr_reader :path, :sign
176
- def initialize(path, sign)
177
- @path = path
178
- @sign = sign
179
- end
180
- end
181
- end
182
- end
183
- end
@@ -1,126 +0,0 @@
1
- require 'zip'
2
- require 'macho'
3
- require 'app_info/core_ext/object/try'
4
-
5
- module AppInfo
6
- module Parser
7
- # DSYM parser
8
- class DSYM
9
- attr_reader :file
10
-
11
- def initialize(file)
12
- @file = file
13
- end
14
-
15
- def file_type
16
- Parser::Platform::DSYM
17
- end
18
-
19
- def machos
20
- @machos ||= case macho_type
21
- when ::MachO::MachOFile
22
- [MachO.new(macho_type, File.size(app_path))]
23
- else
24
- size = macho_type.fat_archs.each_with_object([]) do |arch, obj|
25
- obj << arch.size
26
- end
27
-
28
- machos = []
29
- macho_type.machos.each_with_index do |file, i|
30
- machos << MachO.new(file, size[i])
31
- end
32
- machos
33
- end
34
- end
35
-
36
- def macho_type
37
- @macho_type ||= ::MachO.open(app_path)
38
- end
39
-
40
- def object
41
- @object ||= File.basename(app_path)
42
- end
43
-
44
- def app_path
45
- unless @app_path
46
- path = File.join(contents, 'Contents', 'Resources', 'DWARF')
47
- name = Dir.entries(path).last
48
- @app_path = File.join(path, name)
49
- end
50
-
51
- @app_path
52
- end
53
-
54
- private
55
-
56
- def contents
57
- unless @contents
58
- if File.directory?(@file)
59
- @contents = @file
60
- else
61
- @contents = "#{Dir.mktmpdir}/AppInfo-dsym-#{SecureRandom.hex}"
62
- dsym_dir = nil
63
- Zip::File.open(@file) do |zip_file|
64
- zip_file.each do |f|
65
- dsym_dir ||= f.name
66
-
67
- f_path = File.join(@contents, f.name)
68
- zip_file.extract(f, f_path) unless File.exist?(f_path)
69
- end
70
- end
71
-
72
- @contents = File.join(@contents, dsym_dir)
73
- end
74
- end
75
-
76
- @contents
77
- end
78
-
79
- # DSYM Mach-O
80
- class MachO
81
- def initialize(file, size = 0)
82
- @file = file
83
- @size = size
84
- end
85
-
86
- def cpu_name
87
- @file.cpusubtype
88
- end
89
-
90
- def cpu_type
91
- @file.cputype
92
- end
93
-
94
- def type
95
- @file.filetype
96
- end
97
-
98
- def size(humanable = false)
99
- return Util.size_to_humanable(@size) if humanable
100
-
101
- @size
102
- end
103
-
104
- def uuid
105
- @file[:LC_UUID][0].uuid_string
106
- end
107
- alias debug_id uuid
108
-
109
- def header
110
- @header ||= @file.header
111
- end
112
-
113
- def to_h
114
- {
115
- uuid: uuid,
116
- type: type,
117
- cpu_name: cpu_name,
118
- cpu_type: cpu_type,
119
- size: size,
120
- humanable_size: size(true)
121
- }
122
- end
123
- end
124
- end
125
- end
126
- end
@@ -1,229 +0,0 @@
1
- require 'zip'
2
- require 'pngdefry'
3
- require 'fileutils'
4
- require 'securerandom'
5
- require 'cfpropertylist'
6
- require 'app_info/core_ext/object/try'
7
-
8
- module AppInfo
9
- module Parser
10
- # IPA parser
11
- class IPA
12
- attr_reader :file
13
-
14
- # iOS Export types
15
- module ExportType
16
- DEBUG = 'Debug'.freeze
17
- ADHOC = 'AdHoc'.freeze
18
- INHOUSE = 'inHouse'.freeze
19
- RELEASE = 'Release'.freeze
20
- UNKOWN = nil
21
- end
22
-
23
- def initialize(file)
24
- @file = file
25
- end
26
-
27
- def size(humanable = false)
28
- AppInfo::Parser::Util.file_size(@file, humanable)
29
- end
30
-
31
- def os
32
- Parser::Platform::IOS
33
- end
34
- alias file_type os
35
-
36
- def iphone?
37
- info.iphone?
38
- end
39
-
40
- def ipad?
41
- info.ipad?
42
- end
43
-
44
- def universal?
45
- info.universal?
46
- end
47
-
48
- def build_version
49
- info.build_version
50
- end
51
-
52
- def release_version
53
- info.release_version
54
- end
55
-
56
- def identifier
57
- info.identifier
58
- end
59
-
60
- def name
61
- display_name || bundle_name
62
- end
63
-
64
- def display_name
65
- info.display_name
66
- end
67
-
68
- def bundle_name
69
- info.bundle_name
70
- end
71
-
72
- def icons
73
- info.icons
74
- end
75
-
76
- #
77
- # Return the minimum OS version for the given application
78
- #
79
- def min_sdk_version
80
- info.min_sdk_version
81
- end
82
-
83
- def device_type
84
- info.device_type
85
- end
86
-
87
- def devices
88
- mobileprovision.devices
89
- end
90
-
91
- def team_name
92
- mobileprovision.team_name
93
- end
94
-
95
- def team_identifier
96
- mobileprovision.team_identifier
97
- end
98
-
99
- def profile_name
100
- mobileprovision.profile_name
101
- end
102
-
103
- def expired_date
104
- mobileprovision.expired_date
105
- end
106
-
107
- def distribution_name
108
- "#{profile_name} - #{team_name}" if profile_name && team_name
109
- end
110
-
111
- def release_type
112
- if stored?
113
- ExportType::RELEASE
114
- else
115
- build_type
116
- end
117
- end
118
-
119
- def build_type
120
- if mobileprovision?
121
- if devices
122
- ExportType::ADHOC
123
- else
124
- ExportType::INHOUSE
125
- end
126
- else
127
- ExportType::DEBUG
128
- end
129
- end
130
-
131
- def stored?
132
- metadata? ? true : false
133
- end
134
-
135
- def hide_developer_certificates
136
- mobileprovision.delete('DeveloperCertificates') if mobileprovision?
137
- end
138
-
139
- def mobileprovision
140
- return unless mobileprovision?
141
- return @mobileprovision if @mobileprovision
142
-
143
- @mobileprovision = MobileProvision.new(mobileprovision_path)
144
- end
145
-
146
- def mobileprovision?
147
- File.exist?mobileprovision_path
148
- end
149
-
150
- def mobileprovision_path
151
- filename = 'embedded.mobileprovision'
152
- @mobileprovision_path ||= File.join(@file, filename)
153
- unless File.exist?(@mobileprovision_path)
154
- @mobileprovision_path = File.join(app_path, filename)
155
- end
156
-
157
- @mobileprovision_path
158
- end
159
-
160
- def metadata
161
- return unless metadata?
162
-
163
- @metadata ||= CFPropertyList.native_types(CFPropertyList::List.new(file: metadata_path).value)
164
- end
165
-
166
- def metadata?
167
- File.exist?(metadata_path)
168
- end
169
-
170
- def metadata_path
171
- @metadata_path ||= File.join(contents, 'iTunesMetadata.plist')
172
- end
173
-
174
- def info
175
- @info ||= InfoPlist.new(app_path)
176
- end
177
-
178
- def app_path
179
- @app_path ||= Dir.glob(File.join(contents, 'Payload', '*.app')).first
180
- end
181
-
182
- def cleanup!
183
- return unless @contents
184
-
185
- FileUtils.rm_rf(@contents)
186
-
187
- @contents = nil
188
- @icons = nil
189
- @app_path = nil
190
- @metadata = nil
191
- @metadata_path = nil
192
- @info = nil
193
- end
194
-
195
- alias bundle_id identifier
196
-
197
- private
198
-
199
- def contents
200
- # source: https://github.com/soffes/lagunitas/blob/master/lib/lagunitas/ipa.rb
201
- unless @contents
202
- @contents = "#{Dir.mktmpdir}/AppInfo-ios-#{SecureRandom.hex}"
203
- Zip::File.open(@file) do |zip_file|
204
- zip_file.each do |f|
205
- f_path = File.join(@contents, f.name)
206
- zip_file.extract(f, f_path) unless File.exist?(f_path)
207
- end
208
- end
209
- end
210
-
211
- @contents
212
- end
213
-
214
- def icons_root_path
215
- iphone = 'CFBundleIcons'.freeze
216
- ipad = 'CFBundleIcons~ipad'.freeze
217
-
218
- case device_type
219
- when 'iPhone'
220
- [iphone]
221
- when 'iPad'
222
- [ipad]
223
- when 'Universal'
224
- [iphone, ipad]
225
- end
226
- end
227
- end
228
- end
229
- end