app-info 2.6.5 → 2.7.0.beta1
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/.rubocop.yml +19 -2
- data/CHANGELOG.md +8 -1
- data/README.md +8 -5
- data/app_info.gemspec +1 -0
- data/lib/app_info/aab.rb +220 -0
- data/lib/app_info/apk.rb +3 -2
- data/lib/app_info/core_ext/string/inflector.rb +35 -0
- data/lib/app_info/core_ext.rb +4 -0
- data/lib/app_info/dsym.rb +7 -3
- data/lib/app_info/error.rb +13 -0
- data/lib/app_info/helper.rb +128 -0
- data/lib/app_info/info_plist.rb +6 -6
- data/lib/app_info/ipa.rb +6 -4
- data/lib/app_info/macos.rb +6 -4
- data/lib/app_info/mobile_provision.rb +2 -2
- data/lib/app_info/png_uncrush.rb +1 -1
- data/lib/app_info/proguard.rb +4 -2
- data/lib/app_info/protobuf/manifest.rb +144 -0
- data/lib/app_info/protobuf/models/Configuration.proto +206 -0
- data/lib/app_info/protobuf/models/Configuration_pb.rb +139 -0
- data/lib/app_info/protobuf/models/README.md +19 -0
- data/lib/app_info/protobuf/models/Resources.proto +588 -0
- data/lib/app_info/protobuf/models/Resources_pb.rb +344 -0
- data/lib/app_info/protobuf/resources.rb +229 -0
- data/lib/app_info/version.rb +1 -1
- data/lib/app_info.rb +69 -56
- metadata +30 -5
- data/lib/app_info/util.rb +0 -98
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b37b084ef666db1fad604e2aa19698faad80d68d7d79a031c55d60e3f8b97f48
|
4
|
+
data.tar.gz: 2a8da24c6adc6b9f003d8a8016569f2f20772f1c7524ea411a6b4946f32fd0a7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9041485898807902ce753c4114b82037cf900c45fc4ce183fb2af0ec57c0fd05ebe96314a89016e3f423531daa528d48d6ea566445296be045d18dc6ab4da4cb
|
7
|
+
data.tar.gz: fd22abedeea11ba62dd7f4575f19a08348d146cc6ffd3a6d69b54652a869b9dc095467d9922031a84d79336fbbc4e3dc485a428e8a96d66fc4bedc378d1e962f
|
data/.rubocop.yml
CHANGED
@@ -20,10 +20,12 @@ AllCops:
|
|
20
20
|
Exclude:
|
21
21
|
- 'bin/*'
|
22
22
|
- 'spec/**/*'
|
23
|
-
- vendor/bundle/**/*
|
23
|
+
- 'vendor/bundle/**/*'
|
24
24
|
- 'Rakefile'
|
25
25
|
- 'app_info.gemspec'
|
26
26
|
- 'lib/app-info.rb'
|
27
|
+
- 'lib/app_info/protobuf/models/*_pb.rb'
|
28
|
+
- 'main.rb'
|
27
29
|
|
28
30
|
Metrics/AbcSize:
|
29
31
|
Max: 100
|
@@ -56,4 +58,19 @@ Lint/AssignmentInCondition:
|
|
56
58
|
Enabled: false
|
57
59
|
|
58
60
|
Style/Documentation:
|
59
|
-
Enabled: false
|
61
|
+
Enabled: false
|
62
|
+
|
63
|
+
Style/PerlBackrefs:
|
64
|
+
Exclude:
|
65
|
+
- 'lib/app_info/core_ext/string/inflector.rb'
|
66
|
+
|
67
|
+
Style/DocumentDynamicEvalDefinition:
|
68
|
+
Enabled: false
|
69
|
+
|
70
|
+
Metrics/BlockNesting:
|
71
|
+
Exclude:
|
72
|
+
- 'lib/app_info/dsym.rb'
|
73
|
+
|
74
|
+
|
75
|
+
Style/SlicingWithRange:
|
76
|
+
Enabled: false
|
data/CHANGELOG.md
CHANGED
@@ -9,6 +9,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|
9
9
|
|
10
10
|
> List all changes before release a new version.
|
11
11
|
|
12
|
+
## [2.7.0.beta1] (2021-09-27)
|
13
|
+
|
14
|
+
### Added
|
15
|
+
|
16
|
+
- Android App Bundle a.k.a `aab` file parts support [#36](https://github.com/icyleaf/app_info/pull/36)
|
17
|
+
|
12
18
|
## [2.6.5] (2021-09-17)
|
13
19
|
|
14
20
|
### Added
|
@@ -185,7 +191,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|
185
191
|
|
186
192
|
- Updated dependency of CFPropertly list be a range between 2.3.4. (thanks @[cschroed](https://github.com/cschroed))
|
187
193
|
|
188
|
-
[Unreleased]: https://github.com/icyleaf/app-info/compare/v2.
|
194
|
+
[Unreleased]: https://github.com/icyleaf/app-info/compare/v2.7.0.beta1..HEAD
|
195
|
+
[2.7.0.beta1]: https://github.com/icyleaf/app-info/compare/v2.6.5...v2.7.0.beta1
|
189
196
|
[2.6.5]: https://github.com/icyleaf/app-info/compare/v2.6.4...v2.6.5
|
190
197
|
[2.6.4]: https://github.com/icyleaf/app-info/compare/v2.6.3...v2.6.4
|
191
198
|
[2.6.3]: https://github.com/icyleaf/app-info/compare/v2.6.1...v2.6.3
|
data/README.md
CHANGED
@@ -9,12 +9,14 @@ Teardown tool for mobile(ipa/apk) app, macOS app and dSYM.zip file, analysis met
|
|
9
9
|
|
10
10
|
## Support
|
11
11
|
|
12
|
-
- Android
|
12
|
+
- Android file
|
13
|
+
- `.apk`
|
14
|
+
- `.aab` (Androld App Bundle)
|
13
15
|
- iOS ipa file
|
14
|
-
- Info.plist file
|
15
|
-
-
|
16
|
-
- macOS App
|
17
|
-
-
|
16
|
+
- `Info.plist` file
|
17
|
+
- `.mobileprovision`/`.provisionprofile` file
|
18
|
+
- Zipped macOS App file
|
19
|
+
- Zipped dSYMs file
|
18
20
|
|
19
21
|
## Installation
|
20
22
|
|
@@ -54,6 +56,7 @@ parser = AppInfo.parse('App.dSYm.zip')
|
|
54
56
|
# If detect file type failed, you can parse in other way
|
55
57
|
parser = AppInfo::IPA.new('iphone.ipa')
|
56
58
|
parser = AppInfo::APK.new('android.apk')
|
59
|
+
parser = AppInfo::AAB.new('android.aab')
|
57
60
|
parser = AppInfo::InfoPlist.new('Info.plist')
|
58
61
|
parser = AppInfo::MobileProvision.new('uuid.mobileprovision')
|
59
62
|
parser = AppInfo::Macos.new('App.dSYm.zip')
|
data/app_info.gemspec
CHANGED
@@ -27,6 +27,7 @@ Gem::Specification.new do |spec|
|
|
27
27
|
spec.add_dependency 'rubyzip', '>= 1.2', '< 3.0'
|
28
28
|
spec.add_dependency 'uuidtools', '>= 2.1.5', '< 2.3.0'
|
29
29
|
spec.add_dependency 'icns', '~> 0.2.0'
|
30
|
+
spec.add_dependency 'google-protobuf', '~> 3.18.0'
|
30
31
|
|
31
32
|
spec.add_development_dependency 'bundler', '>= 1.12'
|
32
33
|
spec.add_development_dependency 'rake', '>= 10.0'
|
data/lib/app_info/aab.rb
ADDED
@@ -0,0 +1,220 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'app_info/protobuf/manifest'
|
4
|
+
require 'image_size'
|
5
|
+
require 'forwardable'
|
6
|
+
|
7
|
+
module AppInfo
|
8
|
+
# Parse APK file
|
9
|
+
class AAB
|
10
|
+
include Helper::HumanFileSize
|
11
|
+
extend Forwardable
|
12
|
+
|
13
|
+
attr_reader :file
|
14
|
+
|
15
|
+
# APK Devices
|
16
|
+
module Device
|
17
|
+
PHONE = 'Phone'
|
18
|
+
TABLET = 'Tablet'
|
19
|
+
WATCH = 'Watch'
|
20
|
+
TV = 'Television'
|
21
|
+
end
|
22
|
+
|
23
|
+
BASE_PATH = 'base'
|
24
|
+
BASE_MANIFEST = "#{BASE_PATH}/manifest/AndroidManifest.xml"
|
25
|
+
BASE_RESOURCES = "#{BASE_PATH}/resources.pb"
|
26
|
+
|
27
|
+
def initialize(file)
|
28
|
+
@file = file
|
29
|
+
end
|
30
|
+
|
31
|
+
def size(human_size: false)
|
32
|
+
file_to_human_size(@file, human_size: human_size)
|
33
|
+
end
|
34
|
+
|
35
|
+
def os
|
36
|
+
Platform::ANDROID
|
37
|
+
end
|
38
|
+
alias file_type os
|
39
|
+
|
40
|
+
def_delegators :manifest, :version_name
|
41
|
+
|
42
|
+
alias release_version version_name
|
43
|
+
|
44
|
+
def package_name
|
45
|
+
manifest.package
|
46
|
+
end
|
47
|
+
alias identifier package_name
|
48
|
+
alias bundle_id package_name
|
49
|
+
|
50
|
+
def version_code
|
51
|
+
manifest.version_code.to_s
|
52
|
+
end
|
53
|
+
alias build_version version_code
|
54
|
+
|
55
|
+
def name
|
56
|
+
manifest.label
|
57
|
+
end
|
58
|
+
|
59
|
+
def device_type
|
60
|
+
if wear?
|
61
|
+
Device::WATCH
|
62
|
+
elsif tv?
|
63
|
+
Device::TV
|
64
|
+
else
|
65
|
+
Device::PHONE
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# TODO: find a way to detect
|
70
|
+
# Found answer but not works: https://stackoverflow.com/questions/9279111/determine-if-the-device-is-a-smartphone-or-tablet
|
71
|
+
# def tablet?
|
72
|
+
# resource.first_package
|
73
|
+
# .entries('bool')
|
74
|
+
# .select{|e| e.name == 'isTablet' }
|
75
|
+
# .size >= 1
|
76
|
+
# end
|
77
|
+
|
78
|
+
def wear?
|
79
|
+
use_features.include?('android.hardware.type.watch')
|
80
|
+
end
|
81
|
+
|
82
|
+
def tv?
|
83
|
+
use_features.include?('android.software.leanback')
|
84
|
+
end
|
85
|
+
|
86
|
+
def min_sdk_version
|
87
|
+
manifest.uses_sdk.min_sdk_version
|
88
|
+
end
|
89
|
+
alias min_os_version min_sdk_version
|
90
|
+
|
91
|
+
def target_sdk_version
|
92
|
+
manifest.uses_sdk.target_sdk_version
|
93
|
+
end
|
94
|
+
|
95
|
+
def use_features
|
96
|
+
@use_features ||= manifest&.uses_feature&.map(&:name)
|
97
|
+
end
|
98
|
+
|
99
|
+
def use_permissions
|
100
|
+
@use_permissions ||= manifest&.uses_permission&.map(&:name)
|
101
|
+
end
|
102
|
+
|
103
|
+
def activities
|
104
|
+
@activities ||= manifest.activities.map(&:name)
|
105
|
+
end
|
106
|
+
|
107
|
+
def services
|
108
|
+
@services ||= manifest.services.map(&:name)
|
109
|
+
end
|
110
|
+
|
111
|
+
def components
|
112
|
+
@components ||= manifest.components.transform_values do |child|
|
113
|
+
child.map(&:name)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def signs
|
118
|
+
return @signs if @signs
|
119
|
+
|
120
|
+
@signs = []
|
121
|
+
each_file do |path, data|
|
122
|
+
# find META-INF/xxx.{RSA|DSA}
|
123
|
+
next unless path =~ %r{^META-INF/} && data.unpack('CC') == [0x30, 0x82]
|
124
|
+
|
125
|
+
@signs << APK::Sign.new(path, OpenSSL::PKCS7.new(data))
|
126
|
+
end
|
127
|
+
|
128
|
+
@signs
|
129
|
+
end
|
130
|
+
|
131
|
+
def certificates
|
132
|
+
@certificates ||= signs.each_with_object([]) do |sign, obj|
|
133
|
+
obj << APK::Certificate.new(sign.path, sign.sign.certificates[0])
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def each_file
|
138
|
+
zip.each do |entry|
|
139
|
+
next unless entry.file?
|
140
|
+
|
141
|
+
yield entry.name, @zip.read(entry)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def read_file(name, base_path: BASE_PATH)
|
146
|
+
content = @zip.read(entry(name, base_path: base_path))
|
147
|
+
return parse_binary_xml(content) if xml_file?(name)
|
148
|
+
|
149
|
+
content
|
150
|
+
end
|
151
|
+
|
152
|
+
def entry(name, base_path: BASE_PATH)
|
153
|
+
entry = @zip.find_entry(File.join(base_path, name))
|
154
|
+
raise NotFoundError, "'#{name}'" if entry.nil?
|
155
|
+
|
156
|
+
entry
|
157
|
+
end
|
158
|
+
|
159
|
+
def manifest
|
160
|
+
io = zip.read(zip.find_entry(BASE_MANIFEST))
|
161
|
+
@manifest ||= Protobuf::Manifest.parse(io, resource)
|
162
|
+
end
|
163
|
+
|
164
|
+
def resource
|
165
|
+
io = zip.read(zip.find_entry(BASE_RESOURCES))
|
166
|
+
@resource ||= Protobuf::Resources.parse(io)
|
167
|
+
end
|
168
|
+
|
169
|
+
def zip
|
170
|
+
@zip ||= Zip::File.open(@file)
|
171
|
+
end
|
172
|
+
|
173
|
+
def icons
|
174
|
+
@icons ||= manifest.icons.each_with_object([]) do |res, obj|
|
175
|
+
path = res.value
|
176
|
+
filename = File.basename(path)
|
177
|
+
filepath = File.join(contents, File.dirname(path))
|
178
|
+
file = File.join(filepath, filename)
|
179
|
+
FileUtils.mkdir_p filepath
|
180
|
+
|
181
|
+
binary_data = read_file(path)
|
182
|
+
File.write(file, binary_data, encoding: Encoding::BINARY)
|
183
|
+
|
184
|
+
obj << {
|
185
|
+
name: filename,
|
186
|
+
file: file,
|
187
|
+
dimensions: ImageSize.path(file).size
|
188
|
+
}
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def clear!
|
193
|
+
return unless @contents
|
194
|
+
|
195
|
+
FileUtils.rm_rf(@contents)
|
196
|
+
|
197
|
+
@aab = nil
|
198
|
+
@contents = nil
|
199
|
+
@icons = nil
|
200
|
+
@app_path = nil
|
201
|
+
@info = nil
|
202
|
+
end
|
203
|
+
|
204
|
+
def contents
|
205
|
+
@contents ||= File.join(Dir.mktmpdir, "AppInfo-android-#{SecureRandom.hex}")
|
206
|
+
end
|
207
|
+
|
208
|
+
private
|
209
|
+
|
210
|
+
def xml_file?(file)
|
211
|
+
File.extname(file) == '.xml'
|
212
|
+
end
|
213
|
+
|
214
|
+
# TODO: how to convert xml content after decode protoubufed content
|
215
|
+
def parse_binary_xml(io)
|
216
|
+
io
|
217
|
+
# Aapt::Pb::XmlNode.decode(io)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
data/lib/app_info/apk.rb
CHANGED
@@ -7,6 +7,7 @@ require 'forwardable'
|
|
7
7
|
module AppInfo
|
8
8
|
# Parse APK file
|
9
9
|
class APK
|
10
|
+
include Helper::HumanFileSize
|
10
11
|
extend Forwardable
|
11
12
|
|
12
13
|
attr_reader :file
|
@@ -24,11 +25,11 @@ module AppInfo
|
|
24
25
|
end
|
25
26
|
|
26
27
|
def size(human_size: false)
|
27
|
-
|
28
|
+
file_to_human_size(@file, human_size: human_size)
|
28
29
|
end
|
29
30
|
|
30
31
|
def os
|
31
|
-
|
32
|
+
Platform::ANDROID
|
32
33
|
end
|
33
34
|
alias file_type os
|
34
35
|
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AppInfo
|
4
|
+
module Inflector
|
5
|
+
def snakecase
|
6
|
+
gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
7
|
+
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
8
|
+
.tr('-', '_')
|
9
|
+
.gsub(/\s/, '_')
|
10
|
+
.gsub(/__+/, '_')
|
11
|
+
.downcase
|
12
|
+
end
|
13
|
+
|
14
|
+
def camelcase(first_letter: :upper, separators: ['-', '_', '\s'])
|
15
|
+
str = dup
|
16
|
+
|
17
|
+
separators.each do |s|
|
18
|
+
str = str.gsub(/(?:#{s}+)([a-z])/) { $1.upcase }
|
19
|
+
end
|
20
|
+
|
21
|
+
case first_letter
|
22
|
+
when :upper, true
|
23
|
+
str = str.gsub(/(\A|\s)([a-z])/) { $1 + $2.upcase }
|
24
|
+
when :lower, false
|
25
|
+
str = str.gsub(/(\A|\s)([A-Z])/) { $1 + $2.downcase }
|
26
|
+
end
|
27
|
+
|
28
|
+
str
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class String
|
34
|
+
include AppInfo::Inflector
|
35
|
+
end
|
data/lib/app_info/dsym.rb
CHANGED
@@ -5,6 +5,8 @@ require 'macho'
|
|
5
5
|
module AppInfo
|
6
6
|
# DSYM parser
|
7
7
|
class DSYM
|
8
|
+
include Helper::Archive
|
9
|
+
|
8
10
|
attr_reader :file
|
9
11
|
|
10
12
|
def initialize(file)
|
@@ -12,7 +14,7 @@ module AppInfo
|
|
12
14
|
end
|
13
15
|
|
14
16
|
def file_type
|
15
|
-
|
17
|
+
Platform::DSYM
|
16
18
|
end
|
17
19
|
|
18
20
|
def object
|
@@ -91,7 +93,7 @@ module AppInfo
|
|
91
93
|
@contents = @file
|
92
94
|
else
|
93
95
|
dsym_dir = nil
|
94
|
-
@contents =
|
96
|
+
@contents = unarchive(@file, path: 'dsym') do |path, zip_file|
|
95
97
|
zip_file.each do |f|
|
96
98
|
unless dsym_dir
|
97
99
|
dsym_dir = f.name
|
@@ -114,6 +116,8 @@ module AppInfo
|
|
114
116
|
|
115
117
|
# DSYM Mach-O
|
116
118
|
class MachO
|
119
|
+
include Helper::HumanFileSize
|
120
|
+
|
117
121
|
def initialize(file, size = 0)
|
118
122
|
@file = file
|
119
123
|
@size = size
|
@@ -132,7 +136,7 @@ module AppInfo
|
|
132
136
|
end
|
133
137
|
|
134
138
|
def size(human_size: false)
|
135
|
-
return
|
139
|
+
return number_to_human_size(@size) if human_size
|
136
140
|
|
137
141
|
@size
|
138
142
|
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AppInfo
|
4
|
+
# App Platform
|
5
|
+
module Platform
|
6
|
+
MACOS = 'macOS'
|
7
|
+
IOS = 'iOS'
|
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_defined?(name)
|
103
|
+
Object.const_get(name)
|
104
|
+
else
|
105
|
+
Object.const_get(namespace).const_set(klass_name, klass)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def define_instance_method(key, value)
|
110
|
+
instance_variable_set("@#{key}", value)
|
111
|
+
self.class.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
112
|
+
def #{key}
|
113
|
+
@#{key}
|
114
|
+
end
|
115
|
+
RUBY
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
module ReferenceParser
|
120
|
+
def reference_segments(value)
|
121
|
+
new_value = value.is_a?(Aapt::Pb::Reference) ? value.name : value
|
122
|
+
return new_value.split('/', 2) if new_value.include?('/')
|
123
|
+
|
124
|
+
[nil, new_value]
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
data/lib/app_info/info_plist.rb
CHANGED
@@ -67,13 +67,13 @@ module AppInfo
|
|
67
67
|
def device_type
|
68
68
|
device_family = info.try(:[], 'UIDeviceFamily')
|
69
69
|
if device_family == [1]
|
70
|
-
|
70
|
+
Device::IPHONE
|
71
71
|
elsif device_family == [2]
|
72
|
-
|
72
|
+
Device::IPAD
|
73
73
|
elsif device_family == [1, 2]
|
74
|
-
|
74
|
+
Device::UNIVERSAL
|
75
75
|
elsif !info.try(:[], 'DTSDKName').nil? || !info.try(:[], 'DTPlatformName').nil?
|
76
|
-
|
76
|
+
Device::MACOS
|
77
77
|
end
|
78
78
|
end
|
79
79
|
|
@@ -112,13 +112,13 @@ module AppInfo
|
|
112
112
|
def_delegators :info, :to_h
|
113
113
|
|
114
114
|
def method_missing(method_name, *args, &block)
|
115
|
-
info.try(:[],
|
115
|
+
info.try(:[], method_name.to_s.camelcase) ||
|
116
116
|
info.send(method_name) ||
|
117
117
|
super
|
118
118
|
end
|
119
119
|
|
120
120
|
def respond_to_missing?(method_name, *args)
|
121
|
-
info.key?(
|
121
|
+
info.key?(method_name.to_s.camelcase) ||
|
122
122
|
info.respond_to?(method_name) ||
|
123
123
|
super
|
124
124
|
end
|
data/lib/app_info/ipa.rb
CHANGED
@@ -8,6 +8,8 @@ require 'cfpropertylist'
|
|
8
8
|
module AppInfo
|
9
9
|
# IPA parser
|
10
10
|
class IPA
|
11
|
+
include Helper::HumanFileSize
|
12
|
+
include Helper::Archive
|
11
13
|
extend Forwardable
|
12
14
|
|
13
15
|
attr_reader :file
|
@@ -28,11 +30,11 @@ module AppInfo
|
|
28
30
|
end
|
29
31
|
|
30
32
|
def size(human_size: false)
|
31
|
-
|
33
|
+
file_to_human_size(@file, human_size: human_size)
|
32
34
|
end
|
33
35
|
|
34
36
|
def os
|
35
|
-
|
37
|
+
Platform::IOS
|
36
38
|
end
|
37
39
|
alias file_type os
|
38
40
|
|
@@ -195,7 +197,7 @@ module AppInfo
|
|
195
197
|
end
|
196
198
|
|
197
199
|
def contents
|
198
|
-
@contents ||=
|
200
|
+
@contents ||= unarchive(@file, path: 'ios')
|
199
201
|
end
|
200
202
|
|
201
203
|
private
|
@@ -213,7 +215,7 @@ module AppInfo
|
|
213
215
|
|
214
216
|
# Uncrush png to normal png file (iOS)
|
215
217
|
def uncrush_png(src_file)
|
216
|
-
dest_file =
|
218
|
+
dest_file = tempdir(src_file, prefix: 'uncrushed')
|
217
219
|
PngUncrush.decompress(src_file, dest_file)
|
218
220
|
File.exist?(dest_file) ? dest_file : nil
|
219
221
|
end
|
data/lib/app_info/macos.rb
CHANGED
@@ -8,6 +8,8 @@ require 'cfpropertylist'
|
|
8
8
|
module AppInfo
|
9
9
|
# MacOS App parser
|
10
10
|
class Macos
|
11
|
+
include Helper::HumanFileSize
|
12
|
+
include Helper::Archive
|
11
13
|
extend Forwardable
|
12
14
|
|
13
15
|
attr_reader :file
|
@@ -24,11 +26,11 @@ module AppInfo
|
|
24
26
|
end
|
25
27
|
|
26
28
|
def size(human_size: false)
|
27
|
-
|
29
|
+
file_to_human_size(@file, human_size: human_size)
|
28
30
|
end
|
29
31
|
|
30
32
|
def os
|
31
|
-
|
33
|
+
Platform::MACOS
|
32
34
|
end
|
33
35
|
alias file_type os
|
34
36
|
|
@@ -142,7 +144,7 @@ module AppInfo
|
|
142
144
|
end
|
143
145
|
|
144
146
|
def contents
|
145
|
-
@contents ||=
|
147
|
+
@contents ||= unarchive(@file, path: 'macos')
|
146
148
|
end
|
147
149
|
|
148
150
|
private
|
@@ -172,7 +174,7 @@ module AppInfo
|
|
172
174
|
reader = Icns::Reader.new(file)
|
173
175
|
Icns::SIZE_TO_TYPE.each do |size, _|
|
174
176
|
dest_filename = "#{File.basename(file, '.icns')}_#{size}x#{size}.png"
|
175
|
-
dest_file =
|
177
|
+
dest_file = tempdir(File.join(File.dirname(file), dest_filename), prefix: 'converted')
|
176
178
|
next unless icon_data = reader.image(size: size)
|
177
179
|
|
178
180
|
File.write(dest_file, icon_data, encoding: Encoding::BINARY)
|