app-info 2.0.0.beta3 → 2.0.0.beta4
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/CHANGELOG.md +8 -7
- data/README.md +7 -7
- data/lib/app_info.rb +19 -9
- data/lib/app_info/apk.rb +184 -0
- data/lib/app_info/core_ext/object/try.rb +2 -0
- data/lib/app_info/dsym.rb +149 -0
- data/lib/app_info/ipa.rb +230 -0
- data/lib/app_info/ipa/info_plist.rb +133 -0
- data/lib/app_info/ipa/mobile_provision.rb +89 -0
- data/lib/app_info/util.rb +26 -0
- data/lib/app_info/version.rb +1 -1
- metadata +7 -7
- data/lib/app_info/parser.rb +0 -40
- data/lib/app_info/parser/apk.rb +0 -183
- data/lib/app_info/parser/dsym.rb +0 -126
- data/lib/app_info/parser/ipa.rb +0 -229
- data/lib/app_info/parser/ipa/info_plist.rb +0 -135
- data/lib/app_info/parser/ipa/mobile_provision.rb +0 -91
@@ -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
|
data/lib/app_info/version.rb
CHANGED
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.
|
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/
|
179
|
-
- lib/app_info/
|
180
|
-
- lib/app_info/
|
181
|
-
- lib/app_info/
|
182
|
-
- lib/app_info/
|
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:
|
data/lib/app_info/parser.rb
DELETED
@@ -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
|
data/lib/app_info/parser/apk.rb
DELETED
@@ -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
|
data/lib/app_info/parser/dsym.rb
DELETED
@@ -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
|
data/lib/app_info/parser/ipa.rb
DELETED
@@ -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
|