296 lines
9.9 KiB
C#
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;
|
|
}
|
|
} |