device_detector 1.0.0 → 1.1.3
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 +5 -5
- data/CHANGELOG.md +58 -4
- data/README.md +57 -21
- data/lib/device_detector/bot.rb +2 -2
- data/lib/device_detector/browser.rb +691 -0
- data/lib/device_detector/client.rb +11 -2
- data/lib/device_detector/client_hint.rb +249 -0
- data/lib/device_detector/device.rb +1954 -23
- data/lib/device_detector/memory_cache.rb +26 -19
- data/lib/device_detector/metadata_extractor.rb +7 -8
- data/lib/device_detector/model_extractor.rb +3 -3
- data/lib/device_detector/name_extractor.rb +2 -2
- data/lib/device_detector/os.rb +289 -112
- data/lib/device_detector/parser.rb +49 -13
- data/lib/device_detector/vendor_fragment.rb +25 -0
- data/lib/device_detector/version.rb +3 -1
- data/lib/device_detector/version_extractor.rb +29 -2
- data/lib/device_detector.rb +192 -44
- data/regexes/bots.yml +3399 -91
- data/regexes/client/browser_engine.yml +28 -4
- data/regexes/client/browsers.yml +2697 -408
- data/regexes/client/feed_readers.yml +60 -22
- data/regexes/client/hints/apps.yml +150 -0
- data/regexes/client/hints/browsers.yml +292 -0
- data/regexes/client/libraries.yml +598 -4
- data/regexes/client/mediaplayers.yml +110 -5
- data/regexes/client/mobile_apps.yml +2451 -14
- data/regexes/client/pim.yml +128 -3
- data/regexes/device/cameras.yml +6 -6
- data/regexes/device/car_browsers.yml +39 -3
- data/regexes/device/consoles.yml +40 -6
- data/regexes/device/mobiles.yml +38844 -2907
- data/regexes/device/notebooks.yml +127 -0
- data/regexes/device/portable_media_player.yml +75 -12
- data/regexes/device/shell_tv.yml +145 -0
- data/regexes/device/televisions.yml +981 -40
- data/regexes/oss.yml +1560 -311
- data/regexes/vendorfragments.yml +6 -2
- metadata +31 -105
- data/.gitignore +0 -14
- data/.travis.yml +0 -18
- data/Gemfile +0 -8
- data/Rakefile +0 -79
- data/device_detector.gemspec +0 -26
- data/spec/device_detector/bot_fixtures_spec.rb +0 -30
- data/spec/device_detector/client_fixtures_spec.rb +0 -31
- data/spec/device_detector/concrete_user_agent_spec.rb +0 -136
- data/spec/device_detector/detector_fixtures_spec.rb +0 -60
- data/spec/device_detector/device_fixtures_spec.rb +0 -36
- data/spec/device_detector/device_spec.rb +0 -151
- data/spec/device_detector/memory_cache_spec.rb +0 -116
- data/spec/device_detector/model_extractor_spec.rb +0 -63
- data/spec/device_detector/os_fixtures_spec.rb +0 -26
- data/spec/device_detector/version_extractor_spec.rb +0 -80
- data/spec/device_detector_spec.rb +0 -198
- data/spec/fixtures/client/browser.yml +0 -1313
- data/spec/fixtures/client/feed_reader.yml +0 -187
- data/spec/fixtures/client/library.yml +0 -84
- data/spec/fixtures/client/mediaplayer.yml +0 -168
- data/spec/fixtures/client/mobile_app.yml +0 -30
- data/spec/fixtures/client/pim.yml +0 -96
- data/spec/fixtures/detector/bots.yml +0 -2418
- data/spec/fixtures/detector/camera.yml +0 -115
- data/spec/fixtures/detector/car_browser.yml +0 -20
- data/spec/fixtures/detector/console.yml +0 -267
- data/spec/fixtures/detector/desktop.yml +0 -4828
- data/spec/fixtures/detector/feature_phone.yml +0 -782
- data/spec/fixtures/detector/feed_reader.yml +0 -486
- data/spec/fixtures/detector/mediaplayer.yml +0 -179
- data/spec/fixtures/detector/mobile_apps.yml +0 -149
- data/spec/fixtures/detector/phablet.yml +0 -2140
- data/spec/fixtures/detector/portable_media_player.yml +0 -153
- data/spec/fixtures/detector/smart_display.yml +0 -58
- data/spec/fixtures/detector/smartphone-1.yml +0 -9469
- data/spec/fixtures/detector/smartphone-2.yml +0 -9414
- data/spec/fixtures/detector/smartphone-3.yml +0 -9396
- data/spec/fixtures/detector/smartphone-4.yml +0 -5742
- data/spec/fixtures/detector/smartphone.yml +0 -9411
- data/spec/fixtures/detector/tablet-1.yml +0 -9495
- data/spec/fixtures/detector/tablet-2.yml +0 -248
- data/spec/fixtures/detector/tablet.yml +0 -9484
- data/spec/fixtures/detector/tv.yml +0 -2582
- data/spec/fixtures/detector/unknown.yml +0 -3196
- data/spec/fixtures/device/camera.yml +0 -18
- data/spec/fixtures/device/car_browser.yml +0 -6
- data/spec/fixtures/device/console.yml +0 -78
- data/spec/fixtures/parser/oss.yml +0 -800
- data/spec/fixtures/parser/vendorfragments.yml +0 -162
- data/spec/spec_helper.rb +0 -9
@@ -1,7 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class DeviceDetector
|
2
|
-
class Parser
|
4
|
+
class Parser
|
5
|
+
ROOT = File.expand_path('../..', __dir__)
|
6
|
+
|
7
|
+
REGEX_CACHE = ::DeviceDetector::MemoryCache.new({})
|
8
|
+
private_constant :REGEX_CACHE
|
9
|
+
|
10
|
+
def initialize(user_agent)
|
11
|
+
@user_agent = user_agent
|
12
|
+
end
|
3
13
|
|
4
|
-
|
14
|
+
attr_reader :user_agent
|
5
15
|
|
6
16
|
def name
|
7
17
|
from_cache(['name', self.class.name, user_agent]) do
|
@@ -32,51 +42,77 @@ class DeviceDetector
|
|
32
42
|
end
|
33
43
|
|
34
44
|
def filenames
|
35
|
-
|
45
|
+
raise NotImplementedError
|
36
46
|
end
|
37
47
|
|
38
48
|
def filepaths
|
39
49
|
filenames.map do |filename|
|
40
|
-
[
|
50
|
+
[filename.to_sym, File.join(ROOT, 'regexes', filename)]
|
41
51
|
end
|
42
52
|
end
|
43
53
|
|
44
54
|
def regexes_for(file_paths)
|
45
|
-
|
55
|
+
REGEX_CACHE.get_or_set(file_paths) do
|
46
56
|
load_regexes(file_paths).flat_map { |path, regex| parse_regexes(path, regex) }
|
47
57
|
end
|
48
58
|
end
|
49
59
|
|
50
60
|
def load_regexes(file_paths)
|
51
|
-
file_paths.map
|
61
|
+
file_paths.map do |path, full_path|
|
62
|
+
object = YAML.load_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
|
+
|
66
|
+
[path, symbolize_keys!(object)]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def device_yml_file?(file_path)
|
71
|
+
file_path.include?('/regexes/device/')
|
72
|
+
end
|
73
|
+
|
74
|
+
def vendor_yml_file?(file_path)
|
75
|
+
file_path.include?('/regexes/vendorfragments')
|
76
|
+
end
|
77
|
+
|
78
|
+
def rewrite_vendor_object!(object)
|
79
|
+
object.map { |key, values| values.map { |v| { 'regex_name' => key, 'regex' => v } } }.flatten
|
80
|
+
end
|
81
|
+
|
82
|
+
def rewrite_device_object!(object)
|
83
|
+
object.map { |key, value| [key, { 'regex_name' => key }.merge!(value)] }.to_h
|
52
84
|
end
|
53
85
|
|
54
86
|
def symbolize_keys!(object)
|
55
87
|
case object
|
56
88
|
when Array
|
57
|
-
object.map!{ |v| symbolize_keys!(v) }
|
89
|
+
object.map! { |v| symbolize_keys!(v) }
|
58
90
|
when Hash
|
59
|
-
|
91
|
+
keys = object.keys
|
92
|
+
keys.each do |k|
|
93
|
+
object[k.to_sym] = symbolize_keys!(object.delete(k)) if k.is_a?(String)
|
94
|
+
end
|
60
95
|
end
|
61
96
|
object
|
62
97
|
end
|
63
98
|
|
64
99
|
def parse_regexes(path, raw_regexes)
|
65
100
|
raw_regexes.map do |meta|
|
66
|
-
|
101
|
+
raise "invalid device spec: #{meta.inspect}" unless meta[:regex].is_a? String
|
102
|
+
|
67
103
|
meta[:regex] = build_regex(meta[:regex])
|
104
|
+
meta[:versions].each { |v| v[:regex] = build_regex(v[:regex]) } if meta.key?(:versions)
|
68
105
|
meta[:path] = path
|
69
106
|
meta
|
70
107
|
end
|
71
108
|
end
|
72
109
|
|
73
110
|
def build_regex(src)
|
74
|
-
Regexp.new(
|
111
|
+
Regexp.new("(?:^|[^A-Z0-9_-]|[^A-Z0-9-]_|sprd-|MZ-)(?:#{src})", Regexp::IGNORECASE)
|
75
112
|
end
|
76
113
|
|
77
|
-
def from_cache(key)
|
78
|
-
DeviceDetector.cache.get_or_set(key)
|
114
|
+
def from_cache(key, &block)
|
115
|
+
DeviceDetector.cache.get_or_set(key, &block)
|
79
116
|
end
|
80
|
-
|
81
117
|
end
|
82
118
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
|
5
|
+
class DeviceDetector
|
6
|
+
class VendorFragment < Parser
|
7
|
+
def name
|
8
|
+
vendor_fragment_info
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def vendor_fragment_info
|
14
|
+
from_cache(['vendor_fragment', self.class.name, user_agent]) do
|
15
|
+
return if regex_meta.nil? || regex_meta.empty?
|
16
|
+
|
17
|
+
regex_meta[:regex_name]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def filenames
|
22
|
+
['vendorfragments.yml']
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -1,12 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class DeviceDetector
|
2
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
|
+
simple_version = super&.chomp('.')
|
12
|
+
|
13
|
+
return simple_version unless simple_version&.empty?
|
14
|
+
|
15
|
+
os_version_by_regexes
|
16
|
+
end
|
3
17
|
|
4
18
|
private
|
5
19
|
|
20
|
+
def os_version_by_regexes
|
21
|
+
version_matches = regex_meta[:versions]
|
22
|
+
return '' unless version_matches
|
23
|
+
|
24
|
+
version_matches.detect do |matcher|
|
25
|
+
user_agent.match(matcher[:regex]) do |match_data|
|
26
|
+
return matcher[:version].gsub(/\$(\d)/) do
|
27
|
+
match_data[Regexp.last_match(1).to_i].to_s
|
28
|
+
end.strip
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
''
|
33
|
+
end
|
34
|
+
|
6
35
|
def metadata_string
|
7
36
|
String(regex_meta[:version])
|
8
37
|
end
|
9
|
-
|
10
38
|
end
|
11
39
|
end
|
12
|
-
|
data/lib/device_detector.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'yaml'
|
2
4
|
|
3
5
|
require 'device_detector/version'
|
@@ -11,50 +13,113 @@ require 'device_detector/bot'
|
|
11
13
|
require 'device_detector/client'
|
12
14
|
require 'device_detector/device'
|
13
15
|
require 'device_detector/os'
|
16
|
+
require 'device_detector/browser'
|
17
|
+
require 'device_detector/client_hint'
|
18
|
+
require 'device_detector/vendor_fragment'
|
14
19
|
|
15
20
|
class DeviceDetector
|
21
|
+
attr_reader :client_hint, :user_agent
|
22
|
+
|
23
|
+
def initialize(user_agent, headers = nil)
|
24
|
+
@client_hint = ClientHint.new(headers)
|
25
|
+
utf8_user_agent = encode_user_agent_if_needed(user_agent)
|
26
|
+
@user_agent = build_user_agent(utf8_user_agent)
|
27
|
+
end
|
28
|
+
|
29
|
+
# https://github.com/matomo-org/device-detector/blob/a2535ff3b63e4187f1d3440aed24ff43d74fb7f1/Parser/Device/AbstractDeviceParser.php#L2065-L2073
|
30
|
+
def build_user_agent(user_agent)
|
31
|
+
return user_agent if client_hint.model.nil?
|
32
|
+
|
33
|
+
regex = build_regex('Android 10[.\d]*; K(?: Build/|[;)])')
|
34
|
+
return user_agent unless user_agent =~ regex
|
35
|
+
|
36
|
+
version = client_hint.os_version || '10'
|
37
|
+
|
38
|
+
user_agent.gsub(/(Android 10[.\d]*; K)/, "Android #{version}; #{client_hint.model}")
|
39
|
+
end
|
16
40
|
|
17
|
-
|
41
|
+
def encode_user_agent_if_needed(user_agent)
|
42
|
+
return if user_agent.nil?
|
43
|
+
return user_agent if user_agent.encoding.name == 'UTF-8'
|
18
44
|
|
19
|
-
|
20
|
-
@user_agent = user_agent
|
45
|
+
user_agent.encode('utf-8', 'binary', undef: :replace)
|
21
46
|
end
|
22
47
|
|
23
48
|
def name
|
24
|
-
client.name
|
49
|
+
return client.name if mobile_fix?
|
50
|
+
|
51
|
+
client_hint.browser_name || client.name
|
25
52
|
end
|
26
53
|
|
27
54
|
def full_version
|
28
|
-
client.full_version
|
55
|
+
client_hint.full_version || client.full_version
|
56
|
+
end
|
57
|
+
|
58
|
+
def os_family
|
59
|
+
return 'GNU/Linux' if linux_fix?
|
60
|
+
|
61
|
+
client_hint.os_family || os.family || client_hint.platform
|
29
62
|
end
|
30
63
|
|
31
64
|
def os_name
|
32
|
-
|
65
|
+
return 'GNU/Linux' if linux_fix?
|
66
|
+
|
67
|
+
client_hint.os_name || os.name || client_hint.platform
|
33
68
|
end
|
34
69
|
|
35
70
|
def os_full_version
|
36
|
-
|
71
|
+
return if skip_os_version?
|
72
|
+
return os.full_version if pico_os_fix?
|
73
|
+
return fire_os_version if fire_os_fix?
|
74
|
+
|
75
|
+
client_hint.os_version || os.full_version
|
37
76
|
end
|
38
77
|
|
39
78
|
def device_name
|
40
|
-
|
79
|
+
return if fake_ua?
|
80
|
+
|
81
|
+
device.name || client_hint.model || fix_for_x_music
|
41
82
|
end
|
42
83
|
|
43
84
|
def device_brand
|
44
|
-
|
85
|
+
return if fake_ua?
|
86
|
+
|
87
|
+
# Assume all devices running iOS / Mac OS are from Apple
|
88
|
+
brand = device.brand
|
89
|
+
brand = 'Apple' if brand.nil? && DeviceDetector::OS::APPLE_OS_NAMES.include?(os_name)
|
90
|
+
|
91
|
+
brand
|
45
92
|
end
|
46
93
|
|
47
94
|
def device_type
|
48
95
|
t = device.type
|
49
96
|
|
50
|
-
|
51
|
-
t = 'tablet'
|
52
|
-
end
|
97
|
+
t = nil if fake_ua?
|
53
98
|
|
54
|
-
|
55
|
-
|
99
|
+
# Chrome on Android passes the device type based on the keyword 'Mobile'
|
100
|
+
# If it is present the device should be a smartphone, otherwise it's a tablet
|
101
|
+
# See https://developer.chrome.com/multidevice/user-agent#chrome_for_android_user_agent
|
102
|
+
# Note: We do not check for browser (family) here, as there might be mobile apps using Chrome,
|
103
|
+
# that won't have a detected browser, but can still be detected. So we check the useragent for
|
104
|
+
# Chrome instead.
|
105
|
+
if t.nil? && os_family == 'Android' && user_agent =~ build_regex('Chrome\/[\.0-9]*')
|
106
|
+
t = user_agent =~ build_regex('(?:Mobile|eliboM)') ? 'smartphone' : 'tablet'
|
56
107
|
end
|
57
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
|
+
|
112
|
+
# Some UA contain the fragment 'Android; Tablet;' or 'Opera Tablet', so we assume those devices
|
113
|
+
# as tablets
|
114
|
+
t = 'tablet' if t.nil? && (android_tablet_fragment? || opera_tablet?)
|
115
|
+
|
116
|
+
# Some user agents simply contain the fragment 'Android; Mobile;', so we assume those devices
|
117
|
+
# as smartphones
|
118
|
+
t = 'smartphone' if t.nil? && android_mobile_fragment?
|
119
|
+
|
120
|
+
# Some UA contains the 'Android; Mobile VR;' fragment
|
121
|
+
t = 'wearable' if t.nil? && android_vr_fragment?
|
122
|
+
|
58
123
|
# Android up to 3.0 was designed for smartphones only. But as 3.0,
|
59
124
|
# which was tablet only, was published too late, there were a
|
60
125
|
# bunch of tablets running with 2.x With 4.0 the two trees were
|
@@ -63,46 +128,64 @@ class DeviceDetector
|
|
63
128
|
# So were are expecting that all devices running Android < 2 are
|
64
129
|
# smartphones Devices running Android 3.X are tablets. Device type
|
65
130
|
# of Android 2.X and 4.X+ are unknown
|
66
|
-
if t.nil? &&
|
67
|
-
|
131
|
+
if t.nil? && os_name == 'Android' && os.full_version && !os.full_version.empty?
|
132
|
+
full_version = Gem::Version.new(os.full_version)
|
133
|
+
if full_version < VersionExtractor::MAJOR_VERSION_2
|
68
134
|
t = 'smartphone'
|
69
|
-
elsif
|
135
|
+
elsif full_version >= VersionExtractor::MAJOR_VERSION_3 && \
|
136
|
+
full_version < VersionExtractor::MAJOR_VERSION_4
|
70
137
|
t = 'tablet'
|
71
138
|
end
|
72
139
|
end
|
73
140
|
|
74
141
|
# All detected feature phones running android are more likely a smartphone
|
75
|
-
if t == 'feature phone' &&
|
76
|
-
|
77
|
-
|
142
|
+
t = 'smartphone' if t == 'feature phone' && os_family == 'Android'
|
143
|
+
|
144
|
+
# All unknown devices under running Java ME are more likely a features phones
|
145
|
+
t = 'feature phone' if t.nil? && os_name == 'Java ME'
|
78
146
|
|
79
147
|
# According to http://msdn.microsoft.com/en-us/library/ie/hh920767(v=vs.85).aspx
|
80
|
-
# Internet Explorer 10 introduces the "Touch" UA string token. If this token is present at the
|
81
|
-
# UA string, the computer has touch capability, and is running Windows 8 (or later).
|
148
|
+
# Internet Explorer 10 introduces the "Touch" UA string token. If this token is present at the
|
149
|
+
# end of the UA string, the computer has touch capability, and is running Windows 8 (or later).
|
82
150
|
# This UA string will be transmitted on a touch-enabled system running Windows 8 (RT)
|
83
151
|
#
|
84
|
-
# As most touch enabled devices are tablets and only a smaller part are desktops/notebooks we
|
85
|
-
# all Windows 8 touch devices are tablets.
|
152
|
+
# As most touch enabled devices are tablets and only a smaller part are desktops/notebooks we
|
153
|
+
# assume that all Windows 8 touch devices are tablets.
|
86
154
|
if t.nil? && touch_enabled? &&
|
87
|
-
(
|
155
|
+
(os_name == 'Windows RT' ||
|
156
|
+
(os_name == 'Windows' && os_full_version &&
|
157
|
+
Gem::Version.new(os_full_version) >= VersionExtractor::MAJOR_VERSION_8))
|
88
158
|
t = 'tablet'
|
89
159
|
end
|
90
160
|
|
91
|
-
|
92
|
-
|
93
|
-
end
|
161
|
+
# All devices running Opera TV Store are assumed to be a tv
|
162
|
+
t = 'tv' if opera_tv_store?
|
94
163
|
|
95
|
-
|
164
|
+
# All devices that contain Andr0id in string are assumed to be a tv
|
165
|
+
if user_agent =~ build_regex('Andr0id|(?:Android(?: UHD)?|Google) TV|\(lite\) TV|BRAVIA')
|
96
166
|
t = 'tv'
|
97
167
|
end
|
98
168
|
|
99
|
-
#
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
169
|
+
# All devices running Tizen TV or SmartTV are assumed to be a tv
|
170
|
+
t = 'tv' if t.nil? && tizen_samsung_tv?
|
171
|
+
|
172
|
+
# Devices running those clients are assumed to be a TV
|
173
|
+
t = 'tv' if ['Kylo', 'Espial TV Browser', 'LUJO TV Browser', 'LogicUI TV Browser',
|
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)
|
177
|
+
|
178
|
+
# All devices containing TV fragment are assumed to be a tv
|
179
|
+
t = 'tv' if t.nil? && user_agent =~ build_regex('\(TV;')
|
180
|
+
|
181
|
+
has_desktop = t != 'desktop' && desktop_string? && desktop_fragment?
|
182
|
+
t = 'desktop' if has_desktop
|
183
|
+
|
184
|
+
# set device type to desktop for all devices running a desktop os that were not detected as
|
185
|
+
# another device type
|
186
|
+
return t if t || !desktop?
|
104
187
|
|
105
|
-
|
188
|
+
'desktop'
|
106
189
|
end
|
107
190
|
|
108
191
|
def known?
|
@@ -118,7 +201,6 @@ class DeviceDetector
|
|
118
201
|
end
|
119
202
|
|
120
203
|
class << self
|
121
|
-
|
122
204
|
class Configuration
|
123
205
|
attr_accessor :max_cache_keys
|
124
206
|
|
@@ -137,11 +219,10 @@ class DeviceDetector
|
|
137
219
|
@cache ||= MemoryCache.new(config.to_hash)
|
138
220
|
end
|
139
221
|
|
140
|
-
def configure
|
222
|
+
def configure
|
141
223
|
@config = Configuration.new
|
142
224
|
yield(config)
|
143
225
|
end
|
144
|
-
|
145
226
|
end
|
146
227
|
|
147
228
|
private
|
@@ -162,12 +243,63 @@ class DeviceDetector
|
|
162
243
|
@os ||= OS.new(user_agent)
|
163
244
|
end
|
164
245
|
|
246
|
+
# https://github.com/matomo-org/device-detector/blob/67ae11199a5129b42fa8b985d372ea834104fe3a/DeviceDetector.php#L931-L938
|
247
|
+
def fake_ua?
|
248
|
+
device.brand == 'Apple' && !DeviceDetector::OS::APPLE_OS_NAMES.include?(os_name)
|
249
|
+
end
|
250
|
+
|
251
|
+
# https://github.com/matomo-org/device-detector/blob/be1c9ef486c247dc4886668da5ed0b1c49d90ba8/Parser/Client/Browser.php#L772
|
252
|
+
# Fix mobile browser names e.g. Chrome => Chrome Mobile
|
253
|
+
def mobile_fix?
|
254
|
+
client.name == "#{client_hint.browser_name} Mobile"
|
255
|
+
end
|
256
|
+
|
257
|
+
def linux_fix?
|
258
|
+
client_hint.platform == 'Linux' &&
|
259
|
+
%w[iOS Android].include?(os.name) &&
|
260
|
+
%w[?0 0].include?(client_hint.mobile)
|
261
|
+
end
|
262
|
+
|
263
|
+
# Related to issue mentionned in device.rb#1562
|
264
|
+
def fix_for_x_music
|
265
|
+
user_agent&.include?('X-music Ⅲ') ? 'X-Music III' : nil
|
266
|
+
end
|
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
|
283
|
+
def skip_os_version?
|
284
|
+
!client_hint.os_family.nil? &&
|
285
|
+
client_hint.os_version.nil? &&
|
286
|
+
client_hint.os_family != os.family
|
287
|
+
end
|
288
|
+
|
165
289
|
def android_tablet_fragment?
|
166
|
-
user_agent =~ build_regex('Android
|
290
|
+
user_agent =~ build_regex('Android( [\.0-9]+)?; Tablet;|Tablet(?! PC)|.*\-tablet$')
|
167
291
|
end
|
168
292
|
|
169
293
|
def android_mobile_fragment?
|
170
|
-
user_agent =~ build_regex('Android
|
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 ')
|
299
|
+
end
|
300
|
+
|
301
|
+
def desktop_fragment?
|
302
|
+
user_agent =~ build_regex('Desktop(?: (x(?:32|64)|WOW64))?;')
|
171
303
|
end
|
172
304
|
|
173
305
|
def touch_enabled?
|
@@ -175,20 +307,36 @@ class DeviceDetector
|
|
175
307
|
end
|
176
308
|
|
177
309
|
def opera_tv_store?
|
178
|
-
user_agent =~ build_regex('Opera TV Store')
|
310
|
+
user_agent =~ build_regex('Opera TV Store|OMI/')
|
179
311
|
end
|
180
312
|
|
181
313
|
def opera_tablet?
|
182
314
|
user_agent =~ build_regex('Opera Tablet')
|
183
315
|
end
|
184
316
|
|
317
|
+
def tizen_samsung_tv?
|
318
|
+
user_agent =~ build_regex('SmartTV|Tizen.+ TV .+$')
|
319
|
+
end
|
320
|
+
|
321
|
+
def uses_mobile_browser?
|
322
|
+
client.browser? && client.mobile_only_browser?
|
323
|
+
end
|
324
|
+
|
185
325
|
# This is a workaround until we support detecting mobile only browsers
|
186
|
-
def
|
187
|
-
|
326
|
+
def desktop_string?
|
327
|
+
user_agent =~ /Desktop/
|
328
|
+
end
|
329
|
+
|
330
|
+
def desktop?
|
331
|
+
return false if os_name.nil? || os_name == '' || os_name == 'UNK'
|
332
|
+
|
333
|
+
# Check for browsers available for mobile devices only
|
334
|
+
return false if uses_mobile_browser?
|
335
|
+
|
336
|
+
DeviceDetector::OS::DESKTOP_OSS.include?(os_family)
|
188
337
|
end
|
189
338
|
|
190
339
|
def build_regex(src)
|
191
340
|
Regexp.new('(?:^|[^A-Z0-9\_\-])(?:' + src + ')', Regexp::IGNORECASE)
|
192
341
|
end
|
193
|
-
|
194
342
|
end
|