Implemented async real-time progress updates for drag and drop. Removed deprecated Sideload(...) and CopyOBB(...) functions

Replaced the old sideload/OBB operations with our newer async, progress-aware flows to enable real-time progress feedback in the UI for non-blocking drag and drop operations. Also cleaned up a bit code and made minor logging adjustments. Updated "No Trailer found" message to disappear after 5 seconds
This commit is contained in:
jp64k
2026-02-04 03:47:28 +01:00
parent 3e626c6390
commit 4dc11191f0
3 changed files with 389 additions and 222 deletions

72
ADB.cs
View File

@@ -680,78 +680,6 @@ namespace AndroidSideloader
return $"Total space: {string.Format("{0:0.00}", (double)totalSize / 1000)}GB\nUsed space: {string.Format("{0:0.00}", (double)usedSize / 1000)}GB\nFree space: {string.Format("{0:0.00}", (double)freeSize / 1000)}GB";
}
public static ProcessOutput Sideload(string path, string packagename = "")
{
ProcessOutput ret = new ProcessOutput();
ret += RunAdbCommandToString($"install -g \"{path}\"");
string out2 = ret.Output + ret.Error;
if (out2.Contains("failed"))
{
_ = Logger.Log(out2);
if (out2.Contains("offline") && !settings.NodeviceMode)
{
DialogResult dialogResult2 = FlexibleMessageBox.Show(Program.form, "Device is offline. Press Yes to reconnect, or if you don't wish to connect and just want to download the game (requires unchecking \"Delete games after install\" from settings menu) then press No.", "Device offline.", MessageBoxButtons.YesNoCancel);
}
if (out2.Contains($"signatures do not match previously") || out2.Contains("INSTALL_FAILED_VERSION_DOWNGRADE") || out2.Contains("signatures do not match") || out2.Contains("failed to install"))
{
ret.Error = string.Empty;
ret.Output = string.Empty;
bool cancelClicked = false;
if (!settings.AutoReinstall)
{
Program.form.Invoke((MethodInvoker)(() =>
{
DialogResult dialogResult1 = FlexibleMessageBox.Show(Program.form, "In place upgrade has failed. Rookie can attempt to backup your save data and reinstall the game automatically, however some games do not store their saves in an accessible location (less than 5%). Continue with reinstall?", "In place upgrade failed.", MessageBoxButtons.OKCancel);
if (dialogResult1 == DialogResult.Cancel)
cancelClicked = true;
}));
}
if (cancelClicked)
return ret;
Program.form.changeTitle("Performing reinstall, please wait...");
_ = RunAdbCommandToString("kill-server");
_ = RunAdbCommandToString("devices");
_ = RunAdbCommandToString($"pull \"/sdcard/Android/data/{MainForm.CurrPCKG}\" \"{Environment.CurrentDirectory}\"");
Program.form.changeTitle("Uninstalling game...");
_ = Sideloader.UninstallGame(MainForm.CurrPCKG);
Program.form.changeTitle("Reinstalling game...");
ret += RunAdbCommandToString($"install -g \"{path}\"");
_ = RunAdbCommandToString($"push \"{Environment.CurrentDirectory}\\{MainForm.CurrPCKG}\" /sdcard/Android/data/");
string directoryToDelete = Path.Combine(Environment.CurrentDirectory, MainForm.CurrPCKG);
if (Directory.Exists(directoryToDelete))
{
if (directoryToDelete != Environment.CurrentDirectory)
{
FileSystemUtilities.TryDeleteDirectory(directoryToDelete);
}
}
Program.form.changeTitle("");
return ret;
}
}
Program.form.changeTitle("");
return ret;
}
public static ProcessOutput CopyOBB(string path)
{
string folder = Path.GetFileName(path);
string lastFolder = Path.GetFileName(path);
return folder.Contains(".")
? RunAdbCommandToString($"shell rm -rf \"/sdcard/Android/obb/{lastFolder}\" && mkdir \"/sdcard/Android/obb/{lastFolder}\"") + RunAdbCommandToString($"push \"{path}\" \"/sdcard/Android/obb\"")
: new ProcessOutput("No OBB Folder found");
}
}
internal class EtaEstimator

View File

@@ -872,22 +872,52 @@ namespace AndroidSideloader
}
ADB.DeviceID = GetDeviceID();
Thread t1 = new Thread(() =>
{
output += ADB.Sideload(path);
})
{
IsBackground = true
};
t1.Start();
string filename = Path.GetFileName(path);
changeTitle($"Installing {filename}...");
progressBar.IsIndeterminate = false;
progressBar.OperationType = "Installing";
progressBar.Value = 0;
progressBar.StatusText = "Preparing...";
while (t1.IsAlive)
output = await ADB.SideloadWithProgressAsync(
path,
(percent, eta) => this.Invoke(() =>
{
await Task.Delay(100);
if (percent == 0)
{
progressBar.IsIndeterminate = true;
progressBar.OperationType = "Installing";
}
else
{
progressBar.IsIndeterminate = false;
progressBar.Value = percent;
}
UpdateProgressStatus("Installing", percent: (int)Math.Round(percent), eta: eta);
progressBar.StatusText = $"Installing · {percent:0.0}%";
}),
status => this.Invoke(() =>
{
if (!string.IsNullOrEmpty(status))
{
if (status.Contains("Completing Installation"))
{
speedLabel.Text = status;
}
progressBar.StatusText = status;
}
}),
"",
filename);
// Reset UI on completion
progressBar.Value = 0;
progressBar.StatusText = "";
progressBar.IsIndeterminate = false;
speedLabel.Text = "";
changeTitle("");
showAvailableSpace();
ShowPrcOutput(output);
}
@@ -980,31 +1010,45 @@ namespace AndroidSideloader
progressBar.IsIndeterminate = false;
progressBar.Value = 0;
progressBar.OperationType = "Copying OBB";
progressBar.StatusText = "Preparing...";
string currentStatusBase = string.Empty;
output = await ADB.CopyOBBWithProgressAsync(
path,
(progress, eta) => this.Invoke(() =>
(percent, eta) => this.Invoke(() =>
{
progressBar.Value = progress;
string etaStr = eta.HasValue && eta.Value.TotalSeconds > 0
? $" · ETA: {eta.Value:mm\\:ss}"
: "";
speedLabel.Text = $"Progress: {progress}%{etaStr}";
progressBar.Value = percent;
UpdateProgressStatus("Copying OBB", percent: (int)Math.Round(percent), eta: eta);
if (!string.IsNullOrEmpty(currentStatusBase))
{
progressBar.StatusText = $"{currentStatusBase} · {percent:0.0}%";
}
else
{
progressBar.StatusText = $"Copying · {percent:0.0}%";
}
}),
status => this.Invoke(() =>
{
currentStatusBase = status ?? string.Empty;
if (!string.IsNullOrEmpty(status))
{
progressBar.StatusText = status;
}
}),
folderName);
progressBar.Value = 100;
// Reset UI on completion
progressBar.Value = 0;
progressBar.StatusText = "";
changeTitle("Done.");
showAvailableSpace();
ShowPrcOutput(output);
changeTitle("");
progressBar.IsIndeterminate = false;
speedLabel.Text = "";
changeTitle("");
showAvailableSpace();
ShowPrcOutput(output);
}
}
@@ -1717,23 +1761,49 @@ namespace AndroidSideloader
};
if (dialog.Show(Handle))
{
Thread t1 = new Thread(() =>
{
changeTitle("Copying OBB folders to device...");
progressBar.IsIndeterminate = false;
progressBar.Value = 0;
progressBar.OperationType = "Copying OBB";
progressBar.StatusText = "Preparing...";
Sideloader.RecursiveOutput = new ProcessOutput(String.Empty, String.Empty);
Sideloader.RecursiveCopyOBB(dialog.FileName);
})
string currentStatusBase = string.Empty;
await Sideloader.RecursiveCopyOBBAsync(
dialog.FileName,
(percent, eta) => this.Invoke(() =>
{
IsBackground = true
};
t1.Start();
progressBar.Value = percent;
UpdateProgressStatus("Copying OBB", percent: (int)Math.Round(percent), eta: eta);
if (!string.IsNullOrEmpty(currentStatusBase))
{
progressBar.StatusText = $"{currentStatusBase} · {percent:0.0}%";
}
else
{
progressBar.StatusText = $"Copying · {percent:0.0}%";
}
}),
status => this.Invoke(() =>
{
currentStatusBase = status ?? string.Empty;
if (!string.IsNullOrEmpty(status))
{
changeTitle($"Copying: {status}");
}
}));
// Reset UI on completion
progressBar.Value = 0;
progressBar.StatusText = "";
progressBar.IsIndeterminate = false;
speedLabel.Text = "";
changeTitle("");
showAvailableSpace();
while (t1.IsAlive)
{
await Task.Delay(100);
}
ShowPrcOutput(Sideloader.RecursiveOutput);
}
}
@@ -1778,23 +1848,45 @@ namespace AndroidSideloader
if (!data.Contains("+") && !data.Contains("_") && data.Contains("."))
{
_ = Logger.Log($"Copying {data} to device");
changeTitle($"Copying {data} to device...");
string folderName = Path.GetFileName(data);
changeTitle($"Copying {folderName} to device...");
Thread t2 = new Thread(() =>
progressBar.IsIndeterminate = false;
progressBar.Value = 0;
progressBar.OperationType = "Copying OBB";
progressBar.StatusText = "Preparing...";
{
output += ADB.CopyOBB(data);
})
{
IsBackground = true
};
t2.Start();
string currentStatusBase = string.Empty;
while (t2.IsAlive)
output += await ADB.CopyOBBWithProgressAsync(
data,
(percent, eta) => this.Invoke(() =>
{
await Task.Delay(100);
progressBar.Value = percent;
UpdateProgressStatus("Copying OBB", percent: (int)Math.Round(percent), eta: eta);
if (!string.IsNullOrEmpty(currentStatusBase))
{
progressBar.StatusText = $"{currentStatusBase} · {percent:0.0}%";
}
else
{
progressBar.StatusText = $"Copying · {percent:0.0}%";
}
}),
status => this.Invoke(() =>
{
currentStatusBase = status ?? string.Empty;
if (!string.IsNullOrEmpty(status))
{
changeTitle($"Copying: {status}");
}
}),
folderName);
// Reset UI after this operation
progressBar.StatusText = "";
speedLabel.Text = "";
changeTitle("");
settings.CurrPckg = dir;
settings.Save();
@@ -1829,42 +1921,88 @@ namespace AndroidSideloader
};
t3.Tick += timer_Tick4;
t3.Start();
changeTitle($"Sideloading APK ({filename})");
Thread t2 = new Thread(() =>
changeTitle($"Sideloading APK ({filename})");
progressBar.IsIndeterminate = false;
progressBar.Value = 0;
progressBar.OperationType = "Installing";
progressBar.StatusText = "Preparing...";
output += await ADB.SideloadWithProgressAsync(
file2,
(percent, eta) => this.Invoke(() =>
{
output += ADB.Sideload(file2);
})
if (percent == 0)
{
IsBackground = true
};
t2.Start();
while (t2.IsAlive)
{
await Task.Delay(100);
progressBar.IsIndeterminate = true;
progressBar.OperationType = "Installing";
}
else
{
progressBar.IsIndeterminate = false;
progressBar.Value = percent;
}
UpdateProgressStatus("Installing", percent: (int)Math.Round(percent), eta: eta);
progressBar.StatusText = $"Installing · {percent:0.0}%";
}),
status => this.Invoke(() =>
{
if (!string.IsNullOrEmpty(status))
{
if (status.Contains("Completing Installation"))
{
speedLabel.Text = status;
}
progressBar.StatusText = status;
}
}),
cmdout,
filename);
t3.Stop();
// Reset after APK install
progressBar.StatusText = "";
speedLabel.Text = "";
if (Directory.Exists($"{pathname}\\{cmdout}"))
{
_ = Logger.Log($"Copying OBB folder to device- {cmdout}");
changeTitle($"Copying OBB folder to device...");
Thread t1 = new Thread(() =>
progressBar.IsIndeterminate = false;
progressBar.Value = 0;
progressBar.OperationType = "Copying OBB";
progressBar.StatusText = "Preparing...";
string obbStatusBase = string.Empty;
output += await ADB.CopyOBBWithProgressAsync(
$"{pathname}\\{cmdout}",
(percent, eta) => this.Invoke(() =>
{
if (!string.IsNullOrEmpty(cmdout))
progressBar.Value = percent;
UpdateProgressStatus("Copying OBB", percent: (int)Math.Round(percent), eta: eta);
if (!string.IsNullOrEmpty(obbStatusBase))
{
_ = ADB.RunAdbCommandToString($"shell rm -rf \"/sdcard/Android/obb/{cmdout}\" && mkdir \"/sdcard/Android/obb/{cmdout}\"");
progressBar.StatusText = $"{obbStatusBase} · {percent:0.0}%";
}
_ = ADB.RunAdbCommandToString($"push \"{pathname}\\{cmdout}\" /sdcard/Android/obb/");
})
else
{
IsBackground = true
};
t1.Start();
while (t1.IsAlive)
{
await Task.Delay(100);
progressBar.StatusText = $"Copying · {percent:0.0}%";
}
}),
status => this.Invoke(() =>
{
obbStatusBase = status ?? string.Empty;
}),
cmdout);
// Reset after OBB copy
progressBar.StatusText = "";
speedLabel.Text = "";
changeTitle("");
}
}
@@ -1900,24 +2038,46 @@ namespace AndroidSideloader
string[] folders = Directory.GetDirectories(data);
foreach (string folder in folders)
{
string folderName = Path.GetFileName(folder);
_ = Logger.Log($"Copying {folder} to device");
changeTitle($"Copying {folder} to device...");
changeTitle($"Copying {folderName} to device...");
Thread t2 = new Thread(() =>
progressBar.IsIndeterminate = false;
progressBar.Value = 0;
progressBar.OperationType = "Copying OBB";
progressBar.StatusText = "Preparing...";
{
output += ADB.CopyOBB(folder);
})
{
IsBackground = true
};
t2.Start();
string folderStatusBase = string.Empty;
while (t2.IsAlive)
output += await ADB.CopyOBBWithProgressAsync(
folder,
(percent, eta) => this.Invoke(() =>
{
await Task.Delay(100);
progressBar.Value = percent;
UpdateProgressStatus("Copying OBB", percent: (int)Math.Round(percent), eta: eta);
if (!string.IsNullOrEmpty(folderStatusBase))
{
progressBar.StatusText = $"{folderStatusBase} · {percent:0.0}%";
}
else
{
progressBar.StatusText = $"Copying · {percent:0.0}%";
}
}),
status => this.Invoke(() =>
{
folderStatusBase = status ?? string.Empty;
if (!string.IsNullOrEmpty(status))
{
changeTitle($"Copying: {status}");
}
}),
folderName);
// Reset after folder copy
progressBar.StatusText = "";
speedLabel.Text = "";
changeTitle("");
settings.CurrPckg = dir;
settings.Save();
@@ -1981,43 +2141,85 @@ namespace AndroidSideloader
timer.Start();
changeTitle($"Installing {dataname}...");
progressBar.IsIndeterminate = false;
progressBar.Value = 0;
progressBar.OperationType = "Installing";
progressBar.StatusText = "Preparing...";
Thread t1 = new Thread(() =>
output += await ADB.SideloadWithProgressAsync(
data,
(percent, eta) => this.Invoke(() =>
{
output += ADB.Sideload(data);
})
if (percent == 0)
{
IsBackground = true
};
t1.Start();
while (t1.IsAlive)
{
await Task.Delay(100);
progressBar.IsIndeterminate = true;
progressBar.OperationType = "Installing";
}
else
{
progressBar.IsIndeterminate = false;
progressBar.Value = percent;
}
UpdateProgressStatus("Installing", percent: (int)Math.Round(percent), eta: eta);
progressBar.StatusText = $"Installing · {percent:0.0}%";
}),
status => this.Invoke(() =>
{
if (!string.IsNullOrEmpty(status))
{
if (status.Contains("Completing Installation"))
{
speedLabel.Text = status;
}
progressBar.StatusText = status;
}
}),
cmdout,
dataname);
timer.Stop();
// Reset after APK install
progressBar.StatusText = "";
speedLabel.Text = "";
if (Directory.Exists($"{pathname}\\{cmdout}"))
{
_ = Logger.Log($"Copying OBB folder to device- {cmdout}");
changeTitle($"Copying OBB folder to device...");
Thread t2 = new Thread(() =>
{
if (!string.IsNullOrEmpty(cmdout))
{
_ = ADB.RunAdbCommandToString($"shell rm -rf \"/sdcard/Android/obb/{cmdout}\" && mkdir \"/sdcard/Android/obb/{cmdout}\"");
}
_ = ADB.RunAdbCommandToString($"push \"{pathname}\\{cmdout}\" /sdcard/Android/obb/");
})
{
IsBackground = true
};
t2.Start();
while (t2.IsAlive)
{
await Task.Delay(100);
}
progressBar.IsIndeterminate = false;
progressBar.Value = 0;
progressBar.OperationType = "Copying OBB";
progressBar.StatusText = "Preparing...";
string obbStatusBase = string.Empty;
output += await ADB.CopyOBBWithProgressAsync(
$"{pathname}\\{cmdout}",
(percent, eta) => this.Invoke(() =>
{
progressBar.Value = percent;
UpdateProgressStatus("Copying OBB", percent: (int)Math.Round(percent), eta: eta);
if (!string.IsNullOrEmpty(obbStatusBase))
{
progressBar.StatusText = $"{obbStatusBase} · {percent:0.0}%";
}
else
{
progressBar.StatusText = $"Copying · {percent:0.0}%";
}
}),
status => this.Invoke(() =>
{
obbStatusBase = status ?? string.Empty;
}),
cmdout);
// Reset after OBB copy
progressBar.StatusText = "";
speedLabel.Text = "";
changeTitle("");
}
}
@@ -2034,21 +2236,41 @@ namespace AndroidSideloader
File.Copy(data, Path.Combine(foldername, filename));
path = foldername;
Thread t1 = new Thread(() =>
{
output += ADB.CopyOBB(path);
})
{
IsBackground = true
};
_ = Logger.Log($"Copying OBB folder to device- {path}");
changeTitle($"Copying OBB folder to device ({filename})");
t1.Start();
while (t1.IsAlive)
progressBar.IsIndeterminate = false;
progressBar.Value = 0;
progressBar.OperationType = "Copying OBB";
progressBar.StatusText = "Preparing...";
string obbStatusBase = string.Empty;
output += await ADB.CopyOBBWithProgressAsync(
path,
(percent, eta) => this.Invoke(() =>
{
await Task.Delay(100);
progressBar.Value = percent;
UpdateProgressStatus("Copying OBB", percent: (int)Math.Round(percent), eta: eta);
if (!string.IsNullOrEmpty(obbStatusBase))
{
progressBar.StatusText = $"{obbStatusBase} · {percent:0.0}%";
}
else
{
progressBar.StatusText = $"Copying · {percent:0.0}%";
}
}),
status => this.Invoke(() =>
{
obbStatusBase = status ?? string.Empty;
}),
filename);
// Reset after OBB copy
progressBar.StatusText = "";
speedLabel.Text = "";
FileSystemUtilities.TryDeleteDirectory(foldername);
changeTitle("");
@@ -2106,7 +2328,11 @@ namespace AndroidSideloader
}
}
// Final reset of all UI elements
progressBar.Value = 0;
progressBar.StatusText = "";
progressBar.IsIndeterminate = false;
speedLabel.Text = "";
showAvailableSpace();
ShowPrcOutput(output);
@@ -6000,7 +6226,7 @@ function onYouTubeIframeAPIReady() {
var videoId = await ResolveVideoIdAsync(CurrentGameName);
if (string.IsNullOrEmpty(videoId))
{
changeTitle("No Trailer found");
changeTitle("No Trailer found", true);
ShowVideoPlaceholder();
}
else

View File

@@ -1,13 +1,14 @@
using JR.Utils.GUI.Forms;
using AndroidSideloader.Utilities;
using JR.Utils.GUI.Forms;
using System;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Management;
using System.Net;
using System.Text.RegularExpressions;
using System.Drawing;
using System.Threading.Tasks;
using System.Windows.Forms;
using AndroidSideloader.Utilities;
namespace AndroidSideloader
{
@@ -84,9 +85,12 @@ namespace AndroidSideloader
return output;
}
//Recursive sideload any apk fileD
//Recursive sideload any apk file
public static ProcessOutput RecursiveOutput = new ProcessOutput();
public static void RecursiveSideload(string FolderPath)
public static async Task RecursiveSideloadAsync(
string FolderPath,
Action<float, TimeSpan?> progressCallback = null,
Action<string> statusCallback = null)
{
try
{
@@ -94,31 +98,40 @@ namespace AndroidSideloader
{
if (Path.GetExtension(f) == ".apk")
{
RecursiveOutput += ADB.Sideload(f);
string gameName = Path.GetFileNameWithoutExtension(f);
statusCallback?.Invoke(gameName);
RecursiveOutput += await ADB.SideloadWithProgressAsync(f, progressCallback, statusCallback, "", gameName);
}
}
foreach (string d in Directory.GetDirectories(FolderPath))
{
RecursiveSideload(d);
await RecursiveSideloadAsync(d, progressCallback, statusCallback);
}
}
catch (Exception ex) { _ = Logger.Log(ex.Message, LogLevel.ERROR); }
}
//Recursive copy any obb folder
public static void RecursiveCopyOBB(string FolderPath)
public static async Task RecursiveCopyOBBAsync(
string FolderPath,
Action<float, TimeSpan?> progressCallback = null,
Action<string> statusCallback = null)
{
try
{
foreach (string f in Directory.GetFiles(FolderPath))
{
RecursiveOutput += ADB.CopyOBB(f);
}
foreach (string d in Directory.GetDirectories(FolderPath))
{
RecursiveCopyOBB(d);
string folderName = Path.GetFileName(d);
if (folderName.Contains("."))
{
statusCallback?.Invoke(folderName);
RecursiveOutput += await ADB.CopyOBBWithProgressAsync(d, progressCallback, statusCallback, folderName);
}
else
{
await RecursiveCopyOBBAsync(d, progressCallback, statusCallback);
}
}
}
catch (Exception ex) { _ = Logger.Log(ex.Message, LogLevel.ERROR); }