device_detector 1.1.2 → 1.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -4,6 +4,16 @@ require 'set'
4
4
 
5
5
  class DeviceDetector
6
6
  class OS < Parser
7
+ class << self
8
+ def mapped_os_version(version, mapping)
9
+ return if version.nil?
10
+
11
+ major_version = version.split('.').first
12
+
13
+ mapping[version] || mapping[major_version]
14
+ end
15
+ end
16
+
7
17
  def name
8
18
  os_info[:name]
9
19
  end
@@ -39,31 +49,41 @@ class DeviceDetector
39
49
  end
40
50
  end
41
51
 
52
+ # https://github.com/matomo-org/device-detector/blob/75d88bbefb0182f9207c9f48dc39b1bc8c7cc43f/Parser/OperatingSystem.php#L286-L288
42
53
  DESKTOP_OSS = Set.new(
43
54
  [
44
- 'AmigaOS', 'IBM', 'GNU/Linux', 'Mac', 'Unix', 'Windows', 'BeOS', 'Chrome OS'
55
+ 'AmigaOS', 'IBM', 'GNU/Linux', 'Mac', 'Unix', 'Windows', 'BeOS', 'Chrome OS', 'Chromium OS'
45
56
  ]
46
57
  )
47
58
 
48
59
  # OS short codes mapped to long names
60
+ # https://github.com/matomo-org/device-detector/blob/75d88bbefb0182f9207c9f48dc39b1bc8c7cc43f/Parser/OperatingSystem.php#L42-L220
49
61
  OPERATING_SYSTEMS = {
50
62
  'AIX' => 'AIX',
51
63
  'AND' => 'Android',
52
64
  'ADR' => 'Android TV',
65
+ 'ALP' => 'Alpine Linux',
53
66
  'AMZ' => 'Amazon Linux',
54
67
  'AMG' => 'AmigaOS',
68
+ 'ARM' => 'Armadillo OS',
69
+ 'ARO' => 'AROS',
55
70
  'ATV' => 'tvOS',
56
71
  'ARL' => 'Arch Linux',
72
+ 'AOS' => 'AOSC OS',
73
+ 'ASP' => 'ASPLinux',
57
74
  'BTR' => 'BackTrack',
58
75
  'SBA' => 'Bada',
76
+ 'BYI' => 'Baidu Yi',
59
77
  'BEO' => 'BeOS',
60
78
  'BLB' => 'BlackBerry OS',
61
79
  'QNX' => 'BlackBerry Tablet OS',
62
80
  'BOS' => 'Bliss OS',
63
81
  'BMP' => 'Brew',
82
+ 'BSN' => 'BrightSignOS',
64
83
  'CAI' => 'Caixa Mágica',
65
84
  'CES' => 'CentOS',
66
85
  'CST' => 'CentOS Stream',
86
+ 'CLO' => 'Clear Linux OS',
67
87
  'CLR' => 'ClearOS Mobile',
68
88
  'COS' => 'Chrome OS',
69
89
  'CRS' => 'Chromium OS',
@@ -73,6 +93,8 @@ class DeviceDetector
73
93
  'DEE' => 'Deepin',
74
94
  'DFB' => 'DragonFly',
75
95
  'DVK' => 'DVKBuntu',
96
+ 'ELE' => 'ElectroBSD',
97
+ 'EUL' => 'EulerOS',
76
98
  'FED' => 'Fedora',
77
99
  'FEN' => 'Fenix',
78
100
  'FOS' => 'Firefox OS',
@@ -80,9 +102,13 @@ class DeviceDetector
80
102
  'FOR' => 'Foresight Linux',
81
103
  'FRE' => 'Freebox',
82
104
  'BSD' => 'FreeBSD',
105
+ 'FRI' => 'FRITZ!OS',
83
106
  'FYD' => 'FydeOS',
84
107
  'FUC' => 'Fuchsia',
85
108
  'GNT' => 'Gentoo',
109
+ 'GNX' => 'GENIX',
110
+ 'GEO' => 'GEOS',
111
+ 'GNS' => 'gNewSense',
86
112
  'GRI' => 'GridOS',
87
113
  'GTV' => 'Google TV',
88
114
  'HPX' => 'HP-UX',
@@ -90,12 +116,15 @@ class DeviceDetector
90
116
  'IPA' => 'iPadOS',
91
117
  'HAR' => 'HarmonyOS',
92
118
  'HAS' => 'HasCodingOS',
119
+ 'HEL' => 'HELIX OS',
93
120
  'IRI' => 'IRIX',
94
121
  'INF' => 'Inferno',
95
122
  'JME' => 'Java ME',
123
+ 'JOL' => 'Joli OS',
96
124
  'KOS' => 'KaiOS',
97
125
  'KAL' => 'Kali',
98
126
  'KAN' => 'Kanotix',
127
+ 'KIN' => 'KIN OS',
99
128
  'KNO' => 'Knoppix',
100
129
  'KTV' => 'KreaTV',
101
130
  'KBT' => 'Kubuntu',
@@ -103,6 +132,8 @@ class DeviceDetector
103
132
  'LND' => 'LindowsOS',
104
133
  'LNS' => 'Linspire',
105
134
  'LEN' => 'Lineage OS',
135
+ 'LIR' => 'Liri OS',
136
+ 'LOO' => 'Loongnix',
106
137
  'LBT' => 'Lubuntu',
107
138
  'LOS' => 'Lumin OS',
108
139
  'LUN' => 'LuneOS',
@@ -114,18 +145,23 @@ class DeviceDetector
114
145
  'SMG' => 'MeeGo',
115
146
  'MCD' => 'MocorDroid',
116
147
  'MON' => 'moonOS',
148
+ 'EZX' => 'Motorola EZX',
117
149
  'MIN' => 'Mint',
118
150
  'MLD' => 'MildWild',
119
151
  'MOR' => 'MorphOS',
120
152
  'NBS' => 'NetBSD',
121
153
  'MTK' => 'MTK / Nucleus',
122
154
  'MRE' => 'MRE',
155
+ 'NXT' => 'NeXTSTEP',
156
+ 'NWS' => 'NEWS-OS',
123
157
  'WII' => 'Nintendo',
124
158
  'NDS' => 'Nintendo Mobile',
125
159
  'NOV' => 'Nova',
126
160
  'OS2' => 'OS/2',
127
161
  'T64' => 'OSF1',
128
162
  'OBS' => 'OpenBSD',
163
+ 'OVS' => 'OpenVMS',
164
+ 'OVZ' => 'OpenVZ',
129
165
  'OWR' => 'OpenWrt',
130
166
  'OTV' => 'Opera TV',
131
167
  'ORA' => 'Oracle Linux',
@@ -136,11 +172,17 @@ class DeviceDetector
136
172
  'PLA' => 'Plasma Mobile',
137
173
  'PSP' => 'PlayStation Portable',
138
174
  'PS3' => 'PlayStation',
175
+ 'PVE' => 'Proxmox VE',
139
176
  'PUR' => 'PureOS',
177
+ 'QTP' => 'Qtopia',
178
+ 'PIO' => 'Raspberry Pi OS',
179
+ 'RAS' => 'Raspbian',
140
180
  'RHT' => 'Red Hat',
181
+ 'RST' => 'Red Star',
141
182
  'RED' => 'RedOS',
142
183
  'REV' => 'Revenge OS',
143
184
  'ROS' => 'RISC OS',
185
+ 'ROC' => 'Rocky Linux',
144
186
  'ROK' => 'Roku OS',
145
187
  'RSO' => 'Rosa',
146
188
  'ROU' => 'RouterOS',
@@ -151,10 +193,13 @@ class DeviceDetector
151
193
  'SAB' => 'Sabayon',
152
194
  'SSE' => 'SUSE',
153
195
  'SAF' => 'Sailfish OS',
196
+ 'SCI' => 'Scientific Linux',
154
197
  'SEE' => 'SeewoOS',
198
+ 'SER' => 'SerenityOS',
155
199
  'SIR' => 'Sirin OS',
156
200
  'SLW' => 'Slackware',
157
201
  'SOS' => 'Solaris',
202
+ 'SBL' => 'Star-Blade OS',
158
203
  'SYL' => 'Syllable',
159
204
  'SYM' => 'Symbian',
160
205
  'SYS' => 'Symbian OS',
@@ -166,7 +211,10 @@ class DeviceDetector
166
211
  'TIZ' => 'Tizen',
167
212
  'TIV' => 'TiVo OS',
168
213
  'TOS' => 'TmaxOS',
214
+ 'TUR' => 'Turbolinux',
169
215
  'UBT' => 'Ubuntu',
216
+ 'ULT' => 'ULTRIX',
217
+ 'UOS' => 'UOS',
170
218
  'VID' => 'VIDAA',
171
219
  'WAS' => 'watchOS',
172
220
  'WER' => 'Wear OS',
@@ -178,6 +226,7 @@ class DeviceDetector
178
226
  'WMO' => 'Windows Mobile',
179
227
  'WPH' => 'Windows Phone',
180
228
  'WRT' => 'Windows RT',
229
+ 'WPO' => 'WoPhone',
181
230
  'XBX' => 'Xbox',
182
231
  'XBT' => 'Xubuntu',
183
232
  'YNS' => 'YunOS',
@@ -185,6 +234,7 @@ class DeviceDetector
185
234
  'ZOR' => 'ZorinOS',
186
235
  'IOS' => 'iOS',
187
236
  'POS' => 'palmOS',
237
+ 'WEB' => 'Webian',
188
238
  'WOS' => 'webOS'
189
239
  }.freeze
190
240
 
@@ -192,10 +242,14 @@ class DeviceDetector
192
242
  h[long.downcase] = short
193
243
  end.freeze
194
244
 
245
+ APPLE_OS_NAMES = Set.new(%w[iPadOS tvOS watchOS iOS Mac]).freeze
246
+
247
+ # https://github.com/matomo-org/device-detector/blob/75d88bbefb0182f9207c9f48dc39b1bc8c7cc43f/Parser/OperatingSystem.php#L227-L269
195
248
  OS_FAMILIES = {
196
249
  'Android' => %w[ AND CYN FIR REM RZD MLD MCD YNS GRI HAR
197
- ADR CLR BOS REV LEN SIR RRS WER PIC],
198
- 'AmigaOS' => %w[AMG MOR],
250
+ ADR CLR BOS REV LEN SIR RRS WER PIC ARM
251
+ HEL BYI],
252
+ 'AmigaOS' => %w[AMG MOR ARO],
199
253
  'BlackBerry' => %w[BLB QNX],
200
254
  'Brew' => ['BMP'],
201
255
  'BeOS' => %w[BEO HAI],
@@ -212,17 +266,24 @@ class DeviceDetector
212
266
  ORD TOS RSO DEE FRE MAG FEN CAI PCL HAS
213
267
  LOS DVK ROK OWR OTV KTV PUR PLA FUC PAR
214
268
  FOR MON KAN ZEN LND LNS CHN AMZ TEN CST
215
- NOV ROU ZOR VID
269
+ NOV ROU ZOR RED KAL ORA VID TIV BSN RAS
270
+ UOS PIO FRI LIR WEB SER ASP AOS LOO EUL
271
+ SCI ALP CLO ROC OVZ PVE RST EZX GNS JOL
272
+ TUR QTP WPO
216
273
  ],
217
274
  'Mac' => ['MAC'],
218
275
  'Mobile Gaming Console' => %w[PSP NDS XBX],
276
+ 'OpenVMS' => ['OVS'],
219
277
  'Real-time OS' => %w[MTK TDX MRE JME REX],
