class DeviceDetector

Constants

VERSION

Attributes

client_hint[R]
user_agent[R]

Public Class Methods

cache() click to toggle source
# File lib/device_detector.rb, line 218
def cache
  @cache ||= MemoryCache.new(config.to_hash)
end
config() click to toggle source
# File lib/device_detector.rb, line 214
def config
  @config ||= Configuration.new
end
configure() { |config| ... } click to toggle source
# File lib/device_detector.rb, line 222
def configure
  @config = Configuration.new
  yield(config)
end
new(user_agent, headers = nil) click to toggle source
# File lib/device_detector.rb, line 23
def initialize(user_agent, headers = nil)
  @client_hint = ClientHint.new(headers)
  utf8_user_agent = encode_user_agent_if_needed(user_agent)
  @user_agent = build_user_agent(utf8_user_agent)
end

Public Instance Methods

bot?() click to toggle source
# File lib/device_detector.rb, line 195
def bot?
  bot.bot?
end
bot_name() click to toggle source
# File lib/device_detector.rb, line 199
def bot_name
  bot.name
end
build_user_agent(user_agent) click to toggle source

github.com/matomo-org/device-detector/blob/a2535ff3b63e4187f1d3440aed24ff43d74fb7f1/Parser/Device/AbstractDeviceParser.php#L2065-L2073

# File lib/device_detector.rb, line 30
def build_user_agent(user_agent)
  return user_agent if client_hint.model.nil?

  regex = build_regex('Android 10[.\d]*; K(?: Build/|[;)])')
  return user_agent unless user_agent =~ regex

  version = client_hint.os_version || '10'

  user_agent.gsub(/(Android 10[.\d]*; K)/, "Android #{version}; #{client_hint.model}")
end
device_brand() click to toggle source
# File lib/device_detector.rb, line 84
def device_brand
  return if fake_ua?

  # Assume all devices running iOS / Mac OS are from Apple
  brand = device.brand
  brand = 'Apple' if brand.nil? && DeviceDetector::OS::APPLE_OS_NAMES.include?(os_name)

  brand
end
device_name() click to toggle source
# File lib/device_detector.rb, line 78
def device_name
  return if fake_ua?

  device.name || client_hint.model || fix_for_x_music
end
device_type() click to toggle source
# File lib/device_detector.rb, line 94
def device_type
  t = device.type

  t = nil if fake_ua?

  # Chrome on Android passes the device type based on the keyword 'Mobile'
  # If it is present the device should be a smartphone, otherwise it's a tablet
  # See https://developer.chrome.com/multidevice/user-agent#chrome_for_android_user_agent
  # Note: We do not check for browser (family) here, as there might be mobile apps using Chrome,
  # that won't have a detected browser, but can still be detected. So we check the useragent for
  # Chrome instead.
  if t.nil? && os_family == 'Android' && user_agent =~ build_regex('Chrome\/[\.0-9]*')
    t = user_agent =~ build_regex('(?:Mobile|eliboM)') ? 'smartphone' : 'tablet'
  end

  # Some UA contain the fragment 'Pad/APad', so we assume those devices as tablets
  t = 'tablet' if t == 'smartphone' && user_agent =~ build_regex('Pad\/APad')

  # Some UA contain the fragment 'Android; Tablet;' or 'Opera Tablet', so we assume those devices
  # as tablets
  t = 'tablet' if t.nil? && (android_tablet_fragment? || opera_tablet?)

  # Some user agents simply contain the fragment 'Android; Mobile;', so we assume those devices
  # as smartphones
  t = 'smartphone' if t.nil? && android_mobile_fragment?

  # Some UA contains the 'Android; Mobile VR;' fragment
  t = 'wearable' if t.nil? && android_vr_fragment?

  # Android up to 3.0 was designed for smartphones only. But as 3.0,
  # which was tablet only, was published too late, there were a
  # bunch of tablets running with 2.x With 4.0 the two trees were
  # merged and it is for smartphones and tablets
  #
  # So were are expecting that all devices running Android < 2 are
  # smartphones Devices running Android 3.X are tablets. Device type
  # of Android 2.X and 4.X+ are unknown
  if t.nil? && os_name == 'Android' && os.full_version && !os.full_version.empty?
    full_version = Gem::Version.new(os.full_version)
    if full_version < VersionExtractor::MAJOR_VERSION_2
      t = 'smartphone'
    elsif full_version >= VersionExtractor::MAJOR_VERSION_3 && \
          full_version < VersionExtractor::MAJOR_VERSION_4
      t = 'tablet'
    end
  end

  # All detected feature phones running android are more likely a smartphone
  t = 'smartphone' if t == 'feature phone' && os_family == 'Android'

  # All unknown devices under running Java ME are more likely a features phones
  t = 'feature phone' if t.nil? && os_name == 'Java ME'

  # According to http://msdn.microsoft.com/en-us/library/ie/hh920767(v=vs.85).aspx
  # Internet Explorer 10 introduces the "Touch" UA string token. If this token is present at the
  # end of the UA string, the computer has touch capability, and is running Windows 8 (or later).
  # This UA string will be transmitted on a touch-enabled system running Windows 8 (RT)
  #
  # As most touch enabled devices are tablets and only a smaller part are desktops/notebooks we
  # assume that all Windows 8 touch devices are tablets.
  if t.nil? && touch_enabled? &&
     (os_name == 'Windows RT' ||
      (os_name == 'Windows' && os_full_version &&
       Gem::Version.new(os_full_version) >= VersionExtractor::MAJOR_VERSION_8))
    t = 'tablet'
  end

  # All devices running Opera TV Store are assumed to be a tv
  t = 'tv' if opera_tv_store?

  # All devices that contain Andr0id in string are assumed to be a tv
  if user_agent =~ build_regex('Andr0id|(?:Android(?: UHD)?|Google) TV|\(lite\) TV|BRAVIA')
    t = 'tv'
  end

  # All devices running Tizen TV or SmartTV are assumed to be a tv
  t = 'tv' if t.nil? && tizen_samsung_tv?

  # Devices running those clients are assumed to be a TV
  t = 'tv' if ['Kylo', 'Espial TV Browser', 'LUJO TV Browser', 'LogicUI TV Browser',
               'Open TV Browser', 'Seraphic Sraf', 'Opera Devices', 'Crow Browser',
               'Vewd Browser', 'TiviMate', 'Quick Search TV', 'QJY TV Browser',
               'TV Bro'].include?(name)

  # All devices containing TV fragment are assumed to be a tv
  t = 'tv' if t.nil? && user_agent =~ build_regex('\(TV;')

  has_desktop = t != 'desktop' && desktop_string? && desktop_fragment?
  t = 'desktop' if has_desktop

  # set device type to desktop for all devices running a desktop os that were not detected as
  # another device type
  return t if t || !desktop?

  'desktop'
end
encode_user_agent_if_needed(user_agent) click to toggle source
# File lib/device_detector.rb, line 41
def encode_user_agent_if_needed(user_agent)
  return if user_agent.nil?
  return user_agent if user_agent.encoding.name == 'UTF-8'

  user_agent.encode('utf-8', 'binary', undef: :replace)
end
full_version() click to toggle source
# File lib/device_detector.rb, line 54
def full_version
  client_hint.full_version || client.full_version
end
known?() click to toggle source
# File lib/device_detector.rb, line 191
def known?
  client.known?
end
name() click to toggle source
# File lib/device_detector.rb, line 48
def name
  return client.name if mobile_fix?

  client_hint.browser_name || client.name
end
os_family() click to toggle source
# File lib/device_detector.rb, line 58
def os_family
  return 'GNU/Linux' if linux_fix?

  client_hint.os_family || os.family || client_hint.platform
end
os_full_version() click to toggle source
# File lib/device_detector.rb, line 70
def os_full_version
  return if skip_os_version?
  return os.full_version if pico_os_fix?
  return fire_os_version if fire_os_fix?

  client_hint.os_version || os.full_version
end
os_name() click to toggle source
# File lib/device_detector.rb, line 64
def os_name
  return 'GNU/Linux' if linux_fix?

  client_hint.os_name || os.name || client_hint.platform
end

Private Instance Methods

android_mobile_fragment?() click to toggle source
# File lib/device_detector.rb, line 293
def android_mobile_fragment?
  user_agent =~ build_regex('Android( [\.0-9]+)?; Mobile;|.*\-mobile$')
end
android_tablet_fragment?() click to toggle source
# File lib/device_detector.rb, line 289
def android_tablet_fragment?
  user_agent =~ build_regex('Android( [\.0-9]+)?; Tablet;|Tablet(?! PC)|.*\-tablet$')
end
android_vr_fragment?() click to toggle source
# File lib/device_detector.rb, line 297
def android_vr_fragment?
  user_agent =~ build_regex('Android( [\.0-9]+)?; Mobile VR;| VR ')
end
bot() click to toggle source
# File lib/device_detector.rb, line 230
def bot
  @bot ||= Bot.new(user_agent)
end
build_regex(src) click to toggle source
# File lib/device_detector.rb, line 339
def build_regex(src)
  Regexp.new('(?:^|[^A-Z0-9\_\-])(?:' + src + ')', Regexp::IGNORECASE)
end
client() click to toggle source
# File lib/device_detector.rb, line 234
def client
  @client ||= Client.new(user_agent)
end
desktop?() click to toggle source
# File lib/device_detector.rb, line 330
def desktop?
  return false if os_name.nil? || os_name == '' || os_name == 'UNK'

  # Check for browsers available for mobile devices only
  return false if uses_mobile_browser?

  DeviceDetector::OS::DESKTOP_OSS.include?(os_family)
end
desktop_fragment?() click to toggle source
# File lib/device_detector.rb, line 301
def desktop_fragment?
  user_agent =~ build_regex('Desktop(?: (x(?:32|64)|WOW64))?;')
end
desktop_string?() click to toggle source

This is a workaround until we support detecting mobile only browsers

# File lib/device_detector.rb, line 326
def desktop_string?
  user_agent =~ /Desktop/
end
device() click to toggle source
# File lib/device_detector.rb, line 238
def device
  @device ||= Device.new(user_agent)
end
fake_ua?() click to toggle source

github.com/matomo-org/device-detector/blob/67ae11199a5129b42fa8b985d372ea834104fe3a/DeviceDetector.php#L931-L938

# File lib/device_detector.rb, line 247
def fake_ua?
  device.brand == 'Apple' && !DeviceDetector::OS::APPLE_OS_NAMES.include?(os_name)
end
fire_os_fix?() click to toggle source

github.com/matomo-org/device-detector/blob/323629cb679c8572a9745cba9c3803fee13f3cf6/Parser/OperatingSystem.php#L398-L403

# File lib/device_detector.rb, line 273
def fire_os_fix?
  !client_hint.platform.nil? && os.name == 'Fire OS'
end
fire_os_version() click to toggle source
# File lib/device_detector.rb, line 277
def fire_os_version
  DeviceDetector::OS
    .mapped_os_version(client_hint.os_version, DeviceDetector::OS::FIRE_OS_VERSION_MAPPING)
end
fix_for_x_music() click to toggle source

Related to issue mentionned in device.rb#1562

# File lib/device_detector.rb, line 264
def fix_for_x_music
  user_agent&.include?('X-music Ⅲ') ? 'X-Music III' : nil
end
linux_fix?() click to toggle source
# File lib/device_detector.rb, line 257
def linux_fix?
  client_hint.platform == 'Linux' &&
    %w[iOS Android].include?(os.name) &&
    %w[?0 0].include?(client_hint.mobile)
end
mobile_fix?() click to toggle source

github.com/matomo-org/device-detector/blob/be1c9ef486c247dc4886668da5ed0b1c49d90ba8/Parser/Client/Browser.php#L772 Fix mobile browser names e.g. Chrome => Chrome Mobile

# File lib/device_detector.rb, line 253
def mobile_fix?
  client.name == "#{client_hint.browser_name} Mobile"
end
opera_tablet?() click to toggle source
# File lib/device_detector.rb, line 313
def opera_tablet?
  user_agent =~ build_regex('Opera Tablet')
end
opera_tv_store?() click to toggle source
# File lib/device_detector.rb, line 309
def opera_tv_store?
  user_agent =~ build_regex('Opera TV Store|OMI/')
end
os() click to toggle source
# File lib/device_detector.rb, line 242
def os
  @os ||= OS.new(user_agent)
end
pico_os_fix?() click to toggle source
# File lib/device_detector.rb, line 268
def pico_os_fix?
  client_hint.os_name == 'Pico OS'
end
skip_os_version?() click to toggle source

github.com/matomo-org/device-detector/blob/323629cb679c8572a9745cba9c3803fee13f3cf6/Parser/OperatingSystem.php#L378-L383

# File lib/device_detector.rb, line 283
def skip_os_version?
  !client_hint.os_family.nil? &&
    client_hint.os_version.nil? &&
    client_hint.os_family != os.family
end
tizen_samsung_tv?() click to toggle source
# File lib/device_detector.rb, line 317
def tizen_samsung_tv?
  user_agent =~ build_regex('SmartTV|Tizen.+ TV .+$')
end
touch_enabled?() click to toggle source
# File lib/device_detector.rb, line 305
def touch_enabled?
  user_agent =~ build_regex('Touch')
end
uses_mobile_browser?() click to toggle source
# File lib/device_detector.rb, line 321
def uses_mobile_browser?
  client.browser? && client.mobile_only_browser?
end