app-info 3.0.0.beta1 → 3.0.0.beta3
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 +2 -0
- data/CHANGELOG.md +29 -3
- data/README.md +4 -4
- data/lib/app_info/aab.rb +57 -114
- data/lib/app_info/android/signature.rb +5 -5
- data/lib/app_info/android/signatures/base.rb +41 -37
- data/lib/app_info/android/signatures/info.rb +135 -129
- data/lib/app_info/android/signatures/v1.rb +56 -52
- data/lib/app_info/android/signatures/v2.rb +114 -110
- data/lib/app_info/android/signatures/v3.rb +124 -120
- data/lib/app_info/android/signatures/v4.rb +13 -9
- data/lib/app_info/android.rb +181 -0
- data/lib/app_info/apk.rb +68 -100
- data/lib/app_info/apple.rb +192 -0
- data/lib/app_info/certificate.rb +14 -19
- data/lib/app_info/const.rb +48 -13
- data/lib/app_info/dsym.rb +6 -3
- data/lib/app_info/error.rb +1 -7
- data/lib/app_info/file.rb +30 -4
- data/lib/app_info/helper/file_size.rb +1 -1
- data/lib/app_info/info_plist.rb +54 -25
- data/lib/app_info/ipa.rb +39 -118
- data/lib/app_info/macos.rb +38 -94
- data/lib/app_info/mobile_provision.rb +49 -20
- data/lib/app_info/pe.rb +103 -7
- data/lib/app_info/png_uncrush.rb +19 -0
- data/lib/app_info/proguard.rb +21 -2
- data/lib/app_info/protobuf/manifest.rb +5 -1
- data/lib/app_info/version.rb +1 -1
- data/lib/app_info.rb +4 -3
- metadata +4 -2
@@ -0,0 +1,192 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'macho'
|
4
|
+
require 'fileutils'
|
5
|
+
require 'forwardable'
|
6
|
+
require 'cfpropertylist'
|
7
|
+
|
8
|
+
module AppInfo
|
9
|
+
# Apple base parser for ipa and macos file
|
10
|
+
class Apple < File
|
11
|
+
extend Forwardable
|
12
|
+
include Helper::HumanFileSize
|
13
|
+
include Helper::Archive
|
14
|
+
|
15
|
+
# Export types
|
16
|
+
module ExportType
|
17
|
+
# debug configuration
|
18
|
+
DEBUG = :debug
|
19
|
+
# adhoc configuration (iOS only)
|
20
|
+
ADHOC = :adhoc
|
21
|
+
# enterprise configuration (iOS only)
|
22
|
+
ENTERPRISE = :enterprise
|
23
|
+
# release configuration
|
24
|
+
RELEASE = :release
|
25
|
+
# release configuration but download from App Store
|
26
|
+
APPSTORE = :appstore
|
27
|
+
end
|
28
|
+
|
29
|
+
# @return [Symbol] {Manufacturer}
|
30
|
+
def manufacturer
|
31
|
+
Manufacturer::APPLE
|
32
|
+
end
|
33
|
+
|
34
|
+
# return file size
|
35
|
+
# @example Read file size in integer
|
36
|
+
# aab.size # => 3618865
|
37
|
+
#
|
38
|
+
# @example Read file size in human readabale
|
39
|
+
# aab.size(human_size: true) # => '3.45 MB'
|
40
|
+
#
|
41
|
+
# @param [Boolean] human_size Convert integer value to human readable.
|
42
|
+
# @return [Integer, String]
|
43
|
+
def size(human_size: false)
|
44
|
+
file_to_human_size(@file, human_size: human_size)
|
45
|
+
end
|
46
|
+
|
47
|
+
# @!method device
|
48
|
+
# @see InfoPlist#device
|
49
|
+
# @!method platform
|
50
|
+
# @see InfoPlist#platform
|
51
|
+
# @!method iphone?
|
52
|
+
# @see InfoPlist#iphone?
|
53
|
+
# @!method ipad?
|
54
|
+
# @see InfoPlist#ipad?
|
55
|
+
# @!method universal?
|
56
|
+
# @see InfoPlist#universal?
|
57
|
+
# @!method build_version
|
58
|
+
# @see InfoPlist#build_version
|
59
|
+
# @!method name
|
60
|
+
# @see InfoPlist#name
|
61
|
+
# @!method release_version
|
62
|
+
# @see InfoPlist#release_version
|
63
|
+
# @!method identifier
|
64
|
+
# @see InfoPlist#identifier
|
65
|
+
# @!method bundle_id
|
66
|
+
# @see InfoPlist#bundle_id
|
67
|
+
# @!method display_name
|
68
|
+
# @see InfoPlist#display_name
|
69
|
+
# @!method bundle_name
|
70
|
+
# @see InfoPlist#bundle_name
|
71
|
+
# @!method min_sdk_version
|
72
|
+
# @see InfoPlist#min_sdk_version
|
73
|
+
# @!method min_os_version
|
74
|
+
# @see InfoPlist#min_os_version
|
75
|
+
def_delegators :info, :device, :platform, :iphone?, :ipad?, :universal?, :macos?,
|
76
|
+
:build_version, :name, :release_version, :identifier, :bundle_id,
|
77
|
+
:display_name, :bundle_name, :min_sdk_version, :min_os_version
|
78
|
+
|
79
|
+
# @!method devices
|
80
|
+
# @see MobileProvision#devices
|
81
|
+
# @!method team_name
|
82
|
+
# @see MobileProvision#team_name
|
83
|
+
# @!method team_identifier
|
84
|
+
# @see MobileProvision#team_identifier
|
85
|
+
# @!method profile_name
|
86
|
+
# @see MobileProvision#profile_name
|
87
|
+
# @!method expired_date
|
88
|
+
# @see MobileProvision#expired_date
|
89
|
+
def_delegators :mobileprovision, :devices, :team_name, :team_identifier,
|
90
|
+
:profile_name, :expired_date
|
91
|
+
|
92
|
+
# @return [String, nil]
|
93
|
+
def distribution_name
|
94
|
+
"#{profile_name} - #{team_name}" if profile_name && team_name
|
95
|
+
end
|
96
|
+
|
97
|
+
# @return [String]
|
98
|
+
def release_type
|
99
|
+
if stored?
|
100
|
+
ExportType::APPSTORE
|
101
|
+
else
|
102
|
+
build_type
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# return iOS build configuration, not correct in macOS app.
|
107
|
+
# @return [String]
|
108
|
+
def build_type
|
109
|
+
if mobileprovision?
|
110
|
+
return ExportType::RELEASE if macos?
|
111
|
+
|
112
|
+
devices ? ExportType::ADHOC : ExportType::ENTERPRISE
|
113
|
+
else
|
114
|
+
ExportType::DEBUG
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# @return [Array<MachO>, nil]
|
119
|
+
def archs
|
120
|
+
return unless ::File.exist?(binary_path)
|
121
|
+
|
122
|
+
file = MachO.open(binary_path)
|
123
|
+
case file
|
124
|
+
when MachO::MachOFile
|
125
|
+
[file.cpusubtype]
|
126
|
+
else
|
127
|
+
file.machos.each_with_object([]) do |arch, obj|
|
128
|
+
obj << arch.cpusubtype
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
alias architectures archs
|
133
|
+
|
134
|
+
# @abstract Subclass and override {#icons} to implement.
|
135
|
+
def icons(_uncrush: true)
|
136
|
+
not_implemented_error!(__method__)
|
137
|
+
end
|
138
|
+
|
139
|
+
# @abstract Subclass and override {#stored?} to implement.
|
140
|
+
def stored?
|
141
|
+
not_implemented_error!(__method__)
|
142
|
+
end
|
143
|
+
|
144
|
+
# force remove developer certificate data from {#mobileprovision} method
|
145
|
+
# @return [nil]
|
146
|
+
def hide_developer_certificates
|
147
|
+
mobileprovision.delete('DeveloperCertificates') if mobileprovision?
|
148
|
+
end
|
149
|
+
|
150
|
+
# @return [MobileProvision]
|
151
|
+
def mobileprovision
|
152
|
+
return unless mobileprovision?
|
153
|
+
|
154
|
+
@mobileprovision ||= MobileProvision.new(mobileprovision_path)
|
155
|
+
end
|
156
|
+
|
157
|
+
# @return [Boolean]
|
158
|
+
def mobileprovision?
|
159
|
+
::File.exist?(mobileprovision_path)
|
160
|
+
end
|
161
|
+
|
162
|
+
# @abstract Subclass and override {#mobileprovision_path} to implement.
|
163
|
+
def mobileprovision_path
|
164
|
+
not_implemented_error!(__method__)
|
165
|
+
end
|
166
|
+
|
167
|
+
# @return [InfoPlist]
|
168
|
+
def info
|
169
|
+
@info ||= InfoPlist.new(info_path)
|
170
|
+
end
|
171
|
+
|
172
|
+
# @abstract Subclass and override {#info_path} to implement.
|
173
|
+
def info_path
|
174
|
+
not_implemented_error!(__method__)
|
175
|
+
end
|
176
|
+
|
177
|
+
# @abstract Subclass and override {#app_path} to implement.
|
178
|
+
def app_path
|
179
|
+
not_implemented_error!(__method__)
|
180
|
+
end
|
181
|
+
|
182
|
+
# @abstract Subclass and override {#clear!} to implement.
|
183
|
+
def clear!
|
184
|
+
not_implemented_error!(__method__)
|
185
|
+
end
|
186
|
+
|
187
|
+
# @return [String] contents path of contents
|
188
|
+
def contents
|
189
|
+
@contents ||= unarchive(@file, prefix: format.to_s)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
data/lib/app_info/certificate.rb
CHANGED
@@ -6,6 +6,7 @@ module AppInfo
|
|
6
6
|
class Certificate
|
7
7
|
# Parse Raw data into X509 cerificate wrapper
|
8
8
|
# @param [String] certificate raw data
|
9
|
+
# @return [AppInfo::Certificate]
|
9
10
|
def self.parse(data)
|
10
11
|
cert = OpenSSL::X509::Certificate.new(data)
|
11
12
|
new(cert)
|
@@ -54,18 +55,22 @@ module AppInfo
|
|
54
55
|
convert_cert_name(raw.subject, format: format)
|
55
56
|
end
|
56
57
|
|
58
|
+
# @return [Time]
|
57
59
|
def created_at
|
58
60
|
raw.not_before
|
59
61
|
end
|
60
62
|
|
63
|
+
# @return [Time]
|
61
64
|
def expired_at
|
62
65
|
raw.not_after
|
63
66
|
end
|
64
67
|
|
68
|
+
# @return [Boolean]
|
65
69
|
def expired?
|
66
70
|
expired_at < Time.now.utc
|
67
71
|
end
|
68
72
|
|
73
|
+
# @return [String] format always be :x509.
|
69
74
|
def format
|
70
75
|
:x509
|
71
76
|
end
|
@@ -113,36 +118,25 @@ module AppInfo
|
|
113
118
|
end
|
114
119
|
end
|
115
120
|
|
116
|
-
# return
|
117
|
-
|
121
|
+
# return length of public key
|
122
|
+
# @return [Integer]
|
123
|
+
# @raise NotImplementedError
|
124
|
+
def length
|
118
125
|
case public_key
|
119
126
|
when OpenSSL::PKey::RSA
|
120
127
|
public_key.n.num_bits
|
121
128
|
when OpenSSL::PKey::DSA, OpenSSL::PKey::DH
|
122
129
|
public_key.p.num_bits
|
123
130
|
when OpenSSL::PKey::EC
|
124
|
-
raise NotImplementedError, "key
|
131
|
+
raise NotImplementedError, "key length for #{public_key.inspect} not implemented"
|
125
132
|
end
|
126
133
|
end
|
134
|
+
alias size length
|
127
135
|
|
128
136
|
# return fingerprint of certificate
|
137
|
+
# @return [String]
|
129
138
|
def fingerprint(name = :sha256, transform: :lower, delimiter: nil)
|
130
139
|
digest = OpenSSL::Digest.new(name.to_s.upcase)
|
131
|
-
# digest = case name.to_sym
|
132
|
-
# when :sha1
|
133
|
-
# OpenSSL::Digest::SHA1.new
|
134
|
-
# when :sha224
|
135
|
-
# OpenSSL::Digest::SHA224.new
|
136
|
-
# when :sha384
|
137
|
-
# OpenSSL::Digest::SHA384.new
|
138
|
-
# when :sha512
|
139
|
-
# OpenSSL::Digest::SHA512.new
|
140
|
-
# when :md5
|
141
|
-
# OpenSSL::Digest::MD5.new
|
142
|
-
# else
|
143
|
-
# OpenSSL::Digest::SHA256.new
|
144
|
-
# end
|
145
|
-
|
146
140
|
digest.update(raw.to_der)
|
147
141
|
fingerprint = digest.to_s
|
148
142
|
fingerprint = fingerprint.upcase if transform.to_sym == :upper
|
@@ -152,6 +146,7 @@ module AppInfo
|
|
152
146
|
end
|
153
147
|
|
154
148
|
# Orginal OpenSSL X509 certificate
|
149
|
+
# @return [OpenSSL::X509::Certificate]
|
155
150
|
def raw
|
156
151
|
@cert
|
157
152
|
end
|
@@ -175,7 +170,7 @@ module AppInfo
|
|
175
170
|
end
|
176
171
|
|
177
172
|
def respond_to_missing?(method, *args)
|
178
|
-
@cert.
|
173
|
+
@cert.respond_to?(method.to_sym) || super
|
179
174
|
end
|
180
175
|
end
|
181
176
|
end
|
data/lib/app_info/const.rb
CHANGED
@@ -3,39 +3,74 @@
|
|
3
3
|
module AppInfo
|
4
4
|
# Full Format
|
5
5
|
module Format
|
6
|
-
#
|
7
|
-
|
6
|
+
# Apple
|
7
|
+
|
8
8
|
INFOPLIST = :infoplist
|
9
9
|
MOBILEPROVISION = :mobileprovision
|
10
10
|
DSYM = :dsym
|
11
11
|
|
12
|
+
# macOS
|
13
|
+
|
14
|
+
MACOS = :macos
|
15
|
+
|
16
|
+
# iOS
|
17
|
+
|
18
|
+
IPA = :ipa
|
19
|
+
|
12
20
|
# Android
|
21
|
+
|
13
22
|
APK = :apk
|
14
23
|
AAB = :aab
|
15
24
|
PROGUARD = :proguard
|
16
25
|
|
17
|
-
# macOS
|
18
|
-
MACOS = :macos
|
19
|
-
|
20
26
|
# Windows
|
27
|
+
|
21
28
|
PE = :pe
|
22
29
|
|
23
30
|
UNKNOWN = :unknown
|
24
31
|
end
|
25
32
|
|
33
|
+
# Manufacturer
|
34
|
+
module Manufacturer
|
35
|
+
APPLE = :apple
|
36
|
+
GOOGLE = :google
|
37
|
+
MICROSOFT = :microsoft
|
38
|
+
end
|
39
|
+
|
26
40
|
# Platform
|
27
41
|
module Platform
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
42
|
+
MACOS = :macos
|
43
|
+
IOS = :ios
|
44
|
+
ANDROID = :android
|
45
|
+
WINDOWS = :windows
|
32
46
|
end
|
33
47
|
|
34
48
|
# Apple Device Type
|
35
49
|
module Device
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
50
|
+
# macOS
|
51
|
+
MACOS = :macos
|
52
|
+
|
53
|
+
# Apple iPhone
|
54
|
+
IPHONE = :iphone
|
55
|
+
# Apple iPad
|
56
|
+
IPAD = :ipad
|
57
|
+
# Apple Watch
|
58
|
+
IWATCH = :iwatch # not implemented yet
|
59
|
+
# Apple Universal (iPhone and iPad)
|
60
|
+
UNIVERSAL = :universal
|
61
|
+
|
62
|
+
# Android Phone
|
63
|
+
PHONE = :phone
|
64
|
+
# Android Tablet (not implemented yet)
|
65
|
+
TABLET = :tablet
|
66
|
+
# Android Watch
|
67
|
+
WATCH = :watch
|
68
|
+
# Android TV
|
69
|
+
TELEVISION = :television
|
70
|
+
# Android Car Automotive
|
71
|
+
AUTOMOTIVE = :automotive
|
72
|
+
|
73
|
+
# Windows
|
74
|
+
WINDOWS = :windows
|
40
75
|
end
|
41
76
|
end
|
data/lib/app_info/dsym.rb
CHANGED
@@ -7,16 +7,18 @@ module AppInfo
|
|
7
7
|
class DSYM < File
|
8
8
|
include Helper::Archive
|
9
9
|
|
10
|
-
|
11
|
-
|
10
|
+
# @return [Symbol] {Manufacturer}
|
11
|
+
def manufacturer
|
12
|
+
Manufacturer::APPLE
|
12
13
|
end
|
13
14
|
|
14
15
|
def each_file(&block)
|
15
16
|
files.each { |file| block.call(file) }
|
16
17
|
end
|
17
18
|
|
19
|
+
# @return [Array<DebugInfo>] dsym_files files by alphabetical order
|
18
20
|
def files
|
19
|
-
@files ||= Dir.children(contents).each_with_object([]) do |file, obj|
|
21
|
+
@files ||= Dir.children(contents).sort.each_with_object([]) do |file, obj|
|
20
22
|
obj << DebugInfo.new(::File.join(contents, file))
|
21
23
|
end
|
22
24
|
end
|
@@ -30,6 +32,7 @@ module AppInfo
|
|
30
32
|
@files = nil
|
31
33
|
end
|
32
34
|
|
35
|
+
# @return [String] contents path of dsym
|
33
36
|
def contents
|
34
37
|
@contents ||= lambda {
|
35
38
|
return @file if ::File.directory?(@file)
|
data/lib/app_info/error.rb
CHANGED
@@ -9,13 +9,7 @@ module AppInfo
|
|
9
9
|
|
10
10
|
class NotFoundError < Error; end
|
11
11
|
|
12
|
-
class NotFoundWinBinraryError < NotFoundError; end
|
13
|
-
|
14
12
|
class ProtobufParseError < Error; end
|
15
13
|
|
16
|
-
class
|
17
|
-
|
18
|
-
# @deprecated Correct to the new {UnknownFileTypeError} class because typo.
|
19
|
-
# It will remove since 2.7.0.
|
20
|
-
class UnkownFileTypeError < Error; end
|
14
|
+
class UnknownFormatError < Error; end
|
21
15
|
end
|
data/lib/app_info/file.rb
CHANGED
@@ -10,14 +10,40 @@ module AppInfo
|
|
10
10
|
@logger = logger
|
11
11
|
end
|
12
12
|
|
13
|
-
# @
|
14
|
-
def
|
15
|
-
|
13
|
+
# @return [Symbol] {Format}
|
14
|
+
def format
|
15
|
+
@format ||= lambda {
|
16
|
+
if instance_of?(AppInfo::File) || instance_of?(AppInfo::Apple) ||
|
17
|
+
instance_of?(AppInfo::Android)
|
18
|
+
not_implemented_error!(__method__)
|
19
|
+
end
|
20
|
+
|
21
|
+
self.class.name.split('::')[-1].downcase.to_sym
|
22
|
+
}.call
|
23
|
+
end
|
24
|
+
|
25
|
+
# @abstract Subclass and override {#platform} to implement.
|
26
|
+
def platform
|
27
|
+
not_implemented_error!(__method__)
|
28
|
+
end
|
29
|
+
|
30
|
+
# @abstract Subclass and override {#manufacturer} to implement.
|
31
|
+
def manufacturer
|
32
|
+
not_implemented_error!(__method__)
|
33
|
+
end
|
34
|
+
|
35
|
+
# @abstract Subclass and override {#device} to implement.
|
36
|
+
def device
|
37
|
+
not_implemented_error!(__method__)
|
16
38
|
end
|
17
39
|
|
18
40
|
# @abstract Subclass and override {#size} to implement
|
19
41
|
def size(human_size: false)
|
20
|
-
|
42
|
+
not_implemented_error!(__method__)
|
43
|
+
end
|
44
|
+
|
45
|
+
def not_implemented_error!(method)
|
46
|
+
raise NotImplementedError, ".#{method} method implantation required in #{self.class}"
|
21
47
|
end
|
22
48
|
end
|
23
49
|
end
|
@@ -16,7 +16,7 @@ module AppInfo::Helper
|
|
16
16
|
max_exp = FILE_SIZE_UNITS.size - 1
|
17
17
|
exponent = (Math.log(number) / Math.log(1024)).to_i
|
18
18
|
exponent = max_exp if exponent > max_exp
|
19
|
-
number = format('%<number>.2f', number: (number / (1024**exponent.to_f)))
|
19
|
+
number = Kernel.format('%<number>.2f', number: (number / (1024**exponent.to_f)))
|
20
20
|
end
|
21
21
|
|
22
22
|
"#{number} #{FILE_SIZE_UNITS[exponent]}"
|
data/lib/app_info/info_plist.rb
CHANGED
@@ -17,94 +17,120 @@ module AppInfo
|
|
17
17
|
Device::MACOS => %w[CFBundleIconFile CFBundleIconName]
|
18
18
|
}.freeze
|
19
19
|
|
20
|
-
|
21
|
-
|
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
|
22
48
|
end
|
23
49
|
|
50
|
+
# @return [String, nil]
|
24
51
|
def version
|
25
52
|
release_version || build_version
|
26
53
|
end
|
27
54
|
|
55
|
+
# @return [String, nil]
|
28
56
|
def build_version
|
29
57
|
info.try(:[], 'CFBundleVersion')
|
30
58
|
end
|
31
59
|
|
60
|
+
# @return [String, nil]
|
32
61
|
def release_version
|
33
62
|
info.try(:[], 'CFBundleShortVersionString')
|
34
63
|
end
|
35
64
|
|
65
|
+
# @return [String, nil]
|
36
66
|
def identifier
|
37
67
|
info.try(:[], 'CFBundleIdentifier')
|
38
68
|
end
|
39
69
|
alias bundle_id identifier
|
40
70
|
|
71
|
+
# @return [String, nil]
|
41
72
|
def name
|
42
73
|
display_name || bundle_name
|
43
74
|
end
|
44
75
|
|
76
|
+
# @return [String, nil]
|
45
77
|
def display_name
|
46
78
|
info.try(:[], 'CFBundleDisplayName')
|
47
79
|
end
|
48
80
|
|
81
|
+
# @return [String, nil]
|
49
82
|
def bundle_name
|
50
83
|
info.try(:[], 'CFBundleName')
|
51
84
|
end
|
52
85
|
|
86
|
+
# @return [String, nil]
|
53
87
|
def min_os_version
|
54
88
|
min_sdk_version || min_system_version
|
55
89
|
end
|
56
90
|
|
57
|
-
#
|
58
91
|
# Extract the Minimum OS Version from the Info.plist (iOS Only)
|
59
|
-
#
|
92
|
+
# @return [String, nil]
|
60
93
|
def min_sdk_version
|
61
94
|
info.try(:[], 'MinimumOSVersion')
|
62
95
|
end
|
63
96
|
|
64
|
-
#
|
65
97
|
# Extract the Minimum OS Version from the Info.plist (macOS Only)
|
66
|
-
#
|
98
|
+
# @return [String, nil]
|
67
99
|
def min_system_version
|
68
100
|
info.try(:[], 'LSMinimumSystemVersion')
|
69
101
|
end
|
70
102
|
|
103
|
+
# @return [Array<String>]
|
71
104
|
def icons
|
72
|
-
@icons ||= ICON_KEYS[
|
73
|
-
end
|
74
|
-
|
75
|
-
def device_type
|
76
|
-
device_family = info.try(:[], 'UIDeviceFamily')
|
77
|
-
if device_family == [1]
|
78
|
-
Device::IPHONE
|
79
|
-
elsif device_family == [2]
|
80
|
-
Device::IPAD
|
81
|
-
elsif device_family == [1, 2]
|
82
|
-
Device::UNIVERSAL
|
83
|
-
elsif !info.try(:[], 'DTSDKName').nil? || !info.try(:[], 'DTPlatformName').nil?
|
84
|
-
Device::MACOS
|
85
|
-
end
|
105
|
+
@icons ||= ICON_KEYS[device]
|
86
106
|
end
|
87
107
|
|
108
|
+
# @return [Boolean]
|
88
109
|
def iphone?
|
89
|
-
|
110
|
+
device == Device::IPHONE
|
90
111
|
end
|
91
112
|
|
113
|
+
# @return [Boolean]
|
92
114
|
def ipad?
|
93
|
-
|
115
|
+
device == Device::IPAD
|
94
116
|
end
|
95
117
|
|
118
|
+
# @return [Boolean]
|
96
119
|
def universal?
|
97
|
-
|
120
|
+
device == Device::UNIVERSAL
|
98
121
|
end
|
99
122
|
|
123
|
+
# @return [Boolean]
|
100
124
|
def macos?
|
101
|
-
|
125
|
+
device == Device::MACOS
|
102
126
|
end
|
103
127
|
|
128
|
+
# @return [Array<String>]
|
104
129
|
def device_family
|
105
130
|
info.try(:[], 'UIDeviceFamily') || []
|
106
131
|
end
|
107
132
|
|
133
|
+
# @return [String]
|
108
134
|
def release_type
|
109
135
|
if stored?
|
110
136
|
'Store'
|
@@ -113,10 +139,13 @@ module AppInfo
|
|
113
139
|
end
|
114
140
|
end
|
115
141
|
|
142
|
+
# @return [String, nil]
|
116
143
|
def [](key)
|
117
144
|
info.try(:[], key.to_s)
|
118
145
|
end
|
119
146
|
|
147
|
+
# @!method to_h
|
148
|
+
# @see CFPropertyList#to_h
|
120
149
|
def_delegators :info, :to_h
|
121
150
|
|
122
151
|
def method_missing(method_name, *args, &block)
|
@@ -140,7 +169,7 @@ module AppInfo
|
|
140
169
|
end
|
141
170
|
|
142
171
|
def app_path
|
143
|
-
@app_path ||= case
|
172
|
+
@app_path ||= case device
|
144
173
|
when Device::MACOS
|
145
174
|
::File.dirname(@file)
|
146
175
|
else
|