Files
test/Assets/Editor/PostBuildAdbInstall.cs
vuong dinh hoang 444609b50a for build tool
2026-04-22 13:54:04 +07:00

296 lines
9.9 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using UnityEditor;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
using UnityEngine;
using Debug = UnityEngine.Debug;
public class PostBuildAdbInstall : IPostprocessBuildWithReport
{
private const string PrefKey = "PerfectWorld/AutoAdbInstallAfterAndroidBuild";
public int callbackOrder => 200;
[MenuItem("Tools/Perfect World/Auto install APK after build (toggle)")]
private static void ToggleAutoInstall()
{
bool v = !EditorPrefs.GetBool(PrefKey, true);
EditorPrefs.SetBool(PrefKey, v);
Debug.Log("[AdbInstall] Auto install after Android build: " + (v ? "ON" : "OFF"));
}
[MenuItem("Tools/Perfect World/Auto install APK after build (toggle)", true)]
private static bool ToggleAutoInstallValidate()
{
UnityEditor.Menu.SetChecked("Tools/Perfect World/Auto install APK after build (toggle)", EditorPrefs.GetBool(PrefKey, true));
return true;
}
[MenuItem("Tools/Perfect World/Install APK to device (choose file)")]
private static void InstallApkFromPicker()
{
string p = EditorUtility.OpenFilePanel("Pick APK", "", "apk");
if (string.IsNullOrEmpty(p))
return;
RunAdbInstall(p);
}
public void OnPostprocessBuild(BuildReport report)
{
if (report.summary.platform != BuildTarget.Android)
return;
if (report.summary.result != BuildResult.Succeeded)
return;
if (!EditorPrefs.GetBool(PrefKey, true))
return;
string outputPath = report.summary.outputPath;
string apkPath = ResolveApkPath(outputPath);
if (string.IsNullOrEmpty(apkPath))
{
if (!string.IsNullOrEmpty(outputPath) &&
outputPath.EndsWith(".aab", StringComparison.OrdinalIgnoreCase) &&
File.Exists(outputPath))
{
Debug.LogWarning(
"[AdbInstall] Output is .aab; adb install needs an APK. Disable App Bundle for dev, or use menu to install a built APK.");
}
else
{
Debug.LogWarning("[AdbInstall] No .apk found. Build output: " + outputPath);
}
return;
}
RunAdbInstall(apkPath);
}
private static string ResolveApkPath(string outputPath)
{
if (string.IsNullOrEmpty(outputPath))
return null;
if (File.Exists(outputPath) && outputPath.EndsWith(".apk", StringComparison.OrdinalIgnoreCase))
return outputPath;
if (File.Exists(outputPath) && outputPath.EndsWith(".aab", StringComparison.OrdinalIgnoreCase))
return null;
if (Directory.Exists(outputPath))
{
var apks = new List<string>(Directory.GetFiles(outputPath, "*.apk", SearchOption.AllDirectories));
if (apks.Count == 0)
return null;
apks.Sort((a, b) => File.GetLastWriteTimeUtc(b).CompareTo(File.GetLastWriteTimeUtc(a)));
return apks[0];
}
return null;
}
private static void RunAdbInstall(string apkPath)
{
string adb = FindAdbExecutable();
if (string.IsNullOrEmpty(adb))
{
Debug.LogError(
"[AdbInstall] adb.exe not found. Install Android Build Support (SDK) in Unity Hub, or set ANDROID_HOME, or add platform-tools to PATH. Editor: " + (EditorApplication.applicationPath ?? "(null)"));
return;
}
string serial = GetSingleDeviceOrFirst(adb, out string listErr);
if (serial == null)
{
if (!string.IsNullOrEmpty(listErr))
Debug.LogWarning("[AdbInstall] " + listErr);
return;
}
string argPrefix = string.IsNullOrEmpty(serial) ? "" : ("-s " + serial + " ");
string args = argPrefix + "install -r \"" + apkPath + "\"";
var psi = new ProcessStartInfo(adb, args)
{
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
};
try
{
using (var p = Process.Start(psi))
{
if (p == null)
{
Debug.LogError("[AdbInstall] Failed to start adb.");
return;
}
string std = p.StandardOutput.ReadToEnd();
string err = p.StandardError.ReadToEnd();
p.WaitForExit(120000);
if (p.ExitCode == 0)
Debug.Log("[AdbInstall] Installed: " + apkPath + "\n" + std + err);
else
Debug.LogError("[AdbInstall] Install failed (exit " + p.ExitCode + "): " + apkPath + "\n" + std + err);
}
}
catch (Exception ex)
{
Debug.LogError("[AdbInstall] " + ex.Message);
}
}
private static string GetSingleDeviceOrFirst(string adb, out string error)
{
error = null;
var psi = new ProcessStartInfo(adb, "devices")
{
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
};
string stdout;
try
{
using (var p = Process.Start(psi))
{
if (p == null)
{
error = "Could not run adb devices.";
return null;
}
stdout = p.StandardOutput.ReadToEnd();
p.WaitForExit(10000);
}
}
catch (Exception ex)
{
error = ex.Message;
return null;
}
var onlines = new List<string>();
var lines = stdout.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < lines.Length; i++)
{
if (i == 0 && lines[i].IndexOf("List of devices", StringComparison.OrdinalIgnoreCase) >= 0)
continue;
int tab = lines[i].IndexOf('\t');
if (tab < 0)
continue;
string id = lines[i].Substring(0, tab).Trim();
string st = lines[i].Substring(tab + 1).Trim();
if (st.Equals("device", StringComparison.OrdinalIgnoreCase))
onlines.Add(id);
}
if (onlines.Count == 0)
{
error = "No Android device in state \"device\" (enable USB debugging, cable, allow PC).";
return null;
}
if (onlines.Count == 1)
return "";
Debug.LogWarning("[AdbInstall] Multiple devices - installing to: " + onlines[0]);
return onlines[0];
}
private static string FindAdbExecutable()
{
foreach (string root in EnumerateAndroidSdkRoots())
{
string a = TryAdbInSdk(root);
if (!string.IsNullOrEmpty(a))
return a;
}
var pathvar = Environment.GetEnvironmentVariable("PATH");
if (!string.IsNullOrEmpty(pathvar))
{
foreach (var part in pathvar.Split(new char[] { Path.PathSeparator }, StringSplitOptions.RemoveEmptyEntries))
{
var t = part.Trim();
if (t.Length > 1 && t[0] == '"' && t[t.Length - 1] == '"')
t = t.Substring(1, t.Length - 2);
var candidate = Path.Combine(t, "adb.exe");
if (File.Exists(candidate))
return candidate;
}
}
return null;
}
private static IEnumerable<string> EnumerateAndroidSdkRoots()
{
var seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
void Add(string p)
{
if (string.IsNullOrEmpty(p)) return;
p = p.Trim();
if (p.Length == 0) return;
seen.Add(p);
}
Add(Environment.GetEnvironmentVariable("ANDROID_HOME"));
Add(Environment.GetEnvironmentVariable("ANDROID_SDK_ROOT"));
string ext = GetUnityAndroidSdkRootPathFromSettings();
if (!string.IsNullOrEmpty(ext))
Add(ext);
if (EditorPrefs.HasKey("AndroidSdkRoot"))
Add(EditorPrefs.GetString("AndroidSdkRoot", ""));
try
{
string editorExe = EditorApplication.applicationPath;
if (!string.IsNullOrEmpty(editorExe))
{
string editorDir = Path.GetDirectoryName(editorExe);
if (!string.IsNullOrEmpty(editorDir))
Add(Path.Combine(editorDir, "Data", "PlaybackEngines", "AndroidPlayer", "SDK"));
}
}
catch { }
Add(Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
"AppData", "Local", "Android", "Sdk"));
return seen;
}
private static string GetUnityAndroidSdkRootPathFromSettings()
{
try
{
var t = Type.GetType("UnityEditor.Android.AndroidExternalToolsSettings, UnityEditor.Android.EModule");
if (t == null) return null;
var p = t.GetProperty("sdkRootPath", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
if (p == null) return null;
var s = p.GetValue(null) as string;
if (string.IsNullOrEmpty(s)) return null;
if (Directory.Exists(s)) return s;
}
catch { }
return null;
}
private static string TryAdbInSdk(string sdkRoot)
{
if (string.IsNullOrEmpty(sdkRoot) || !Directory.Exists(sdkRoot))
return null;
var adb = Path.Combine(sdkRoot, "platform-tools", "adb.exe");
if (File.Exists(adb))
return adb;
adb = Path.Combine(sdkRoot, "adb.exe");
if (File.Exists(adb))
return adb;
return null;
}
}