Skip to content

Instantly share code, notes, and snippets.

@Naphier
Last active May 20, 2017 23:45
Show Gist options
  • Save Naphier/8fc8dd377d572cbce320a8f47e04cea2 to your computer and use it in GitHub Desktop.
Save Naphier/8fc8dd377d572cbce320a8f47e04cea2 to your computer and use it in GitHub Desktop.
Unity3D Editor Utility to install a built apk to multiple android devices.
using UnityEngine;
using UnityEditor;
using UnityEditor.Callbacks;
using System.IO;
using System.Diagnostics;
using System.Collections.Generic;
using System.Threading;
using Debug = UnityEngine.Debug;
/// <summary>
/// Automates installation to all USB connected Android devices.
/// Some devices do not properly report success.
/// Needs to be made async so that progress can be reported.
///
/// Set AUTORUN = false to only allow use via the Editor menu.
///
/// </summary>
#pragma warning disable 0162
public class InstallToAllAndroidDevices : EditorWindow
{
private const string ANDROID_SDK_PATH_PREF_KEY = "AndroidSdkRoot";
private const string LAST_APK_PATH_PREF_KEY = "LastApkBuildPath-InstallToAll";
private const bool AUTORUN = false;
[PostProcessBuild]
public static void OnPostprocessBuild(BuildTarget target, string pathToBuiltProject)
{
if (target != BuildTarget.Android)
return;
EditorPrefs.SetString(LAST_APK_PATH_PREF_KEY, pathToBuiltProject);
if (AUTORUN)
InstallToAll(pathToBuiltProject);
}
[MenuItem("Edit/Install last APK to all")]
public static void RunIt()
{
InstallToAll(EditorPrefs.GetString(LAST_APK_PATH_PREF_KEY, ""));
}
private static bool running = false;
private static bool started = false;
private static void InstallToAll(string apkPath)
{
string title = "Installing to connected devices";
started = true;
EditorUtility.ClearProgressBar();
EditorApplication.update += OnGUI;
EditorUtility.DisplayProgressBar(title, apkPath, 0);
Debug.LogFormat("Installing '{0}' to all connected Android devices.", apkPath);
List<string> devices = GetAdbDevices();
if (devices.Count <= 0)
{
Debug.LogError("No Android devices found");
return;
}
string adbPath = GetAdbPath();
if (string.IsNullOrEmpty(adbPath))
return;
if (!File.Exists(apkPath))
{
Debug.LogErrorFormat("Could not find APK file at: '{0}'", apkPath);
return;
}
running = true;
for (int i = 0; i < devices.Count; i++)
{
Process adbProcess = new Process();
adbProcess.StartInfo.UseShellExecute = false;
adbProcess.StartInfo.RedirectStandardOutput = true;
adbProcess.StartInfo.RedirectStandardError = true;
adbProcess.StartInfo.CreateNoWindow = true;
adbProcess.StartInfo.FileName = adbPath;
adbProcess.EnableRaisingEvents = true;
List<string> errors = new List<string>();
adbProcess.ErrorDataReceived += (sender, args) =>
{
errors.Add(args.Data);
};
Debug.Log("Installing to device: " + devices[i]);
adbProcess.StartInfo.Arguments = "-s " + devices[i] + " install -r \""+ apkPath + "\"";
EditorUtility.DisplayProgressBar(title, devices[i], (i + 1f) / (devices.Count + 1f));
adbProcess.Exited += (o, e) =>
{
List<string> output = new List<string>();
string line = "";
while ((line = adbProcess.StandardOutput.ReadLine()) != null)
{
if (line.Length > 0 && line[0] != '[')
output.Add(line);
}
//adbProcess.WaitForExit();
if (output.Count > 0 && output[output.Count - 1].ToLower() == "success")
Debug.Log("Success!");
else
{
var sb = new System.Text.StringBuilder();
sb.AppendLine("Failure! Error count: " + errors.Count);
foreach (var item in errors)
{
sb.AppendLine(item);
}
errors.Clear();
sb.AppendLine("Output:");
foreach (var item in output)
{
sb.AppendLine(item);
}
Debug.LogWarning(sb);
}
if (i >= devices.Count - 1)
{
running = false;
}
};
adbProcess.Start();
//adbProcess.WaitForExit();
}
}
private static List<string> GetAdbDevices()
{
List<string> devices = new List<string>();
string adbPath = GetAdbPath();
if (string.IsNullOrEmpty(adbPath))
return devices;
Process adbProcess = new Process();
adbProcess.StartInfo.UseShellExecute = false;
adbProcess.StartInfo.RedirectStandardOutput = true;
adbProcess.StartInfo.CreateNoWindow = true;
adbProcess.StartInfo.FileName = adbPath;
adbProcess.StartInfo.Arguments = "devices";
adbProcess.Start();
string line;
while ((line = adbProcess.StandardOutput.ReadLine()) != null)
{
string[] split = line.Split('\t');
if (split.Length > 1)
{
devices.Add(split[0]);
}
}
adbProcess.WaitForExit();
return devices;
}
private static string GetAdbPath()
{
if (!EditorPrefs.HasKey(ANDROID_SDK_PATH_PREF_KEY))
{
Debug.LogError("Please ensure that the Android SDK path is specified in the editor prefs");
return null;
}
string adbPath = Path.Combine(
EditorPrefs.GetString(ANDROID_SDK_PATH_PREF_KEY),
"platform-tools/adb.exe");
if (!File.Exists(adbPath))
{
Debug.LogErrorFormat("Could not find ADB.EXE at the expected path: '{0}'", adbPath);
return null;
}
return adbPath;
}
private static void OnGUI()
{
if (started && !running)
{
started = false;
EditorUtility.ClearProgressBar();
EditorApplication.update -= OnGUI;
}
}
}
@Naphier
Copy link
Author

Naphier commented May 19, 2017

Needs to be made asynchronous so that progress can be reported.

@Naphier
Copy link
Author

Naphier commented May 20, 2017

Made asynchronous with progress bar.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment