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;
}
}
}