device_detector 1.0.5 → 1.0.6

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.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +7 -6
  3. data/lib/device_detector/browser.rb +364 -0
  4. data/lib/device_detector/client.rb +8 -0
  5. data/lib/device_detector/device.rb +1124 -4
  6. data/lib/device_detector/os.rb +36 -11
  7. data/lib/device_detector/version.rb +1 -1
  8. data/lib/device_detector/version_extractor.rb +9 -0
  9. data/lib/device_detector.rb +57 -13
  10. data/regexes/bots.yml +551 -58
  11. data/regexes/client/browser_engine.yml +7 -1
  12. data/regexes/client/browsers.yml +594 -71
  13. data/regexes/client/feed_readers.yml +4 -10
  14. data/regexes/client/libraries.yml +47 -2
  15. data/regexes/client/mediaplayers.yml +25 -1
  16. data/regexes/client/mobile_apps.yml +447 -77
  17. data/regexes/client/pim.yml +49 -1
  18. data/regexes/device/cameras.yml +5 -5
  19. data/regexes/device/car_browsers.yml +16 -0
  20. data/regexes/device/consoles.yml +6 -0
  21. data/regexes/device/mobiles.yml +12905 -4561
  22. data/regexes/device/portable_media_player.yml +20 -3
  23. data/regexes/device/shell_tv.yml +117 -0
  24. data/regexes/device/televisions.yml +426 -35
  25. data/regexes/oss.yml +567 -139
  26. data/spec/device_detector/concrete_user_agent_spec.rb +30 -42
  27. data/spec/device_detector/detector_fixtures_spec.rb +9 -5
  28. data/spec/device_detector/device_spec.rb +26 -10
  29. data/spec/fixtures/client/browser.yml +1463 -391
  30. data/spec/fixtures/client/feed_reader.yml +0 -12
  31. data/spec/fixtures/client/library.yml +91 -37
  32. data/spec/fixtures/client/mediaplayer.yml +30 -0
  33. data/spec/fixtures/client/mobile_app.yml +498 -45
  34. data/spec/fixtures/client/pim.yml +60 -0
  35. data/spec/fixtures/detector/bots.yml +1189 -568
  36. data/spec/fixtures/detector/camera.yml +12 -26
  37. data/spec/fixtures/detector/car_browser.yml +151 -15
  38. data/spec/fixtures/detector/console.yml +70 -48
  39. data/spec/fixtures/detector/desktop.yml +2041 -916
  40. data/spec/fixtures/detector/feature_phone.yml +895 -189
  41. data/spec/fixtures/detector/feed_reader.yml +50 -77
  42. data/spec/fixtures/detector/mediaplayer.yml +79 -26
  43. data/spec/fixtures/detector/mobile_apps.yml +726 -72
  44. data/spec/fixtures/detector/peripheral.yml +271 -0
  45. data/spec/fixtures/detector/phablet.yml +3635 -1596
  46. data/spec/fixtures/detector/portable_media_player.yml +355 -46
  47. data/spec/fixtures/detector/smart_display.yml +183 -9
  48. data/spec/fixtures/detector/smart_speaker.yml +13 -8
  49. data/spec/fixtures/detector/smartphone-1.yml +4002 -4286
  50. data/spec/fixtures/detector/smartphone-10.yml +3771 -4763
  51. data/spec/fixtures/detector/smartphone-11.yml +3615 -4692
  52. data/spec/fixtures/detector/smartphone-12.yml +3856 -4764
  53. data/spec/fixtures/detector/smartphone-13.yml +4213 -4713
  54. data/spec/fixtures/detector/smartphone-14.yml +4039 -4497
  55. data/spec/fixtures/detector/smartphone-15.yml +5642 -2956
  56. data/spec/fixtures/detector/smartphone-16.yml +4739 -5082
  57. data/spec/fixtures/detector/smartphone-17.yml +4832 -4275
  58. data/spec/fixtures/detector/smartphone-18.yml +9806 -0
  59. data/spec/fixtures/detector/smartphone-19.yml +9965 -0
  60. data/spec/fixtures/detector/smartphone-2.yml +4842 -2589
  61. data/spec/fixtures/detector/smartphone-20.yml +9710 -0
  62. data/spec/fixtures/detector/smartphone-21.yml +8693 -0
  63. data/spec/fixtures/detector/smartphone-22.yml +10178 -0
  64. data/spec/fixtures/detector/smartphone-23.yml +9453 -0
  65. data/spec/fixtures/detector/smartphone-24.yml +9843 -0
  66. data/spec/fixtures/detector/smartphone-25.yml +9703 -0
  67. data/spec/fixtures/detector/smartphone-26.yml +10007 -0
  68. data/spec/fixtures/detector/smartphone-27.yml +4927 -0
  69. data/spec/fixtures/detector/smartphone-3.yml +4387 -4427
  70. data/spec/fixtures/detector/smartphone-4.yml +3597 -4582
  71. data/spec/fixtures/detector/smartphone-5.yml +4066 -5022
  72. data/spec/fixtures/detector/smartphone-6.yml +3455 -4621
  73. data/spec/fixtures/detector/smartphone-7.yml +3574 -4574
  74. data/spec/fixtures/detector/smartphone-8.yml +4617 -4704
  75. data/spec/fixtures/detector/smartphone-9.yml +4080 -5035
  76. data/spec/fixtures/detector/smartphone.yml +3244 -4234
  77. data/spec/fixtures/detector/tablet-1.yml +4652 -4492
  78. data/spec/fixtures/detector/tablet-2.yml +3515 -4434
  79. data/spec/fixtures/detector/tablet-3.yml +3418 -4351
  80. data/spec/fixtures/detector/tablet-4.yml +5149 -3200
  81. data/spec/fixtures/detector/tablet-5.yml +9273 -0
  82. data/spec/fixtures/detector/tablet-6.yml +4588 -0
  83. data/spec/fixtures/detector/tablet.yml +1621 -2613
  84. data/spec/fixtures/detector/tv-1.yml +2501 -0
  85. data/spec/fixtures/detector/tv.yml +7826 -3114
  86. data/spec/fixtures/detector/unknown.yml +370 -531
  87. data/spec/fixtures/detector/wearable.yml +863 -9
  88. data/spec/fixtures/parser/oss.yml +1350 -21
  89. data/spec/fixtures/parser/vendorfragments.yml +53 -53
  90. metadata +35 -5
