using Android.OS; using Java.Lang; using System.Collections.Generic; using System.IO; using System.Linq; using Xamarin.Essentials; namespace System { /// /// Android ROM 相关函数集 /// public static class AndroidROM { /// /// Android ROM 信息 /// public sealed class Info { public bool HasVersion { get; } public Version? Version { get; } public string? VersionString { get; } public bool HasVersionString { get; } public AndroidROMType Type { get; } public Info(AndroidROMType type, string? version) { Type = type; VersionString = version; HasVersionString = !string.IsNullOrWhiteSpace(version); #pragma warning disable CS8604 // 可能的 null 引用参数。 Version = HasVersionString ? StringExtensions.VersionTryParse(version) : null; #pragma warning restore CS8604 // 可能的 null 引用参数。 HasVersion = Version != null; } public override string ToString() { if (HasVersionString) return $"{Type} {VersionString}"; return Type.ToString(); } public static Info Unknown { get; } = new Info(AndroidROMType.Unknown, null); /// /// is MIUI (MI) /// public bool IsMIUI => Type == AndroidROMType.MIUI; /// /// is Emotion UI (HUAWEI) /// public bool IsEMUI => Type == AndroidROMType.EMUI; /// /// is ColorOS (OPPO) /// public bool IsColorOS => Type == AndroidROMType.ColorOS; /// /// is Funtouch OS (vivo) /// public bool IsFuntouchOS => Type == AndroidROMType.FuntouchOS; /// /// is Smartisan OS (Smartisan) /// public bool IsSmartisanOS => Type == AndroidROMType.SmartisanOS; } const string RO_BUILD_DISPLAY_ID = "ro.build.display.id"; /// /// ROM 识别关键字 /// static readonly IReadOnlyDictionary identify_keywords_rule_a = new Dictionary { { "ro.miui.ui.version.name", AndroidROMType.MIUI }, // https://dev.mi.com/doc/?p=254 { "ro.build.version.emui", AndroidROMType.EMUI }, { "ro.build.version.opporom", AndroidROMType.ColorOS }, { "ro.vivo.os.version", AndroidROMType.FuntouchOS }, { "ro.smartisan.version", AndroidROMType.SmartisanOS }, { "ro.letv.release.version", AndroidROMType.EUI }, { "ro.build.sense.version", AndroidROMType.HTCSense }, { "ro.build.nubia.rom.code", AndroidROMType.NubiaUI }, }; static readonly IReadOnlyDictionary identify_keywords_rule_b = new Dictionary { { "flyme", AndroidROMType.Flyme }, { "amigo", AndroidROMType.AmigoOS }, }; /// /// 当前设备是否支持 Meizu Flyme SmartBar /// public static bool HasFlymeSmartBar { get { try { var method = Class.ForName("android.os.Build").GetMethod("hasSmartBar"); if (method != null) { var value = method.Invoke(null); if (value != null) { return (bool)value; } } } catch { } return false; } } #region Emulator static int rating = -1; static readonly Lazy mIsEmulator = new Lazy(GetIsEmulator); static bool GetIsEmulator() { // 参考 https://github.com/gingo/android-emulator-detector/blob/master/EmulatorDetectorProject/EmulatorDetector/src/main/java/net/skoumal/emulatordetector/EmulatorDetector.java return _() || DeviceInfo.DeviceType == DeviceType.Virtual; static bool contains(string l, string r) { return l.Contains(r, StringComparison.OrdinalIgnoreCase); } static bool equals(string l, string r) => string.Equals(l, r, StringComparison.OrdinalIgnoreCase); static bool _() { var newRating = 0; if (rating < 0) { var PRODUCT = Build.Product; if (PRODUCT == null || contains(PRODUCT, "sdk") || contains(PRODUCT, "Andy") || contains(PRODUCT, "ttVM_Hdragon") || contains(PRODUCT, "google_sdk") || contains(PRODUCT, "Droid4X") || contains(PRODUCT, "nox") || contains(PRODUCT, "sdk_x86") || contains(PRODUCT, "sdk_google") || contains(PRODUCT, "vbox86p")) { newRating++; } var MANUFACTURER = Build.Manufacturer; if (MANUFACTURER == null || equals(MANUFACTURER, "unknown") || equals(MANUFACTURER, "Genymotion") || contains(MANUFACTURER, "VS Emulator") || contains(MANUFACTURER, "Andy") || contains(MANUFACTURER, "MIT") || contains(MANUFACTURER, "nox") || contains(MANUFACTURER, "TiantianVM")) { newRating++; } var BRAND = Build.Brand; if (BRAND == null || equals(BRAND, "generic") || equals(BRAND, "generic_x86") || equals(BRAND, "TTVM") || contains(BRAND, "Andy")) { newRating++; } var DEVICE = Build.Device; if (DEVICE == null || contains(DEVICE, "generic") || contains(DEVICE, "generic_x86") || contains(DEVICE, "Andy") || contains(DEVICE, "ttVM_Hdragon") || contains(DEVICE, "Droid4X") || contains(DEVICE, "nox") || contains(DEVICE, "generic_x86_64") || contains(DEVICE, "vbox86p")) { newRating++; } var MODEL = Build.Model; if (MODEL == null || equals(MODEL, "sdk") || equals(MODEL, "google_sdk") || contains(MODEL, "Droid4X") || contains(MODEL, "TiantianVM") || contains(MODEL, "Andy") || equals(MODEL, "Android SDK built for x86_64") || equals(MODEL, "Android SDK built for x86")) { newRating++; } var HARDWARE = Build.Hardware; if (HARDWARE == null || equals(HARDWARE, "goldfish") || equals(HARDWARE, "vbox86") || contains(HARDWARE, "nox") || contains(HARDWARE, "ttVM_x86")) { newRating++; } var FINGERPRINT = Build.Fingerprint; if (FINGERPRINT == null || contains(FINGERPRINT, "generic/sdk/generic") || contains(FINGERPRINT, "generic_x86/sdk_x86/generic_x86") || contains(FINGERPRINT, "Andy") || contains(FINGERPRINT, "ttVM_Hdragon") || contains(FINGERPRINT, "generic_x86_64") || contains(FINGERPRINT, "generic/google_sdk/generic") || contains(FINGERPRINT, "vbox86p") || contains(FINGERPRINT, "generic/vbox86p/vbox86p")) { newRating++; } try { var opengl = Android.Opengl.GLES20.GlGetString(Android.Opengl.GLES20.GlRenderer); if (!string.IsNullOrWhiteSpace(opengl)) { #pragma warning disable CS8604 // 可能的 null 引用参数。 if (contains(opengl, "Bluestacks") || contains(opengl, "Translator") ) #pragma warning restore CS8604 // 可能的 null 引用参数。 newRating += 10; } } catch { } try { #pragma warning disable CS0618 // 类型或成员已过时 var esDir = Android.OS.Environment.ExternalStorageDirectory; #pragma warning restore CS0618 // 类型或成员已过时 if (esDir != null) { var path = Path.Combine(esDir.ToString(), "windows", "BstSharedFolder"); var sharedFolder = new Java.IO.File(path); if (sharedFolder.Exists()) { newRating += 10; } } } catch { } rating = newRating; } return rating > 3; } } /// /// 当前设备是否为模拟器 /// public static bool IsEmulator => mIsEmulator.Value; #endregion static Info? mCurrent; /// /// 获取当前 ROM 信息 /// public static Info Current => mCurrent ?? throw new NullReferenceException("must call AndroidROM.Initialize() before using."); static bool isInit; /// /// 初始化 ROM 信息 /// public static void Initialize() { if (isInit) return; string? version = null; var firstOrDefault = identify_keywords_rule_a .FirstOrDefault(x => SystemProperties.TryGet(x.Key, out version)); if (firstOrDefault.Key != null) { mCurrent = new Info(firstOrDefault.Value, version); } else { if (SystemProperties.TryGet(RO_BUILD_DISPLAY_ID, out var value)) { foreach (var item in identify_keywords_rule_b) { if (value.Contains(item.Key, StringComparison.OrdinalIgnoreCase)) { mCurrent = new Info(item.Value, value); break; } } } } if (mCurrent == null) mCurrent = Info.Unknown; isInit = true; } } }