220
- 'Other Mobile' => %w[WOS POS SBA TIZ SMG MAE],
278
+ 'Other Mobile' => %w[WOS POS SBA TIZ SMG MAE LUN GEO],
221
279
  'Symbian' => %w[SYM SYS SY3 S60 S40],
222
- 'Unix' => %w[SOS AIX HPX BSD NBS OBS DFB SYL IRI T64 INF],
280
+ 'Unix' => %w[
281
+ SOS AIX HPX BSD NBS OBS DFB SYL IRI T64
282
+ INF ELE GNX ULT NWS NXT SBL
283
+ ],
223
284
  'WebTV' => ['WTV'],
224
285
  'Windows' => ['WIN'],
225
- 'Windows Mobile' => %w[WPH WMO WCE WRT WIO],
286
+ 'Windows Mobile' => %w[WPH WMO WCE WRT WIO KIN],
226
287
  'Other Smart TV' => ['WHS']
227
288
  }.freeze
228
289
 
@@ -230,6 +291,47 @@ class DeviceDetector
230
291
  oss.each { |os| h[os] = family }
231
292
  end.freeze
232
293
 
294
+ # https://github.com/matomo-org/device-detector/blob/75d88bbefb0182f9207c9f48dc39b1bc8c7cc43f/Parser/OperatingSystem.php#L295-L308
295
+ FIRE_OS_VERSION_MAPPING = {
296
+ '11' => '8',
297
+ '10' => '8',
298
+ '9' => '7',
299
+ '7' => '6',
300
+ '5' => '5',
301
+ '4.4.3' => '4.5.1',
302
+ '4.4.2' => '4',
303
+ '4.2.2' => '3',
304
+ '4.0.3' => '3',
305
+ '4.0.2' => '3',
306
+ '4' => '2',
307
+ '2' => '1',
308
+ }.freeze
309
+
310
+ # https://github.com/matomo-org/device-detector/blob/75d88bbefb0182f9207c9f48dc39b1bc8c7cc43f/Parser/OperatingSystem.php#L315-L337
311
+ LINEAGE_OS_VERSION_MAPPING = {
312
+ '14' => '21',
313
+ '13' => '20.0',
314
+ '12.1' => '19.1',
315
+ '12' => '19.0',
316
+ '11' => '18.0',
317
+ '10' => '17.0',
318
+ '9' => '16.0',
319
+ '8.1.0' => '15.1',
320
+ '8.0.0' => '15.0',
321
+ '7.1.2' => '14.1',
322
+ '7.1.1' => '14.1',
323
+ '7.0' => '14.0',
324
+ '6.0.1' => '13.0',
325
+ '6.0' => '13.0',
326
+ '5.1.1' => '12.1',
327
+ '5.0.2' => '12.0',
328
+ '5.0' => '12.0',
329
+ '4.4.4' => '11.0',
330
+ '4.3' => '10.2',
331
+ '4.2.2' => '10.1',
332
+ '4.0.4' => '9.1.0'
333
+ }.freeze
334
+
233
335
  def filenames
234
336
  ['oss.yml']
235
337
  end
@@ -60,18 +60,18 @@ class DeviceDetector
60
60
  def load_regexes(file_paths)
61
61
  file_paths.map do |path, full_path|
62
62
  object = YAML.load_file(full_path)
63
- object = rewrite_device_object!(object) if is_device_yml_file?(full_path)
64
- object = rewrite_vendor_object!(object) if is_vendor_yml_file?(full_path)
63
+ object = rewrite_device_object!(object) if device_yml_file?(full_path)
64
+ object = rewrite_vendor_object!(object) if vendor_yml_file?(full_path)
65
65
 
66
66
  [path, symbolize_keys!(object)]
67
67
  end
68
68
  end
69
69
 
70
- def is_device_yml_file?(file_path)
70
+ def device_yml_file?(file_path)
71
71
  file_path.include?('/regexes/device/')
72
72
  end
73
73
 
74
- def is_vendor_yml_file?(file_path)
74
+ def vendor_yml_file?(file_path)
75
75
  file_path.include?('/regexes/vendorfragments')
76
76
  end
77
77
 
@@ -101,13 +101,14 @@ class DeviceDetector
101
101
  raise "invalid device spec: #{meta.inspect}" unless meta[:regex].is_a? String
102
102
 
103
103
  meta[:regex] = build_regex(meta[:regex])
104
+ meta[:versions].each { |v| v[:regex] = build_regex(v[:regex]) } if meta.key?(:versions)
104
105
  meta[:path] = path
105
106
  meta
106
107
  end
107
108
  end
108
109
 
109
110
  def build_regex(src)
110
- Regexp.new('(?:^|[^A-Z0-9\-_]|[^A-Z0-9\-]_|sprd-|MZ-)(?:' + src + ')', Regexp::IGNORECASE)
111
+ Regexp.new("(?:^|[^A-Z0-9_-]|[^A-Z0-9-]_|sprd-|MZ-)(?:#{src})", Regexp::IGNORECASE)
111
112
  end
112
113
 
113
114
  def from_cache(key, &block)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class DeviceDetector
4
- VERSION = '1.1.2'
4
+ VERSION = '1.1.3'
5
5
  end
@@ -23,11 +23,11 @@ class DeviceDetector
23
23
  def initialize(user_agent, headers = nil)
24
24
  @client_hint = ClientHint.new(headers)
25
25
  utf8_user_agent = encode_user_agent_if_needed(user_agent)
26
- @user_agent = set_user_agent(utf8_user_agent)
26
+ @user_agent = build_user_agent(utf8_user_agent)
27
27
  end
28
28
 
29
- # https://github.com/matomo-org/device-detector/blob/c235832dba13961ab0f71b681616baf1aa48de23/Parser/Device/AbstractDeviceParser.php#L1873
30
- def set_user_agent(user_agent)
29
+ # https://github.com/matomo-org/device-detector/blob/a2535ff3b63e4187f1d3440aed24ff43d74fb7f1/Parser/Device/AbstractDeviceParser.php#L2065-L2073
30
+ def build_user_agent(user_agent)
31
31
  return user_agent if client_hint.model.nil?
32
32
 
33
33
  regex = build_regex('Android 10[.\d]*; K(?: Build/|[;)])')
@@ -35,7 +35,7 @@ class DeviceDetector
35
35
 
36
36
  version = client_hint.os_version || '10'
37
37
 
38
- user_agent.gsub(regex, "Android #{version}, #{client_hint.model}")
38
+ user_agent.gsub(/(Android 10[.\d]*; K)/, "Android #{version}; #{client_hint.model}")
39
39
  end
40
40
 
41
41
  def encode_user_agent_if_needed(user_agent)
@@ -52,7 +52,7 @@ class DeviceDetector
52
52
  end
53
53
 
54
54
  def full_version
55
- client_hint.platform_version || client.full_version
55
+ client_hint.full_version || client.full_version
56
56
  end
57
57
 
58
58
  def os_family
@@ -69,6 +69,8 @@ class DeviceDetector
69
69
 
70
70
  def os_full_version
71
71
  return if skip_os_version?
72
+ return os.full_version if pico_os_fix?
73
+ return fire_os_version if fire_os_fix?
72
74
 
73
75
  client_hint.os_version || os.full_version
74
76
  end
@@ -84,7 +86,7 @@ class DeviceDetector
84
86
 
85
87
  # Assume all devices running iOS / Mac OS are from Apple
86
88
  brand = device.brand
87
- brand = 'Apple' if brand.nil? && %w[iPadOS tvOS watchOS iOS Mac].include?(os_name)
89
+ brand = 'Apple' if brand.nil? && DeviceDetector::OS::APPLE_OS_NAMES.include?(os_name)
88
90
 
89
91
  brand
90
92
  end
@@ -92,6 +94,8 @@ class DeviceDetector
92
94
  def device_type
93
95
  t = device.type
94
96
 
97
+ t = nil if fake_ua?
98
+
95
99
  # Chrome on Android passes the device type based on the keyword 'Mobile'
96
100
  # If it is present the device should be a smartphone, otherwise it's a tablet
97
101
  # See https://developer.chrome.com/multidevice/user-agent#chrome_for_android_user_agent
@@ -99,21 +103,23 @@ class DeviceDetector
99
103
  # that won't have a detected browser, but can still be detected. So we check the useragent for
100
104
  # Chrome instead.
101
105
  if t.nil? && os_family == 'Android' && user_agent =~ build_regex('Chrome\/[\.0-9]*')
102
- if user_agent =~ build_regex('(?:Mobile|eliboM) Safari\/')
103
- t = 'smartphone'
104
- elsif user_agent =~ build_regex('(?!Mobile )Safari\/')
105
- t = 'tablet'
106
- end
106
+ t = user_agent =~ build_regex('(?:Mobile|eliboM)') ? 'smartphone' : 'tablet'
107
107
  end
108
108
 
109
+ # Some UA contain the fragment 'Pad/APad', so we assume those devices as tablets
110
+ t = 'tablet' if t == 'smartphone' && user_agent =~ build_regex('Pad\/APad')
111
+
109
112
  # Some UA contain the fragment 'Android; Tablet;' or 'Opera Tablet', so we assume those devices
110
113
  # as tablets
111
- t = 'tablet' if t.nil? && android_tablet_fragment? || opera_tablet?
114
+ t = 'tablet' if t.nil? && (android_tablet_fragment? || opera_tablet?)
112
115
 
113
116
  # Some user agents simply contain the fragment 'Android; Mobile;', so we assume those devices
114
117
  # as smartphones
115
118
  t = 'smartphone' if t.nil? && android_mobile_fragment?
116
119
 
120
+ # Some UA contains the 'Android; Mobile VR;' fragment
121
+ t = 'wearable' if t.nil? && android_vr_fragment?
122
+
117
123
  # Android up to 3.0 was designed for smartphones only. But as 3.0,
118
124
  # which was tablet only, was published too late, there were a
119
125
  # bunch of tablets running with 2.x With 4.0 the two trees were
@@ -156,14 +162,18 @@ class DeviceDetector
156
162
  t = 'tv' if opera_tv_store?
157
163
 
158
164
  # All devices that contain Andr0id in string are assumed to be a tv
159
- t = 'tv' if user_agent =~ build_regex('Andr0id|Android TV')
165
+ if user_agent =~ build_regex('Andr0id|(?:Android(?: UHD)?|Google) TV|\(lite\) TV|BRAVIA')
166
+ t = 'tv'
167
+ end
160
168
 
161
169
  # All devices running Tizen TV or SmartTV are assumed to be a tv
162
170
  t = 'tv' if t.nil? && tizen_samsung_tv?
163
171
 
164
- # Devices running Kylo or Espital TV Browsers are assumed to be a TV
172
+ # Devices running those clients are assumed to be a TV
165
173
  t = 'tv' if ['Kylo', 'Espial TV Browser', 'LUJO TV Browser', 'LogicUI TV Browser',
166
- 'Open TV Browser'].include?(name)
174
+ 'Open TV Browser', 'Seraphic Sraf', 'Opera Devices', 'Crow Browser',
175
+ 'Vewd Browser', 'TiviMate', 'Quick Search TV', 'QJY TV Browser',
176
+ 'TV Bro'].include?(name)
167
177
 
168
178
  # All devices containing TV fragment are assumed to be a tv
169
179
  t = 'tv' if t.nil? && user_agent =~ build_regex('\(TV;')
@@ -233,9 +243,9 @@ class DeviceDetector
233
243
  @os ||= OS.new(user_agent)
234
244
  end
235
245
 
236
- # https://github.com/matomo-org/device-detector/blob/827a3fab7e38c3274c18d2f5f5bc2a78b7ef4a3a/DeviceDetector.php#L921C5-L921C5
246
+ # https://github.com/matomo-org/device-detector/blob/67ae11199a5129b42fa8b985d372ea834104fe3a/DeviceDetector.php#L931-L938
237
247
  def fake_ua?
238
- os_name == 'Android' && device.brand == 'Apple'
248
+ device.brand == 'Apple' && !DeviceDetector::OS::APPLE_OS_NAMES.include?(os_name)
239
249
  end
240
250
 
241
251
  # https://github.com/matomo-org/device-detector/blob/be1c9ef486c247dc4886668da5ed0b1c49d90ba8/Parser/Client/Browser.php#L772
@@ -245,7 +255,9 @@ class DeviceDetector
245
255
  end
246
256
 
247
257
  def linux_fix?
248
- client_hint.platform == 'Linux' && os.name == 'Android' && client_hint.mobile == '?0'
258
+ client_hint.platform == 'Linux' &&
259
+ %w[iOS Android].include?(os.name) &&
260
+ %w[?0 0].include?(client_hint.mobile)
249
261
  end
250
262
 
251
263
  # Related to issue mentionned in device.rb#1562
@@ -253,20 +265,41 @@ class DeviceDetector
253
265
  user_agent&.include?('X-music Ⅲ') ? 'X-Music III' : nil
254
266
  end
255
267
 
268
+ def pico_os_fix?
269
+ client_hint.os_name == 'Pico OS'
270
+ end
271
+
272
+ # https://github.com/matomo-org/device-detector/blob/323629cb679c8572a9745cba9c3803fee13f3cf6/Parser/OperatingSystem.php#L398-L403
273
+ def fire_os_fix?
274
+ !client_hint.platform.nil? && os.name == 'Fire OS'
275
+ end
276
+
277
+ def fire_os_version
278
+ DeviceDetector::OS
279
+ .mapped_os_version(client_hint.os_version, DeviceDetector::OS::FIRE_OS_VERSION_MAPPING)
280
+ end
281
+
282
+ # https://github.com/matomo-org/device-detector/blob/323629cb679c8572a9745cba9c3803fee13f3cf6/Parser/OperatingSystem.php#L378-L383
256
283
  def skip_os_version?
257
- !client_hint.os_family.nil? && client_hint.os_family != os.family
284
+ !client_hint.os_family.nil? &&
285
+ client_hint.os_version.nil? &&
286
+ client_hint.os_family != os.family
258
287
  end
259
288
 
260
289
  def android_tablet_fragment?
261
- user_agent =~ build_regex('Android( [\.0-9]+)?; Tablet;')
290
+ user_agent =~ build_regex('Android( [\.0-9]+)?; Tablet;|Tablet(?! PC)|.*\-tablet$')
262
291
  end
263
292
 
264
293
  def android_mobile_fragment?
265
- user_agent =~ build_regex('Android( [\.0-9]+)?; Mobile;')
294
+ user_agent =~ build_regex('Android( [\.0-9]+)?; Mobile;|.*\-mobile$')
295
+ end
296
+
297
+ def android_vr_fragment?
298
+ user_agent =~ build_regex('Android( [\.0-9]+)?; Mobile VR;| VR ')
266
299
  end
267
300
 
268
301
  def desktop_fragment?
269
- user_agent =~ build_regex('Desktop (x(?:32|64)|WOW64);')
302
+ user_agent =~ build_regex('Desktop(?: (x(?:32|64)|WOW64))?;')
270
303
  end
271
304
 
272
305
  def touch_enabled?
@@ -300,7 +333,7 @@ class DeviceDetector
300
333
  # Check for browsers available for mobile devices only
301
334
  return false if uses_mobile_browser?
302
335
 
303
- os.desktop?
336
+ DeviceDetector::OS::DESKTOP_OSS.include?(os_family)
304
337
  end
305
338
 
306
339
  def build_regex(src)