@@ -30,7 +30,7 @@ class DeviceDetector
30
30
  def os_info
31
31
  from_cache(['os_info', self.class.name, user_agent]) do
32
32
  os_name = NameExtractor.new(user_agent, regex_meta).call
33
- if os_name && short = DOWNCASED_OPERATING_SYSTEMS[os_name.downcase]
33
+ if os_name && (short = DOWNCASED_OPERATING_SYSTEMS[os_name.downcase])
34
34
  os_name = OPERATING_SYSTEMS[short]
35
35
  else
36
36
  short = 'UNK'
@@ -50,7 +50,7 @@ class DeviceDetector
50
50
  'AIX' => 'AIX',
51
51
  'AND' => 'Android',
52
52
  'AMG' => 'AmigaOS',
53
- 'ATV' => 'Apple TV',
53
+ 'ATV' => 'tvOS',
54
54
  'ARL' => 'Arch Linux',
55
55
  'BTR' => 'BackTrack',
56
56
  'SBA' => 'Bada',
@@ -58,29 +58,42 @@ class DeviceDetector
58
58
  'BLB' => 'BlackBerry OS',
59
59
  'QNX' => 'BlackBerry Tablet OS',
60
60
  'BMP' => 'Brew',
61
+ 'CAI' => 'Caixa Mágica',
61
62
  'CES' => 'CentOS',
62
63
  'COS' => 'Chrome OS',
63
64
  'CYN' => 'CyanogenMod',
64
65
  'DEB' => 'Debian',
66
+ 'DEE' => 'Deepin',
65
67
  'DFB' => 'DragonFly',
68
+ 'DVK' => 'DVKBuntu',
66
69
  'FED' => 'Fedora',
70
+ 'FEN' => 'Fenix',
67
71
  'FOS' => 'Firefox OS',
68
72
  'FIR' => 'Fire OS',
73
+ 'FRE' => 'Freebox',
69
74
  'BSD' => 'FreeBSD',
75
+ 'FYD' => 'FydeOS',
70
76
  'GNT' => 'Gentoo',
77
+ 'GRI' => 'GridOS',
71
78
  'GTV' => 'Google TV',
72
79
  'HPX' => 'HP-UX',
73
80
  'HAI' => 'Haiku OS',
81
+ 'IPA' => 'iPadOS',
82
+ 'HAR' => 'HarmonyOS',
83
+ 'HAS' => 'HasCodingOS',
74
84
  'IRI' => 'IRIX',
75
85
  'INF' => 'Inferno',
