app-info 2.7.0.beta2 → 2.8.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '09f29a3f5c7efa6e39d589cfc5d4a168d5267bb88ebf9434a377dbe1ba9d6326'
4
- data.tar.gz: 45bb2036a6777b043ab85b399d0559f92f144750d98097ea3ad6007ffed39c0d
3
+ metadata.gz: e3f9fde9fba3ff6b0653e7ebf7c122c2dc290aaa81be4ce52d6fd1853fc9963e
4
+ data.tar.gz: 3a18e33551bce9cb9155779e56f943c8df6f7528807410e66ec897d8f64a57c6
5
5
  SHA512:
6
- metadata.gz: 99af334d2500db7df891232986366e390943a11689b593f0b827edeabc68fdc3df38b0c2d675c09282941f4f3870ad8afa6ee46c95b18acf1d40c41990698b19
7
- data.tar.gz: 45c1da2efa47617b4dccd315fb868097df4814492e6e9e6371082c9f84171ed493eb9de8449483a8d640e8b0d2a830f0840eff199e4a786ff937f8662b102240
6
+ metadata.gz: 1ed9868f7cde0c52565cb9433f812c51645c5d01ea5501770d8f09be3ab4ed06ff0a09baf45c8e69d09c3e011a3376d2268de71988bcf89d363570d5c4e02872
7
+ data.tar.gz: aa2516f818a0e2452f960769c4ac344ab31ea87e6802e0126373f91cfe492cefa575051377cfbfaeeddcff988adf984a09d6c73cbe61a3c6a1ce6548f9bec460
data/CHANGELOG.md CHANGED
@@ -9,6 +9,31 @@ 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.8.1] (2021-12-20)
13
+
14
+ ### Fixed
15
+
16
+ - Fix no found intent filter in manifest error.
17
+
18
+ ## [2.8.0] (2021-12-16)
19
+
20
+ ### Added
21
+
22
+ - New methods added to `apk` and `aab` [3adfa223](https://github.com/icyleaf/app_info/tree/3adfa223479caa672fce5d3a119b6db098463699) [939a6506](https://github.com/icyleaf/app_info/tree/939a6506f3ac1cb7ad1ed46128df41de6ee3b0d0)
23
+
24
+ ## [2.7.0] (2021-10-15)
25
+
26
+ ### Added
27
+
28
+ - Android App Bundle (a.k.a) aab support!!! parts support [#36](https://github.com/icyleaf/app_info/pull/36)
29
+
30
+ ## [2.7.0.beta5] (2021-10-14)
31
+
32
+ ### Fixed
33
+
34
+ - Renamed methods of inflector (Conflicts with similar external methods, such like ActiveSupport Core Extensions)
35
+ - Keep same behavior methods between apk and aab
36
+
12
37
  ## [2.7.0.beta2] (2021-09-29)
13
38
 
14
39
  ### Fixed
@@ -197,7 +222,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
197
222
 
198
223
  - Updated dependency of CFPropertly list be a range between 2.3.4. (thanks @[cschroed](https://github.com/cschroed))
199
224
 
200
- [Unreleased]: https://github.com/icyleaf/app-info/compare/v2.7.0.beta2..HEAD
225
+ [Unreleased]: https://github.com/icyleaf/app-info/compare/v2.8.1..HEAD
226
+ [2.8.1]: https://github.com/icyleaf/app-info/compare/v2.8.0...v2.8.1
227
+ [2.8.0]: https://github.com/icyleaf/app-info/compare/v2.7.0...v2.8.0
228
+ [2.7.0]: https://github.com/icyleaf/app-info/compare/v2.6.5...v2.7.0
229
+ [2.7.0.beta5]: https://github.com/icyleaf/app-info/compare/v2.7.0.beta2...v2.7.0.beta5
201
230
  [2.7.0.beta2]: https://github.com/icyleaf/app-info/compare/v2.7.0.beta1...v2.7.0.beta2
202
231
  [2.7.0.beta1]: https://github.com/icyleaf/app-info/compare/v2.6.5...v2.7.0.beta1
203
232
  [2.6.5]: https://github.com/icyleaf/app-info/compare/v2.6.4...v2.6.5
data/README.md CHANGED
@@ -49,6 +49,7 @@ require 'app-info'
49
49
  parser = AppInfo.parse('iphone.ipa')
50
50
  parser = AppInfo.parse('ipad.ipa')
51
51
  parser = AppInfo.parse('android.apk')
52
+ parser = AppInfo.parse('android.aab')
52
53
  parser = AppInfo.parse('u-u-i-d.mobileprovision')
53
54
  parser = AppInfo.parse('macOS.App.zip')
54
55
  parser = AppInfo.parse('App.dSYm.zip')
@@ -153,56 +154,67 @@ profile.enabled_capabilities
153
154
 
154
155
  ### Android
155
156
 
157
+ Accept `.aab` and `.apk` Android file.
158
+
156
159
  ```ruby
157
- apk = AppInfo.parse('android.apk')
160
+ android = AppInfo.parse('android.apk_or_aab')
158
161
 
159
162
  # get app file size
160
- apk.size
163
+ android.size
161
164
  # => 3093823
162
165
 
163
166
  # get app file size in human reable.
164
- apk.size(human_size: true)
167
+ android.size(human_size: true)
165
168
  # => 29 MB
166
169
 
167
170
  # get app release version
168
- apk.release_version
171
+ android.release_version
169
172
  # => 1.0
170
173
 
171
174
  # get app package name
172
- apk.bundle_id
175
+ android.bundle_id
173
176
  # => com.icyleaf.AppInfoDemo
174
177
 
178
+ # detect app type (It's difficult to detect phone or tablet)
179
+ android.tv?
180
+ android.wear?
181
+ android.automotive?
182
+
175
183
  # get app icons
176
- apk.icons
184
+ android.icons
177
185
  # => [{:name=>"ic_launcher.png", :file=> "/temp/dir/app/ic_launcher.png", :dimensions=>[48, 48]}, ...]
178
186
 
179
187
  # get app support min sdk version
180
- apk.min_sdk_version
188
+ android.min_sdk_version
181
189
  # => 13
182
190
 
183
191
  # get use_permissions list
184
- apk.use_permissions
192
+ android.use_permissions
185
193
  # => [...]
186
194
 
187
195
  # get activitiy list
188
- apk.activities
196
+ android.activities
189
197
  # => [...]
190
198
 
191
199
  # get service list
192
- apk.services
200
+ android.services
193
201
  # => [...]
194
202
 
195
- # get certificate list
196
- apk.certificates
197
- # => [...]
203
+ # get deep links host
204
+ android.deep_links
205
+ # => ['a.com']
198
206
 
199
- # get sign list
200
- apk.signs
207
+ # get schemes without http or https
208
+ android.schemes
209
+ # => ['appinfo']
210
+
211
+ # get sign list (only v1 sign)
212
+ android.signs
201
213
  # => [...]
202
214
 
203
- # detect app type (It's difficult to detect phone or tablet)
204
- apk.tv?
205
- apk.wear?
215
+ # get certificate list (only v1 sign)
216
+ android.certificates
217
+ # => [...]
206
218
  ```
207
219
 
208
220
  ### macOS
@@ -296,9 +308,9 @@ It is possible to use this gem as a command line interface to parse mobile app:
296
308
  ```
297
309
  > app-info
298
310
 
299
- app-info (0.6.0)> p = AppInfo.parse('/path/to/app')
311
+ app-info (2.7.0)> p = AppInfo.parse('/path/to/app')
300
312
  => #<AppInfo::APK::......>
301
- app-info (0.6.0)> p.name
313
+ app-info (2.7.0)> p.name
302
314
  => "AppName"
303
315
  ```
304
316
 
data/Rakefile CHANGED
@@ -4,6 +4,7 @@ $LOAD_PATH.unshift File.expand_path('lib', __dir__)
4
4
  require 'app_info'
5
5
  require 'bundler/gem_tasks'
6
6
  require 'rspec/core/rake_task'
7
+ require 'rubocop/rake_task'
7
8
 
8
9
  RSpec::Core::RakeTask.new(:spec)
9
10
 
data/app_info.gemspec CHANGED
@@ -21,9 +21,9 @@ Gem::Specification.new do |spec|
21
21
  spec.required_ruby_version = '>= 2.5'
22
22
 
23
23
  spec.add_dependency 'CFPropertyList', '< 3.1.0', '>= 2.3.4'
24
- spec.add_dependency 'image_size', '>= 1.5', '< 2.2'
24
+ spec.add_dependency 'image_size', '>= 1.5', '< 3.1'
25
25
  spec.add_dependency 'ruby-macho', '< 3', '>= 1.4'
26
- spec.add_dependency 'android_parser', '~> 2.4.1'
26
+ spec.add_dependency 'android_parser', '~> 2.5.0'
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'
data/lib/app_info/aab.rb CHANGED
@@ -14,10 +14,11 @@ module AppInfo
14
14
 
15
15
  # APK Devices
16
16
  module Device
17
- PHONE = 'Phone'
18
- TABLET = 'Tablet'
19
- WATCH = 'Watch'
20
- TV = 'Television'
17
+ PHONE = 'Phone'
18
+ TABLET = 'Tablet'
19
+ WATCH = 'Watch'
20
+ TV = 'Television'
21
+ AUTOMOTIVE = 'Automotive'
21
22
  end
22
23
 
23
24
  BASE_PATH = 'base'
@@ -37,7 +38,7 @@ module AppInfo
37
38
  end
38
39
  alias file_type os
39
40
 
40
- def_delegators :manifest, :version_name
41
+ def_delegators :manifest, :version_name, :deep_links, :schemes
41
42
 
42
43
  alias release_version version_name
43
44
 
@@ -61,6 +62,8 @@ module AppInfo
61
62
  Device::WATCH
62
63
  elsif tv?
63
64
  Device::TV
65
+ elsif automotive?
66
+ Device::AUTOMOTIVE
64
67
  else
65
68
  Device::PHONE
66
69
  end
@@ -83,6 +86,10 @@ module AppInfo
83
86
  use_features.include?('android.software.leanback')
84
87
  end
85
88
 
89
+ def automotive?
90
+ use_features.include?('android.hardware.type.automotive')
91
+ end
92
+
86
93
  def min_sdk_version
87
94
  manifest.uses_sdk.min_sdk_version
88
95
  end
@@ -101,17 +108,27 @@ module AppInfo
101
108
  end
102
109
 
103
110
  def activities
104
- @activities ||= manifest.activities.map(&:name)
111
+ @activities ||= manifest.activities
105
112
  end
106
113
 
107
114
  def services
108
- @services ||= manifest.services.map(&:name)
115
+ @services ||= manifest.services
109
116
  end
110
117
 
111
118
  def components
112
- @components ||= manifest.components.transform_values do |child|
113
- child.map(&:name)
114
- end
119
+ @components ||= manifest.components.transform_values
120
+ end
121
+
122
+ def sign_version
123
+ return 'v1' unless signs.empty?
124
+
125
+ # when ?
126
+ # https://source.android.com/security/apksigning/v2?hl=zh-cn
127
+ # 'v2'
128
+ # when ?
129
+ # https://source.android.com/security/apksigning/v3?hl=zh-cn
130
+ # 'v3'
131
+ 'unknown'
115
132
  end
116
133
 
117
134
  def signs
data/lib/app_info/apk.rb CHANGED
@@ -14,10 +14,11 @@ module AppInfo
14
14
 
15
15
  # APK Devices
16
16
  module Device
17
- PHONE = 'Phone'
18
- TABLET = 'Tablet'
19
- WATCH = 'Watch'
20
- TV = 'Television'
17
+ PHONE = 'Phone'
18
+ TABLET = 'Tablet'
19
+ WATCH = 'Watch'
20
+ TV = 'Television'
21
+ AUTOMOTIVE = 'Automotive'
21
22
  end
22
23
 
23
24
  def initialize(file)
@@ -35,8 +36,9 @@ module AppInfo
35
36
 
36
37
  def_delegators :apk, :manifest, :resource, :dex
37
38
 
38
- def_delegators :manifest, :version_name, :package_name,
39
- :use_permissions, :components
39
+ def_delegators :manifest, :version_name, :package_name, :target_sdk_version,
40
+ :components, :services, :use_permissions, :use_features,
41
+ :deep_links, :schemes
40
42
 
41
43
  alias release_version version_name
42
44
  alias identifier package_name
@@ -56,14 +58,15 @@ module AppInfo
56
58
  Device::WATCH
57
59
  elsif tv?
58
60
  Device::TV
61
+ elsif automotive?
62
+ Device::AUTOMOTIVE
59
63
  else
60
64
  Device::PHONE
61
65
  end
62
66
  end
63
67
 
64
- # TODO: find a way to detect
68
+ # TODO: find a way to detect, no way!
65
69
  # def tablet?
66
- # resource
67
70
  # end
68
71
 
69
72
  def wear?
@@ -74,20 +77,25 @@ module AppInfo
74
77
  use_features.include?('android.software.leanback')
75
78
  end
76
79
 
80
+ def automotive?
81
+ use_features.include?('android.hardware.type.automotive')
82
+ end
83
+
77
84
  def min_sdk_version
78
85
  manifest.min_sdk_ver
79
86
  end
80
87
  alias min_os_version min_sdk_version
81
88
 
82
- def target_sdk_version
83
- manifest.doc
84
- .elements['/manifest/uses-sdk']
85
- .attributes['targetSdkVersion']
86
- .to_i
87
- end
89
+ def sign_version
90
+ return 'v1' unless signs.empty?
88
91
 
89
- def use_features
90
- manifest_values('/manifest/uses-feature')
92
+ # when ?
93
+ # https://source.android.com/security/apksigning/v2?hl=zh-cn
94
+ # 'v2'
95
+ # when ?
96
+ # https://source.android.com/security/apksigning/v3?hl=zh-cn
97
+ # 'v3'
98
+ 'unknown'
91
99
  end
92
100
 
93
101
  def signs
@@ -106,10 +114,6 @@ module AppInfo
106
114
  components.select { |c| c.type == 'activity' }
107
115
  end
108
116
 
109
- def services
110
- components.select { |c| c.type == 'service' }
111
- end
112
-
113
117
  def apk
114
118
  @apk ||= ::Android::Apk.new(@file)
115
119
  end
@@ -146,16 +150,6 @@ module AppInfo
146
150
  @contents ||= File.join(Dir.mktmpdir, "AppInfo-android-#{SecureRandom.hex}")
147
151
  end
148
152
 
149
- private
150
-
151
- def manifest_values(path, key = 'name')
152
- values = []
153
- manifest.doc.each_element(path) do |elem|
154
- values << elem.attributes[key]
155
- end
156
- values.uniq
157
- end
158
-
159
153
  # Android Certificate
160
154
  class Certificate
161
155
  attr_reader :path, :certificate
@@ -2,7 +2,7 @@
2
2
 
3
3
  module AppInfo
4
4
  module Inflector
5
- def snakecase
5
+ def ai_snakecase
6
6
  gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
7
7
  .gsub(/([a-z\d])([A-Z])/, '\1_\2')
8
8
  .tr('-', '_')
@@ -11,7 +11,7 @@ module AppInfo
11
11
  .downcase
12
12
  end
13
13
 
14
- def camelcase(first_letter: :upper, separators: ['-', '_', '\s'])
14
+ def ai_camelcase(first_letter: :upper, separators: ['-', '_', '\s'])
15
15
  str = dup
16
16
 
17
17
  separators.each do |s|
@@ -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(:[], method_name.to_s.camelcase) ||
115
+ info.try(:[], method_name.to_s.ai_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?(method_name.to_s.camelcase) ||
121
+ info.key?(method_name.to_s.ai_camelcase) ||
122
122
  info.respond_to?(method_name) ||
123
123
  super
124
124
  end
@@ -212,13 +212,13 @@ module AppInfo
212
212
  end
213
213
 
214
214
  def method_missing(method_name, *args, &block)
215
- mobileprovision.try(:[], method_name.to_s.camelcase) ||
215
+ mobileprovision.try(:[], method_name.to_s.ai_camelcase) ||
216
216
  mobileprovision.send(method_name) ||
217
217
  super
218
218
  end
219
219
 
220
220
  def respond_to_missing?(method_name, *args)
221
- mobileprovision.key?(method_name.to_s.camelcase) ||
221
+ mobileprovision.key?(method_name.to_s.ai_camelcase) ||
222
222
  mobileprovision.respond_to?(method_name) ||
223
223
  super
224
224
  end
@@ -72,7 +72,7 @@ module AppInfo
72
72
  @attributes = element.attribute.each_with_object({}).each do |item, obj|
73
73
  node = Attribute.new(item)
74
74
 
75
- method_name = node.name.snakecase
75
+ method_name = node.name.ai_snakecase
76
76
  obj[method_name] = node
77
77
  define_instance_method(method_name, node.value)
78
78
  end
@@ -87,11 +87,11 @@ module AppInfo
87
87
  @children = element.child.each_with_object({}) do |item, obj|
88
88
  next unless item_element = item.element
89
89
 
90
- class_name = item_element.name.camelcase
90
+ class_name = item_element.name.ai_camelcase
91
91
  klass = create_class(class_name, Protobuf::Node, namespace: 'AppInfo::Protobuf::Manifest')
92
92
  node = klass.new(item)
93
93
 
94
- method_name = item_element.name.snakecase
94
+ method_name = item_element.name.ai_snakecase
95
95
  if UNIQUE_KEY.include?(method_name)
96
96
  obj[method_name] = node
97
97
  else
@@ -129,19 +129,98 @@ module AppInfo
129
129
  end
130
130
 
131
131
  def activities
132
- application.activity
132
+ application.respond_to?(:activity) ? application.activity : []
133
133
  end
134
134
 
135
135
  def services
136
- application.service
136
+ application.respond_to?(:service) ? application.service : []
137
137
  end
138
138
 
139
139
  def icons
140
140
  @resources.find(application.icon)
141
141
  end
142
142
 
143
+ def deep_links
144
+ intent_filters(search: :deep_links)
145
+ end
146
+
147
+ def schemes
148
+ intent_filters(search: :schemes)
149
+ end
150
+
151
+ private
152
+
153
+ def intent_filters(search: nil)
154
+ activities.each_with_object([]) do |activity, obj|
155
+ intent_filters = activity.intent_filter
156
+ next if intent_filters&.empty?
157
+
158
+ if search.nil? || search.empty?
159
+ obj << intent_filters
160
+ else
161
+ intent_filters.each do |filter|
162
+ exist_method = "#{search}?".to_sym
163
+ next if filter.respond_to?(exist_method) && !filter.send(exist_method)
164
+
165
+ obj << filter.send(search)
166
+ end
167
+ end
168
+ end.flatten.uniq
169
+ end
170
+
171
+ # :nodoc:
143
172
  # Workaround ruby always return true by called `Object.const_defined?(Data)`
144
173
  class Data < Node; end
174
+
175
+ class IntentFilter < Node
176
+ # filter types (action is required, category and data are optional)
177
+ TYPES = %w[action category data].freeze
178
+
179
+ DEEP_LINK_SCHEMES = %w[http https].freeze
180
+
181
+ # browsable of category
182
+ CATEGORY_BROWSABLE = 'android.intent.category.BROWSABLE'
183
+
184
+ def deep_links?
185
+ browsable? && data.any? { |d| DEEP_LINK_SCHEMES.include?(d.scheme) }
186
+ end
187
+
188
+ def deep_links
189
+ return unless deep_links?
190
+
191
+ data.reject { |d| d.host.nil? }
192
+ .map(&:host)
193
+ .uniq
194
+ end
195
+
196
+ def schemes
197
+ return unless schemes?
198
+
199
+ data.select { |d| !d.scheme.nil? && !DEEP_LINK_SCHEMES.include?(d.scheme) }
200
+ .map(&:scheme)
201
+ .uniq
202
+ end
203
+
204
+ def schemes?
205
+ browsable? && data.any? { |d| !DEEP_LINK_SCHEMES.include?(d.scheme) }
206
+ end
207
+
208
+ def browsable?
209
+ exist?(CATEGORY_BROWSABLE)
210
+ end
211
+
212
+ def exist?(name, type: nil)
213
+ if type.to_s.empty? && !name.start_with?('android.intent.')
214
+ raise 'Fill type or use correct name'
215
+ end
216
+
217
+ type ||= name.split('.')[2]
218
+ raise 'Not found type' unless TYPES.include?(type)
219
+
220
+ values = send(type.to_sym).select { |e| e.name == name }
221
+ values.empty? ? false : values
222
+ end
223
+ end
145
224
  end
146
225
  end
147
226
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AppInfo
4
- VERSION = '2.7.0.beta2'
4
+ VERSION = '2.8.1'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: app-info
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.7.0.beta2
4
+ version: 2.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - icyleaf
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-09-29 00:00:00.000000000 Z
11
+ date: 2021-12-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: CFPropertyList
@@ -39,7 +39,7 @@ dependencies:
39
39
  version: '1.5'
40
40
  - - "<"
41
41
  - !ruby/object:Gem::Version
42
- version: '2.2'
42
+ version: '3.1'
43
43
  type: :runtime
44
44
  prerelease: false
45
45
  version_requirements: !ruby/object:Gem::Requirement
@@ -49,7 +49,7 @@ dependencies:
49
49
  version: '1.5'
50
50
  - - "<"
51
51
  - !ruby/object:Gem::Version
52
- version: '2.2'
52
+ version: '3.1'
53
53
  - !ruby/object:Gem::Dependency
54
54
  name: ruby-macho
55
55
  requirement: !ruby/object:Gem::Requirement
@@ -76,14 +76,14 @@ dependencies:
76
76
  requirements:
77
77
  - - "~>"
78
78
  - !ruby/object:Gem::Version
79
- version: 2.4.1
79
+ version: 2.5.0
80
80
  type: :runtime
81
81
  prerelease: false
82
82
  version_requirements: !ruby/object:Gem::Requirement
83
83
  requirements:
84
84
  - - "~>"
85
85
  - !ruby/object:Gem::Version
86
- version: 2.4.1
86
+ version: 2.5.0
87
87
  - !ruby/object:Gem::Dependency
88
88
  name: rubyzip
89
89
  requirement: !ruby/object:Gem::Requirement
@@ -272,9 +272,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
272
272
  version: '2.5'
273
273
  required_rubygems_version: !ruby/object:Gem::Requirement
274
274
  requirements:
275
- - - ">"
275
+ - - ">="
276
276
  - !ruby/object:Gem::Version
277
- version: 1.3.1
277
+ version: '0'
278
278
  requirements: []
279
279
  rubygems_version: 3.1.4
280
280
  signing_key: