using System; using System.Globalization; using Microsoft.AspNetCore.Http; namespace FrontOffice.Main.Utilities; public interface IDeviceDetector { DeviceInfo Detect(string? userAgent = null); bool IsMobile(string? userAgent = null); bool IsTablet(string? userAgent = null); bool IsDesktop(string? userAgent = null); } public sealed class DeviceDetector : IDeviceDetector { private readonly IHttpContextAccessor _httpContextAccessor; public DeviceDetector(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor)); } public DeviceInfo Detect(string? userAgent = null) { string? headerUa = null; var http = _httpContextAccessor.HttpContext; if (http != null) { headerUa = http.Request.Headers["User-Agent"].ToString(); } var ua = userAgent ?? headerUa ?? string.Empty; var uaLower = ua.ToLower(CultureInfo.InvariantCulture); // Bots if (ContainsAny(uaLower, "bot", "spider", "crawler", "bingpreview", "facebookexternalhit")) { return new DeviceInfo(DeviceType.Bot, DetectOs(uaLower), DetectBrowser(uaLower), ua); } var type = DetectDeviceType(uaLower); var os = DetectOs(uaLower); var browser = DetectBrowser(uaLower); return new DeviceInfo(type, os, browser, ua); } public bool IsMobile(string? userAgent = null) => Detect(userAgent).Type == DeviceType.Mobile; public bool IsTablet(string? userAgent = null) => Detect(userAgent).Type == DeviceType.Tablet; public bool IsDesktop(string? userAgent = null) => Detect(userAgent).Type == DeviceType.Desktop; private static DeviceType DetectDeviceType(string uaLower) { if (string.IsNullOrWhiteSpace(uaLower)) return DeviceType.Unknown; // Tablets if (ContainsAny(uaLower, "ipad", "tablet", "kindle", "silk", "playbook")) return DeviceType.Tablet; // Mobiles (exclude tablets when possible) if (ContainsAny(uaLower, "mobi", "iphone", "ipod", "android") && !uaLower.Contains("tablet")) return DeviceType.Mobile; // Smart TV if (ContainsAny(uaLower, "smart-tv", "smarttv", "hbbtv", "appletv")) return DeviceType.Desktop; // treat TVs as desktop-like return DeviceType.Desktop; } private static string DetectOs(string uaLower) { if (string.IsNullOrWhiteSpace(uaLower)) return "Unknown"; if (uaLower.Contains("windows nt")) return "Windows"; if (uaLower.Contains("android")) return "Android"; if (uaLower.Contains("iphone") || uaLower.Contains("ipad") || uaLower.Contains("ipod") || uaLower.Contains("ios")) return "iOS"; if (uaLower.Contains("mac os x") || uaLower.Contains("macintosh")) return "macOS"; if (uaLower.Contains("linux")) return "Linux"; if (uaLower.Contains("cros")) return "ChromeOS"; return "Unknown"; } private static string DetectBrowser(string uaLower) { if (string.IsNullOrWhiteSpace(uaLower)) return "Unknown"; if (uaLower.Contains("edg/")) return "Edge"; // Chromium Edge if (uaLower.Contains("edge/")) return "Edge"; // Legacy Edge if (uaLower.Contains("opr/") || uaLower.Contains("opera")) return "Opera"; if (uaLower.Contains("chrome/") && !uaLower.Contains("edg/") && !uaLower.Contains("opr/") && !uaLower.Contains("chromium")) return "Chrome"; if (uaLower.Contains("safari") && !uaLower.Contains("chrome")) return "Safari"; if (uaLower.Contains("firefox")) return "Firefox"; if (uaLower.Contains("msie") || uaLower.Contains("trident/")) return "IE"; if (uaLower.Contains("chromium")) return "Chromium"; return "Unknown"; } private static bool ContainsAny(string source, params string[] values) { foreach (var v in values) { if (source.Contains(v)) return true; } return false; } } public enum DeviceType { Unknown = 0, Mobile = 1, Tablet = 2, Desktop = 3, Bot = 4 } public sealed record DeviceInfo(DeviceType Type, string Os, string Browser, string UserAgent);