86
+ 'JME' => 'Java ME',
76
87
  'KOS' => 'KaiOS',
77
88
  'KNO' => 'Knoppix',
78
89
  'KBT' => 'Kubuntu',
79
90
  'LIN' => 'GNU/Linux',
80
91
  'LBT' => 'Lubuntu',
92
+ 'LOS' => 'Lumin OS',
81
93
  'VLN' => 'VectorLinux',
82
94
  'MAC' => 'Mac',
83
95
  'MAE' => 'Maemo',
96
+ 'MAG' => 'Mageia',
84
97
  'MDR' => 'Mandriva',
85
98
  'SMG' => 'MeeGo',
86
99
  'MCD' => 'MocorDroid',
@@ -89,21 +102,26 @@ class DeviceDetector
89
102
  'MOR' => 'MorphOS',
90
103
  'NBS' => 'NetBSD',
91
104
  'MTK' => 'MTK / Nucleus',
105
+ 'MRE' => 'MRE',
92
106
  'WII' => 'Nintendo',
93
107
  'NDS' => 'Nintendo Mobile',
94
108
  'OS2' => 'OS/2',
95
109
  'T64' => 'OSF1',
96
110
  'OBS' => 'OpenBSD',
97
111
  'ORD' => 'Ordissimo',
112
+ 'PCL' => 'PCLinuxOS',
98
113
  'PSP' => 'PlayStation Portable',
99
114
  'PS3' => 'PlayStation',
100
115
  'RHT' => 'Red Hat',
101
116
  'ROS' => 'RISC OS',
117
+ 'RSO' => 'Rosa',
102
118
  'REM' => 'Remix OS',
119
+ 'REX' => 'REX',
103
120
  'RZD' => 'RazoDroiD',
104
121
  'SAB' => 'Sabayon',
105
122
  'SSE' => 'SUSE',
106
123
  'SAF' => 'Sailfish OS',
124
+ 'SEE' => 'SeewoOS',
107
125
  'SLW' => 'Slackware',
108
126
  'SOS' => 'Solaris',
109
127
  'SYL' => 'Syllable',
@@ -116,7 +134,9 @@ class DeviceDetector
116
134
  'TIZ' => 'Tizen',
117
135
  'TOS' => 'TmaxOS',
118
136
  'UBT' => 'Ubuntu',
137
+ 'WAS' => 'watchOS',
119
138
  'WTV' => 'WebTV',
139
+ 'WHS' => 'Whale OS',
120
140
  'WIN' => 'Windows',
121
141
  'WCE' => 'Windows CE',
122
142
  'WIO' => 'Windows IoT',
@@ -133,37 +153,42 @@ class DeviceDetector
133
153
 
134
154
  DOWNCASED_OPERATING_SYSTEMS = OPERATING_SYSTEMS.each_with_object({}) do |(short, long), h|
135
155
  h[long.downcase] = short
136
- end
156
+ end.freeze
137
157
 
138
158
  OS_FAMILIES = {
139
- 'Android' => %w[AND CYN FIR REM RZD MLD MCD YNS],
159
+ 'Android' => %w[AND CYN FIR REM RZD MLD MCD YNS GRI HAR],
140
160
  'AmigaOS' => %w[AMG MOR],
141
- 'Apple TV' => ['ATV'],
142
161
  'BlackBerry' => %w[BLB QNX],
143
162
  'Brew' => ['BMP'],
144
163
  'BeOS' => %w[BEO HAI],
145
- 'Chrome OS' => ['COS'],
164
+ 'Chrome OS' => %w[COS FYD SEE],
146
165
  'Firefox OS' => %w[FOS KOS],
147
166
  'Gaming Console' => %w[WII PS3],
148
167
  'Google TV' => ['GTV'],
149
168
  'IBM' => ['OS2'],
150
- 'iOS' => ['IOS'],
169
+ 'iOS' => %w[IOS ATV WAS IPA],
151
170
  'RISC OS' => ['ROS'],
152
- 'GNU/Linux' => %w[LIN ARL DEB KNO MIN UBT KBT XBT LBT FED RHT VLN MDR GNT SAB SLW SSE CES BTR SAF ORD TOS],
171
+ 'GNU/Linux' => %w[
172
+ LIN ARL DEB KNO MIN UBT KBT XBT LBT FED
173
+ RHT VLN MDR GNT SAB SLW SSE CES BTR SAF
174
+ ORD TOS RSO DEE FRE MAG FEN CAI PCL HAS
175
+ LOS DVK
176
+ ],
153
177
  'Mac' => ['MAC'],
154
178
  'Mobile Gaming Console' => %w[PSP NDS XBX],
155
- 'Real-time OS' => %w[MTK TDX],
179
+ 'Real-time OS' => %w[MTK TDX MRE JME REX],
156
180
  'Other Mobile' => %w[WOS POS SBA TIZ SMG MAE],
157
181
  'Symbian' => %w[SYM SYS SY3 S60 S40],
158
182
  'Unix' => %w[SOS AIX HPX BSD NBS OBS DFB SYL IRI T64 INF],
159
183
  'WebTV' => ['WTV'],
160
184
  'Windows' => ['WIN'],
161
- 'Windows Mobile' => %w[WPH WMO WCE WRT WIO]
185
+ 'Windows Mobile' => %w[WPH WMO WCE WRT WIO],
186
+ 'Other Smart TV' => ['WHS']
162
187
  }.freeze
163
188
 
164
189
  FAMILY_TO_OS = OS_FAMILIES.each_with_object({}) do |(family, oss), h|
165
190
  oss.each { |os| h[os] = family }
166
- end
191
+ end.freeze
167
192
 
168
193
  def filenames
169
194
  ['oss.yml']
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class DeviceDetector
4
- VERSION = '1.0.5'
4
+ VERSION = '1.0.6'
5
5
  end
@@ -2,6 +2,15 @@
2
2
 
3
3
  class DeviceDetector
4
4
  class VersionExtractor < MetadataExtractor
5
+ MAJOR_VERSION_2 = Gem::Version.new('2.0')
6
+ MAJOR_VERSION_3 = Gem::Version.new('3.0')
7
+ MAJOR_VERSION_4 = Gem::Version.new('4.0')
8
+ MAJOR_VERSION_8 = Gem::Version.new('8.0')
9
+
10
+ def call
11
+ super&.chomp('.')
12
+ end
13
+
5
14
  private
6
15
 
7
16
  def metadata_string
@@ -13,6 +13,7 @@ require 'device_detector/bot'
13
13
  require 'device_detector/client'
14
14
  require 'device_detector/device'
15
15
  require 'device_detector/os'
16
+ require 'device_detector/browser'
16
17
 
17
18
  class DeviceDetector
18
19
  attr_reader :user_agent
@@ -46,7 +47,10 @@ class DeviceDetector
46
47
  end
47
48
 
48
49
  def device_brand
49
- device.brand
50
+ # Assume all devices running iOS / Mac OS are from Apple
51
+ brand = device.brand
52
+ brand = 'Apple' if brand.nil? && ['Apple TV', 'iOS', 'Mac'].include?(os_name)
53
+ brand
50
54
  end
51
55
 
52
56
  def device_type
@@ -59,15 +63,19 @@ class DeviceDetector
59
63
  # that won't have a detected browser, but can still be detected. So we check the useragent for
60
64
  # Chrome instead.
61
65
  if t.nil? && os.family == 'Android' && user_agent =~ build_regex('Chrome\/[\.0-9]*')
62
- if user_agent =~ build_regex('Chrome\/[\.0-9]* Mobile')
66
+ if user_agent =~ build_regex('(?:Mobile|eliboM) Safari\/')
63
67
  t = 'smartphone'
64
- elsif user_agent =~ build_regex('Chrome\/[\.0-9]* (?!Mobile)')
68
+ elsif user_agent =~ build_regex('(?!Mobile )Safari\/')
65
69
  t = 'tablet'
66
70
  end
67
71
  end
68
72
 
73
+ # Some UA contain the fragment 'Android; Tablet;' or 'Opera Tablet', so we assume those devices
74
+ # as tablets
69
75
  t = 'tablet' if t.nil? && android_tablet_fragment? || opera_tablet?
70
76
 
77
+ # Some user agents simply contain the fragment 'Android; Mobile;', so we assume those devices
78
+ # as smartphones
71
79
  t = 'smartphone' if t.nil? && android_mobile_fragment?
72
80
 
73
81
  # Android up to 3.0 was designed for smartphones only. But as 3.0,
@@ -78,10 +86,12 @@ class DeviceDetector
78
86
  # So were are expecting that all devices running Android < 2 are
79
87
  # smartphones Devices running Android 3.X are tablets. Device type
80
88
  # of Android 2.X and 4.X+ are unknown
81
- if t.nil? && os.short_name == 'AND' && os.full_version && !os.full_version.empty?
82
- if os.full_version < '2'
89
+ if t.nil? && os_name == 'Android' && os.full_version && !os.full_version.empty?
90
+ full_version = Gem::Version.new(os.full_version)
91
+ if full_version < VersionExtractor::MAJOR_VERSION_2
83
92
  t = 'smartphone'
84
- elsif os.full_version >= '3' && os.full_version < '4'
93
+ elsif full_version >= VersionExtractor::MAJOR_VERSION_3 && \
94
+ full_version < VersionExtractor::MAJOR_VERSION_4
85
95
  t = 'tablet'
86
96
  end
87
97
  end
@@ -89,6 +99,9 @@ class DeviceDetector
89
99
  # All detected feature phones running android are more likely a smartphone
90
100
  t = 'smartphone' if t == 'feature phone' && os.family == 'Android'
91
101
 
102
+ # All unknown devices under running Java ME are more likely a features phones
103
+ t = 'feature phone' if t.nil? && os_name == 'Java ME'
104
+
92
105
  # According to http://msdn.microsoft.com/en-us/library/ie/hh920767(v=vs.85).aspx
93
106
  # Internet Explorer 10 introduces the "Touch" UA string token. If this token is present at the
94
107
  # end of the UA string, the computer has touch capability, and is running Windows 8 (or later).
@@ -97,19 +110,29 @@ class DeviceDetector
97
110
  # As most touch enabled devices are tablets and only a smaller part are desktops/notebooks we
98
111
  # assume that all Windows 8 touch devices are tablets.
99
112
  if t.nil? && touch_enabled? &&
100
- (os.short_name == 'WRT' || (os.short_name == 'WIN' && os.full_version && os.full_version >= '8'))
113
+ (os_name == 'Windows RT' ||
114
+ (os_name == 'Windows' && os.full_version &&
115
+ Gem::Version.new(os.full_version) >= VersionExtractor::MAJOR_VERSION_8))
101
116
  t = 'tablet'
102
117
  end
103
118
 
119
+ # All devices running Opera TV Store are assumed to be a tv
104
120
  t = 'tv' if opera_tv_store?
105
121
 
122
+ # All devices running Tizen TV or SmartTV are assumed to be a tv
123
+ t = 'tv' if t.nil? && tizen_samsung_tv?
124
+
125
+ # Devices running Kylo or Espital TV Browsers are assumed to be a TV
106
126
  t = 'tv' if t.nil? && ['Kylo', 'Espial TV Browser'].include?(client.name)
107
127
 
108
- # set device type to desktop for all devices running a desktop os that were
109
- # not detected as an other device type
110
- t = 'desktop' if t.nil? && os.desktop? && !puffin_browser?
128
+ has_desktop = t != 'desktop' && desktop_string? && desktop_fragment?
129
+ t = 'desktop' if has_desktop
111
130
 
112
- t
131
+ # set device type to desktop for all devices running a desktop os that were not detected as
132
+ # another device type
133
+ return t if t || !desktop?
134
+
135
+ 'desktop'
113
136
  end
114
137
 
115
138
  def known?
@@ -175,6 +198,10 @@ class DeviceDetector
175
198
  user_agent =~ build_regex('Android(?: \d.\d(?:.\d)?)?; Mobile;')
176
199
  end
177
200
 
201
+ def desktop_fragment?
202
+ user_agent =~ build_regex('Desktop (x(?:32|64)|WOW64);')
203
+ end
204
+
178
205
  def touch_enabled?
179
206
  user_agent =~ build_regex('Touch')
180
207
  end
@@ -187,9 +214,26 @@ class DeviceDetector
187
214
  user_agent =~ build_regex('Opera Tablet')
188
215
  end
189
216
 
217
+ def tizen_samsung_tv?
218
+ user_agent =~ build_regex('SmartTV|Tizen.+ TV .+$')
219
+ end
220
+
221
+ def uses_mobile_browser?
222
+ client.browser? && client.mobile_only_browser?
223
+ end
224
+
190
225
  # This is a workaround until we support detecting mobile only browsers
191
- def puffin_browser?
192
- client.name == 'Puffin'
226
+ def desktop_string?
227
+ user_agent =~ /Desktop/
228
+ end
229
+
230
+ def desktop?
231
+ return false if os_name.nil? || os_name == '' || os_name == 'UNK'
232
+
233
+ # Check for browsers available for mobile devices only
234
+ return false if uses_mobile_browser?
235
+
236
+ os.desktop?
193
237
  end
194
238
 
195
239
  def build_regex(src)