Compare commits
28 Commits
v3.0-beta2
...
v3.0-beta4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
58cb75c38c | ||
|
|
4383b9d398 | ||
|
|
b3ce3ab214 | ||
|
|
ae72432aee | ||
|
|
5a939d6234 | ||
|
|
e6ce947700 | ||
|
|
d24df061df | ||
|
|
1b06ab7981 | ||
|
|
5f4cfc09fe | ||
|
|
1de339da75 | ||
|
|
4f653f2131 | ||
|
|
3148ddcfa3 | ||
|
|
b793d2a140 | ||
|
|
5f16ad13e2 | ||
|
|
ddb503feec | ||
|
|
1bcfbd132d | ||
|
|
9862a6a8ca | ||
|
|
9ef3c9264e | ||
|
|
12c371da84 | ||
|
|
782ead1c1e | ||
|
|
e9f77449f0 | ||
|
|
fd77c4db8b | ||
|
|
d9a8d1c460 | ||
|
|
c5b151471a | ||
|
|
2f843bc458 | ||
|
|
f528520024 | ||
|
|
a92d4c0267 | ||
|
|
acaea1d243 |
230
ADB.cs
230
ADB.cs
@@ -154,13 +154,13 @@ namespace AndroidSideloader
|
||||
// Copies and installs an APK with real-time progress reporting using AdvancedSharpAdbClient
|
||||
public static async Task<ProcessOutput> SideloadWithProgressAsync(
|
||||
string path,
|
||||
Action<int> progressCallback = null,
|
||||
Action<float, TimeSpan?> progressCallback = null,
|
||||
Action<string> statusCallback = null,
|
||||
string packagename = "",
|
||||
string gameName = "")
|
||||
{
|
||||
statusCallback?.Invoke("Installing APK...");
|
||||
progressCallback?.Invoke(0);
|
||||
progressCallback?.Invoke(0, null);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -175,43 +175,87 @@ namespace AndroidSideloader
|
||||
|
||||
statusCallback?.Invoke("Installing APK...");
|
||||
|
||||
// Throttle UI updates to prevent lag
|
||||
DateTime lastProgressUpdate = DateTime.MinValue;
|
||||
float lastReportedPercent = -1;
|
||||
const int ThrottleMs = 100; // Update UI every 100ms
|
||||
|
||||
// Shared ETA engine (percent-units)
|
||||
var eta = new EtaEstimator(alpha: 0.05, reanchorThreshold: 0.20);
|
||||
|
||||
// Create install progress handler
|
||||
Action<InstallProgressEventArgs> installProgress = (args) =>
|
||||
{
|
||||
// Map PackageInstallProgressState to percentage
|
||||
int percent = 0;
|
||||
float percent = 0;
|
||||
string status = null;
|
||||
TimeSpan? displayEta = null;
|
||||
|
||||
switch (args.State)
|
||||
{
|
||||
case PackageInstallProgressState.Preparing:
|
||||
percent = 0;
|
||||
statusCallback?.Invoke("Preparing...");
|
||||
status = "Preparing...";
|
||||
eta.Reset();
|
||||
break;
|
||||
|
||||
case PackageInstallProgressState.Uploading:
|
||||
percent = (int)Math.Round(args.UploadProgress);
|
||||
statusCallback?.Invoke($"Installing · {args.UploadProgress:F0}%");
|
||||
percent = (float)args.UploadProgress;
|
||||
|
||||
// Update ETA engine using percent as units (0..100)
|
||||
if (percent > 0 && percent < 100)
|
||||
{
|
||||
eta.Update(totalUnits: 100, doneUnits: (long)Math.Round(percent));
|
||||
displayEta = eta.GetDisplayEta();
|
||||
}
|
||||
else
|
||||
{
|
||||
displayEta = eta.GetDisplayEta();
|
||||
}
|
||||
|
||||
status = $"Installing · {percent:0.0}%";
|
||||
break;
|
||||
|
||||
case PackageInstallProgressState.Installing:
|
||||
percent = 100;
|
||||
statusCallback?.Invoke("Completing Installation...");
|
||||
status = "Completing Installation...";
|
||||
displayEta = null;
|
||||
break;
|
||||
|
||||
case PackageInstallProgressState.Finished:
|
||||
percent = 100;
|
||||
statusCallback?.Invoke("");
|
||||
status = "";
|
||||
displayEta = null;
|
||||
break;
|
||||
|
||||
default:
|
||||
percent = 50;
|
||||
percent = 100;
|
||||
status = "";
|
||||
displayEta = null;
|
||||
break;
|
||||
}
|
||||
progressCallback?.Invoke(percent);
|
||||
|
||||
var updateNow = DateTime.UtcNow;
|
||||
bool shouldUpdate = (updateNow - lastProgressUpdate).TotalMilliseconds >= ThrottleMs
|
||||
|| Math.Abs(percent - lastReportedPercent) >= 0.1f
|
||||
|| args.State != PackageInstallProgressState.Uploading;
|
||||
|
||||
if (shouldUpdate)
|
||||
{
|
||||
lastProgressUpdate = updateNow;
|
||||
lastReportedPercent = percent;
|
||||
|
||||
// ETA goes back via progress callback (label); status remains percent-only string for inner bar
|
||||
progressCallback?.Invoke(percent, displayEta);
|
||||
if (status != null) statusCallback?.Invoke(status);
|
||||
}
|
||||
};
|
||||
|
||||
// Install the package with progress
|
||||
await Task.Run(() =>
|
||||
{
|
||||
packageManager.InstallPackage(path, installProgress);
|
||||
});
|
||||
|
||||
progressCallback?.Invoke(100);
|
||||
progressCallback?.Invoke(100, null);
|
||||
statusCallback?.Invoke("");
|
||||
|
||||
return new ProcessOutput($"{gameName}: Success\n");
|
||||
@@ -220,7 +264,6 @@ namespace AndroidSideloader
|
||||
{
|
||||
Logger.Log($"SideloadWithProgressAsync error: {ex.Message}", LogLevel.ERROR);
|
||||
|
||||
// Check for signature mismatch errors
|
||||
if (ex.Message.Contains("INSTALL_FAILED") ||
|
||||
ex.Message.Contains("signatures do not match"))
|
||||
{
|
||||
@@ -241,7 +284,6 @@ namespace AndroidSideloader
|
||||
if (cancelClicked)
|
||||
return new ProcessOutput("", "Installation cancelled by user");
|
||||
|
||||
// Perform reinstall
|
||||
statusCallback?.Invoke("Performing reinstall...");
|
||||
|
||||
try
|
||||
@@ -250,26 +292,22 @@ namespace AndroidSideloader
|
||||
var client = GetAdbClient();
|
||||
var packageManager = new PackageManager(client, device);
|
||||
|
||||
// Backup save data
|
||||
statusCallback?.Invoke("Backing up save data...");
|
||||
_ = RunAdbCommandToString($"pull \"/sdcard/Android/data/{MainForm.CurrPCKG}\" \"{Environment.CurrentDirectory}\"");
|
||||
|
||||
// Uninstall
|
||||
statusCallback?.Invoke("Uninstalling old version...");
|
||||
packageManager.UninstallPackage(packagename);
|
||||
|
||||
// Reinstall with progress
|
||||
statusCallback?.Invoke("Reinstalling game...");
|
||||
Action<InstallProgressEventArgs> reinstallProgress = (args) =>
|
||||
{
|
||||
if (args.State == PackageInstallProgressState.Uploading)
|
||||
{
|
||||
progressCallback?.Invoke((int)Math.Round(args.UploadProgress));
|
||||
progressCallback?.Invoke((float)args.UploadProgress, null);
|
||||
}
|
||||
};
|
||||
packageManager.InstallPackage(path, reinstallProgress);
|
||||
|
||||
// Restore save data
|
||||
statusCallback?.Invoke("Restoring save data...");
|
||||
_ = RunAdbCommandToString($"push \"{Environment.CurrentDirectory}\\{MainForm.CurrPCKG}\" /sdcard/Android/data/");
|
||||
|
||||
@@ -279,7 +317,7 @@ namespace AndroidSideloader
|
||||
Directory.Delete(directoryToDelete, true);
|
||||
}
|
||||
|
||||
progressCallback?.Invoke(100);
|
||||
progressCallback?.Invoke(100, null);
|
||||
return new ProcessOutput($"{gameName}: Reinstall: Success\n", "");
|
||||
}
|
||||
catch (Exception reinstallEx)
|
||||
@@ -295,7 +333,7 @@ namespace AndroidSideloader
|
||||
// Copies OBB folder with real-time progress reporting using AdvancedSharpAdbClient
|
||||
public static async Task<ProcessOutput> CopyOBBWithProgressAsync(
|
||||
string localPath,
|
||||
Action<int> progressCallback = null,
|
||||
Action<float, TimeSpan?> progressCallback = null,
|
||||
Action<string> statusCallback = null,
|
||||
string gameName = "")
|
||||
{
|
||||
@@ -318,7 +356,7 @@ namespace AndroidSideloader
|
||||
string remotePath = $"/sdcard/Android/obb/{folderName}";
|
||||
|
||||
statusCallback?.Invoke($"Preparing: {folderName}");
|
||||
progressCallback?.Invoke(0);
|
||||
progressCallback?.Invoke(0, null);
|
||||
|
||||
// Delete existing OBB folder and create new one
|
||||
ExecuteShellCommand(client, device, $"rm -rf \"{remotePath}\"");
|
||||
@@ -329,6 +367,14 @@ namespace AndroidSideloader
|
||||
long totalBytes = files.Sum(f => new FileInfo(f).Length);
|
||||
long transferredBytes = 0;
|
||||
|
||||
// Throttle UI updates to prevent lag
|
||||
DateTime lastProgressUpdate = DateTime.MinValue;
|
||||
float lastReportedPercent = -1;
|
||||
const int ThrottleMs = 100; // Update UI every 100ms
|
||||
|
||||
// Shared ETA engine (bytes-units)
|
||||
var eta = new EtaEstimator(alpha: 0.10, reanchorThreshold: 0.20);
|
||||
|
||||
statusCallback?.Invoke($"Copying: {folderName}");
|
||||
|
||||
using (var syncService = new SyncService(client, device))
|
||||
@@ -341,9 +387,6 @@ namespace AndroidSideloader
|
||||
string remoteFilePath = $"{remotePath}/{relativePath}";
|
||||
string fileName = Path.GetFileName(file);
|
||||
|
||||
// Let UI know which file we're currently on
|
||||
statusCallback?.Invoke(fileName);
|
||||
|
||||
// Ensure remote directory exists
|
||||
string remoteDir = remoteFilePath.Substring(0, remoteFilePath.LastIndexOf('/'));
|
||||
ExecuteShellCommand(client, device, $"mkdir -p \"{remoteDir}\"");
|
||||
@@ -352,23 +395,37 @@ namespace AndroidSideloader
|
||||
long fileSize = fileInfo.Length;
|
||||
long capturedTransferredBytes = transferredBytes;
|
||||
|
||||
// Progress handler for this file
|
||||
Action<SyncProgressChangedEventArgs> progressHandler = (args) =>
|
||||
{
|
||||
long totalProgressBytes = capturedTransferredBytes + args.ReceivedBytesSize;
|
||||
|
||||
double overallPercent = totalBytes > 0
|
||||
? (totalProgressBytes * 100.0) / totalBytes
|
||||
: 0.0;
|
||||
float overallPercent = totalBytes > 0
|
||||
? (float)(totalProgressBytes * 100.0 / totalBytes)
|
||||
: 0f;
|
||||
|
||||
int overallPercentInt = (int)Math.Round(overallPercent);
|
||||
overallPercentInt = Math.Max(0, Math.Min(100, overallPercentInt));
|
||||
overallPercent = Math.Max(0, Math.Min(100, overallPercent));
|
||||
|
||||
// Single source of truth for UI (bar + label + text)
|
||||
progressCallback?.Invoke(overallPercentInt);
|
||||
// Update ETA engine in bytes
|
||||
if (totalBytes > 0 && totalProgressBytes > 0 && overallPercent < 100)
|
||||
{
|
||||
eta.Update(totalUnits: totalBytes, doneUnits: totalProgressBytes);
|
||||
}
|
||||
|
||||
TimeSpan? displayEta = eta.GetDisplayEta();
|
||||
|
||||
var now2 = DateTime.UtcNow;
|
||||
bool shouldUpdate = (now2 - lastProgressUpdate).TotalMilliseconds >= ThrottleMs
|
||||
|| Math.Abs(overallPercent - lastReportedPercent) >= 0.1f;
|
||||
|
||||
if (shouldUpdate)
|
||||
{
|
||||
lastProgressUpdate = now2;
|
||||
lastReportedPercent = overallPercent;
|
||||
progressCallback?.Invoke(overallPercent, displayEta);
|
||||
statusCallback?.Invoke(fileName);
|
||||
}
|
||||
};
|
||||
|
||||
// Push the file with progress
|
||||
using (var stream = File.OpenRead(file))
|
||||
{
|
||||
await Task.Run(() =>
|
||||
@@ -383,13 +440,11 @@ namespace AndroidSideloader
|
||||
});
|
||||
}
|
||||
|
||||
// Mark this file as fully transferred
|
||||
transferredBytes += fileSize;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure final 100% and clear status
|
||||
progressCallback?.Invoke(100);
|
||||
progressCallback?.Invoke(100, null);
|
||||
statusCallback?.Invoke("");
|
||||
|
||||
return new ProcessOutput($"{gameName}: OBB transfer: Success\n", "");
|
||||
@@ -397,7 +452,6 @@ namespace AndroidSideloader
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Log($"CopyOBBWithProgressAsync error: {ex.Message}", LogLevel.ERROR);
|
||||
|
||||
return new ProcessOutput("", $"{gameName}: OBB transfer: Failed: {ex.Message}\n");
|
||||
}
|
||||
}
|
||||
@@ -656,4 +710,102 @@ namespace AndroidSideloader
|
||||
: new ProcessOutput("No OBB Folder found");
|
||||
}
|
||||
}
|
||||
|
||||
internal class EtaEstimator
|
||||
{
|
||||
private readonly double _alpha; // EWMA smoothing
|
||||
private readonly double _reanchorThreshold; // % difference required to re-anchor
|
||||
private readonly double _minSampleSeconds; // ignore too-short dt
|
||||
|
||||
private DateTime _lastSampleTimeUtc;
|
||||
private long _lastSampleDoneUnits;
|
||||
private double _smoothedUnitsPerSecond;
|
||||
|
||||
private TimeSpan? _etaAnchorValue;
|
||||
private DateTime _etaAnchorTimeUtc;
|
||||
|
||||
public EtaEstimator(double alpha, double reanchorThreshold, double minSampleSeconds = 0.15)
|
||||
{
|
||||
_alpha = alpha;
|
||||
_reanchorThreshold = reanchorThreshold;
|
||||
_minSampleSeconds = minSampleSeconds;
|
||||
Reset();
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_lastSampleTimeUtc = DateTime.UtcNow;
|
||||
_lastSampleDoneUnits = 0;
|
||||
_smoothedUnitsPerSecond = 0;
|
||||
_etaAnchorValue = null;
|
||||
_etaAnchorTimeUtc = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
// Updates internal rate estimate and re-anchors ETA
|
||||
// totalUnits: total work units (e.g., 100 for percent, or totalBytes for bytes)
|
||||
// doneUnits: completed work units so far (e.g., percent, or bytes transferred)
|
||||
public void Update(long totalUnits, long doneUnits)
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
if (totalUnits <= 0) return;
|
||||
|
||||
doneUnits = Math.Max(0, Math.Min(totalUnits, doneUnits));
|
||||
|
||||
long remainingUnits = Math.Max(0, totalUnits - doneUnits);
|
||||
|
||||
double dt = (now - _lastSampleTimeUtc).TotalSeconds;
|
||||
long dUnits = doneUnits - _lastSampleDoneUnits;
|
||||
|
||||
if (dt >= _minSampleSeconds && dUnits > 0)
|
||||
{
|
||||
double instUnitsPerSecond = dUnits / dt;
|
||||
|
||||
if (_smoothedUnitsPerSecond <= 0)
|
||||
_smoothedUnitsPerSecond = instUnitsPerSecond;
|
||||
else
|
||||
_smoothedUnitsPerSecond = _alpha * instUnitsPerSecond + (1 - _alpha) * _smoothedUnitsPerSecond;
|
||||
|
||||
_lastSampleTimeUtc = now;
|
||||
_lastSampleDoneUnits = doneUnits;
|
||||
}
|
||||
|
||||
if (_smoothedUnitsPerSecond > 1e-6 && remainingUnits > 0)
|
||||
{
|
||||
var newEta = TimeSpan.FromSeconds(remainingUnits / _smoothedUnitsPerSecond);
|
||||
if (newEta < TimeSpan.Zero) newEta = TimeSpan.Zero;
|
||||
|
||||
if (!_etaAnchorValue.HasValue)
|
||||
{
|
||||
_etaAnchorValue = newEta;
|
||||
_etaAnchorTimeUtc = now;
|
||||
}
|
||||
else
|
||||
{
|
||||
// What countdown would currently show
|
||||
var predictedNow = _etaAnchorValue.Value - (now - _etaAnchorTimeUtc);
|
||||
if (predictedNow < TimeSpan.Zero) predictedNow = TimeSpan.Zero;
|
||||
|
||||
double baseSeconds = Math.Max(1, predictedNow.TotalSeconds);
|
||||
double diffRatio = Math.Abs(newEta.TotalSeconds - predictedNow.TotalSeconds) / baseSeconds;
|
||||
|
||||
if (diffRatio > _reanchorThreshold)
|
||||
{
|
||||
_etaAnchorValue = newEta;
|
||||
_etaAnchorTimeUtc = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a countdown ETA for UI display
|
||||
public TimeSpan? GetDisplayEta()
|
||||
{
|
||||
if (!_etaAnchorValue.HasValue) return null;
|
||||
|
||||
var remaining = _etaAnchorValue.Value - (DateTime.UtcNow - _etaAnchorTimeUtc);
|
||||
if (remaining < TimeSpan.Zero) remaining = TimeSpan.Zero;
|
||||
|
||||
return TimeSpan.FromSeconds(Math.Ceiling(remaining.TotalSeconds));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,10 @@ namespace AndroidSideloader
|
||||
public AdbCommandForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
// Use same icon as the executable
|
||||
this.Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath);
|
||||
this.ShowIcon = true; // Enable icon
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
|
||||
@@ -190,6 +190,7 @@
|
||||
<Compile Include="GalleryView.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="ModernListView.cs" />
|
||||
<Compile Include="ModernProgessBar.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Windows.Forms;
|
||||
|
||||
/// <summary>
|
||||
@@ -41,23 +42,41 @@ public class ListViewColumnSorter : IComparer
|
||||
ListViewItem listviewX = (ListViewItem)x;
|
||||
ListViewItem listviewY = (ListViewItem)y;
|
||||
|
||||
// Determine if the column requires numeric comparison
|
||||
if (SortColumn == 3 || SortColumn == 5) // Numeric columns: VersionCodeIndex, VersionNameIndex
|
||||
// Special handling for column 6 (Popularity ranking)
|
||||
if (SortColumn == 6)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Parse and compare numeric values directly
|
||||
int xNum = ParseNumber(listviewX.SubItems[SortColumn].Text);
|
||||
int yNum = ParseNumber(listviewY.SubItems[SortColumn].Text);
|
||||
string textX = listviewX.SubItems[SortColumn].Text;
|
||||
string textY = listviewY.SubItems[SortColumn].Text;
|
||||
|
||||
// Compare numerically
|
||||
compareResult = xNum.CompareTo(yNum);
|
||||
}
|
||||
catch
|
||||
// Extract numeric values from "#1", "#10", etc.
|
||||
// "-" represents unranked and should go to the end
|
||||
int rankX = int.MaxValue; // Default for unranked (-)
|
||||
int rankY = int.MaxValue;
|
||||
|
||||
if (textX.StartsWith("#") && int.TryParse(textX.Substring(1), out int parsedX))
|
||||
{
|
||||
// Fallback to string comparison if parsing fails
|
||||
compareResult = ObjectCompare.Compare(listviewX.SubItems[SortColumn].Text, listviewY.SubItems[SortColumn].Text);
|
||||
rankX = parsedX;
|
||||
}
|
||||
|
||||
if (textY.StartsWith("#") && int.TryParse(textY.Substring(1), out int parsedY))
|
||||
{
|
||||
rankY = parsedY;
|
||||
}
|
||||
|
||||
// Compare the numeric ranks
|
||||
compareResult = rankX.CompareTo(rankY);
|
||||
}
|
||||
// Special handling for column 5 (Size)
|
||||
else if (SortColumn == 5)
|
||||
{
|
||||
string textX = listviewX.SubItems[SortColumn].Text;
|
||||
string textY = listviewY.SubItems[SortColumn].Text;
|
||||
|
||||
double sizeX = ParseSize(textX);
|
||||
double sizeY = ParseSize(textY);
|
||||
|
||||
// Compare the numeric sizes
|
||||
compareResult = sizeX.CompareTo(sizeY);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -91,6 +110,49 @@ public class ListViewColumnSorter : IComparer
|
||||
return int.TryParse(text, out int result) ? result : 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses size strings with units (GB/MB) and converts them to MB for comparison.
|
||||
/// </summary>
|
||||
/// <param name="sizeStr">Size string (e.g., "1.23 GB", "123 MB")</param>
|
||||
/// <returns>Size in MB as a double</returns>
|
||||
private double ParseSize(string sizeStr)
|
||||
{
|
||||
if (string.IsNullOrEmpty(sizeStr))
|
||||
return 0;
|
||||
|
||||
// Remove whitespace
|
||||
sizeStr = sizeStr.Trim();
|
||||
|
||||
// Handle new format: "1.23 GB" or "123 MB"
|
||||
if (sizeStr.EndsWith(" GB", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
string numPart = sizeStr.Substring(0, sizeStr.Length - 3).Trim();
|
||||
if (double.TryParse(numPart, System.Globalization.NumberStyles.Any,
|
||||
System.Globalization.CultureInfo.InvariantCulture, out double gb))
|
||||
{
|
||||
return gb * 1024.0; // Convert GB to MB for consistent sorting
|
||||
}
|
||||
}
|
||||
else if (sizeStr.EndsWith(" MB", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
string numPart = sizeStr.Substring(0, sizeStr.Length - 3).Trim();
|
||||
if (double.TryParse(numPart, System.Globalization.NumberStyles.Any,
|
||||
System.Globalization.CultureInfo.InvariantCulture, out double mb))
|
||||
{
|
||||
return mb;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: try parsing as raw number
|
||||
if (double.TryParse(sizeStr, System.Globalization.NumberStyles.Any,
|
||||
System.Globalization.CultureInfo.InvariantCulture, out double rawMb))
|
||||
{
|
||||
return rawMb;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the index of the column to be sorted (default is '0').
|
||||
/// </summary>
|
||||
|
||||
2
DonorsListView.Designer.cs
generated
2
DonorsListView.Designer.cs
generated
@@ -30,7 +30,6 @@ namespace AndroidSideloader
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.components = new System.ComponentModel.Container();
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(DonorsListViewForm));
|
||||
this.DonationTimer = new System.Windows.Forms.Timer(this.components);
|
||||
this.panel1 = new System.Windows.Forms.Panel();
|
||||
this.skip_forever = new AndroidSideloader.RoundButton();
|
||||
@@ -310,7 +309,6 @@ namespace AndroidSideloader
|
||||
this.Controls.Add(this.panel1);
|
||||
this.ForeColor = System.Drawing.Color.White;
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
|
||||
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
|
||||
this.Name = "DonorsListViewForm";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||
this.Load += new System.EventHandler(this.DonorsListViewForm_Load);
|
||||
|
||||
@@ -42,6 +42,10 @@ namespace AndroidSideloader
|
||||
public DonorsListViewForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
// Use same icon as the executable
|
||||
this.Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath);
|
||||
|
||||
ApplyModernTheme();
|
||||
CenterToScreen();
|
||||
|
||||
|
||||
6849
DonorsListView.resx
6849
DonorsListView.resx
File diff suppressed because it is too large
Load Diff
@@ -170,7 +170,7 @@ namespace JR.Utils.GUI.Forms
|
||||
titleLabel.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold);
|
||||
titleLabel.Location = new System.Drawing.Point(0, 0);
|
||||
titleLabel.Name = "titleLabel";
|
||||
titleLabel.Padding = new System.Windows.Forms.Padding(18, 0, 0, 0);
|
||||
titleLabel.Padding = new System.Windows.Forms.Padding(12, 0, 0, 0);
|
||||
titleLabel.Size = new System.Drawing.Size(218, 28);
|
||||
titleLabel.TabIndex = 0;
|
||||
titleLabel.Text = "<Caption>";
|
||||
@@ -198,7 +198,7 @@ namespace JR.Utils.GUI.Forms
|
||||
//
|
||||
button1.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right;
|
||||
button1.DialogResult = System.Windows.Forms.DialogResult.OK;
|
||||
button1.Location = new System.Drawing.Point(16, 80);
|
||||
button1.Location = new System.Drawing.Point(26, 80);
|
||||
button1.Name = "button1";
|
||||
button1.Size = new System.Drawing.Size(75, 28);
|
||||
button1.TabIndex = 2;
|
||||
@@ -222,7 +222,7 @@ namespace JR.Utils.GUI.Forms
|
||||
richTextBoxMessage.BorderStyle = System.Windows.Forms.BorderStyle.None;
|
||||
richTextBoxMessage.DataBindings.Add(new System.Windows.Forms.Binding("Text", FlexibleMessageBoxFormBindingSource, "MessageText", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
|
||||
richTextBoxMessage.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, 0);
|
||||
richTextBoxMessage.Location = new System.Drawing.Point(52, 6);
|
||||
richTextBoxMessage.Location = new System.Drawing.Point(46, 6);
|
||||
richTextBoxMessage.Margin = new System.Windows.Forms.Padding(0);
|
||||
richTextBoxMessage.Name = "richTextBoxMessage";
|
||||
richTextBoxMessage.ReadOnly = true;
|
||||
@@ -259,7 +259,7 @@ namespace JR.Utils.GUI.Forms
|
||||
//
|
||||
button2.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right;
|
||||
button2.DialogResult = System.Windows.Forms.DialogResult.OK;
|
||||
button2.Location = new System.Drawing.Point(97, 80);
|
||||
button2.Location = new System.Drawing.Point(107, 80);
|
||||
button2.Name = "button2";
|
||||
button2.Size = new System.Drawing.Size(75, 28);
|
||||
button2.TabIndex = 3;
|
||||
@@ -277,7 +277,7 @@ namespace JR.Utils.GUI.Forms
|
||||
//
|
||||
button3.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right;
|
||||
button3.DialogResult = System.Windows.Forms.DialogResult.OK;
|
||||
button3.Location = new System.Drawing.Point(178, 80);
|
||||
button3.Location = new System.Drawing.Point(188, 80);
|
||||
button3.Name = "button3";
|
||||
button3.Size = new System.Drawing.Size(75, 28);
|
||||
button3.TabIndex = 0;
|
||||
@@ -843,6 +843,11 @@ namespace JR.Utils.GUI.Forms
|
||||
flexibleMessageBoxForm.richTextBoxMessage.Font = FONT;
|
||||
|
||||
SetDialogSizes(flexibleMessageBoxForm, text, caption);
|
||||
|
||||
// Force panel resize to fix closebutton position
|
||||
int contentWidth = flexibleMessageBoxForm.ClientSize.Width - 16; // 8px padding
|
||||
flexibleMessageBoxForm.titlePanel.Width = contentWidth;
|
||||
|
||||
SetDialogStartPosition(flexibleMessageBoxForm, owner);
|
||||
|
||||
return flexibleMessageBoxForm.ShowDialog(owner);
|
||||
|
||||
200
GalleryView.cs
200
GalleryView.cs
@@ -6,6 +6,7 @@ using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Forms;
|
||||
|
||||
public enum SortField { Name, LastUpdated, Size, Popularity }
|
||||
@@ -51,7 +52,7 @@ public class FastGalleryPanel : Control
|
||||
|
||||
// Interaction
|
||||
private int _hoveredIndex = -1;
|
||||
private int _selectedIndex = -1;
|
||||
public int _selectedIndex = -1;
|
||||
private bool _isHoveringDeleteButton = false;
|
||||
|
||||
// Context Menu & Favorites
|
||||
@@ -64,7 +65,7 @@ public class FastGalleryPanel : Control
|
||||
|
||||
// Visual constants
|
||||
private const int CORNER_RADIUS = 10;
|
||||
private const int THUMB_CORNER_RADIUS = 6;
|
||||
private const int THUMB_CORNER_RADIUS = 8;
|
||||
private const float HOVER_SCALE = 1.07f;
|
||||
private const float ANIMATION_SPEED = 0.25f;
|
||||
private const float SCROLL_SMOOTHING = 0.3f;
|
||||
@@ -90,6 +91,24 @@ public class FastGalleryPanel : Control
|
||||
public event EventHandler<int> TileRightClicked;
|
||||
public event EventHandler<SortField> SortChanged;
|
||||
|
||||
[DllImport("dwmapi.dll")]
|
||||
private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize);
|
||||
|
||||
[DllImport("uxtheme.dll", CharSet = CharSet.Unicode)]
|
||||
private static extern int SetWindowTheme(IntPtr hwnd, string pszSubAppName, string pszSubIdList);
|
||||
|
||||
private void ApplyModernScrollbars()
|
||||
{
|
||||
if (_scrollBar == null || !_scrollBar.IsHandleCreated) return;
|
||||
|
||||
int dark = 1;
|
||||
int hr = DwmSetWindowAttribute(_scrollBar.Handle, 20, ref dark, sizeof(int));
|
||||
if (hr != 0)
|
||||
DwmSetWindowAttribute(_scrollBar.Handle, 19, ref dark, sizeof(int));
|
||||
|
||||
if (SetWindowTheme(_scrollBar.Handle, "DarkMode_Explorer", null) != 0)
|
||||
SetWindowTheme(_scrollBar.Handle, "Explorer", null);
|
||||
}
|
||||
private class TileAnimationState
|
||||
{
|
||||
public float Scale = 1.0f;
|
||||
@@ -155,6 +174,8 @@ public class FastGalleryPanel : Control
|
||||
_isScrolling = false;
|
||||
Invalidate();
|
||||
};
|
||||
|
||||
_scrollBar.HandleCreated += (s, e) => ApplyModernScrollbars();
|
||||
Controls.Add(_scrollBar);
|
||||
|
||||
// Animation timer (~120fps)
|
||||
@@ -353,10 +374,10 @@ public class FastGalleryPanel : Control
|
||||
|
||||
case SortField.Popularity:
|
||||
if (_currentSortDirection == SortDirection.Ascending)
|
||||
_items = _items.OrderBy(i => ParsePopularity(i.SubItems.Count > 6 ? i.SubItems[6].Text : "0"))
|
||||
_items = _items.OrderByDescending(i => ParsePopularity(i.SubItems.Count > 6 ? i.SubItems[6].Text : "-"))
|
||||
.ThenBy(i => i.Text, new GameNameComparer()).ToList();
|
||||
else
|
||||
_items = _items.OrderByDescending(i => ParsePopularity(i.SubItems.Count > 6 ? i.SubItems[6].Text : "0"))
|
||||
_items = _items.OrderBy(i => ParsePopularity(i.SubItems.Count > 6 ? i.SubItems[6].Text : "-"))
|
||||
.ThenBy(i => i.Text, new GameNameComparer()).ToList();
|
||||
break;
|
||||
}
|
||||
@@ -378,12 +399,35 @@ public class FastGalleryPanel : Control
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
private double ParsePopularity(string popStr)
|
||||
private int ParsePopularity(string popStr)
|
||||
{
|
||||
if (double.TryParse(popStr?.Trim(), System.Globalization.NumberStyles.Any,
|
||||
System.Globalization.CultureInfo.InvariantCulture, out double pop))
|
||||
return pop;
|
||||
return 0;
|
||||
if (string.IsNullOrEmpty(popStr))
|
||||
return int.MaxValue; // Unranked goes to end
|
||||
|
||||
popStr = popStr.Trim();
|
||||
|
||||
// Handle new format: "#123" or "-"
|
||||
if (popStr == "-")
|
||||
{
|
||||
return int.MaxValue; // Unranked items sort to the end
|
||||
}
|
||||
|
||||
if (popStr.StartsWith("#"))
|
||||
{
|
||||
string numPart = popStr.Substring(1);
|
||||
if (int.TryParse(numPart, out int rank))
|
||||
{
|
||||
return rank;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: try parsing as raw number
|
||||
if (int.TryParse(popStr, out int rawNum))
|
||||
{
|
||||
return rawNum;
|
||||
}
|
||||
|
||||
return int.MaxValue; // Unparseable goes to end
|
||||
}
|
||||
|
||||
// Custom sort to match list sort behaviour: '_' before digits, digits before letters (case-insensitive)
|
||||
@@ -430,15 +474,54 @@ public class FastGalleryPanel : Control
|
||||
private DateTime ParseDate(string dateStr)
|
||||
{
|
||||
if (string.IsNullOrEmpty(dateStr)) return DateTime.MinValue;
|
||||
string datePart = dateStr.Split(' ')[0];
|
||||
return DateTime.TryParse(datePart, out DateTime date) ? date : DateTime.MinValue;
|
||||
|
||||
string[] formats = { "yyyy-MM-dd HH:mm 'UTC'", "yyyy-MM-dd HH:mm" };
|
||||
|
||||
return DateTime.TryParseExact(
|
||||
dateStr,
|
||||
formats,
|
||||
System.Globalization.CultureInfo.InvariantCulture,
|
||||
System.Globalization.DateTimeStyles.AssumeUniversal | System.Globalization.DateTimeStyles.AdjustToUniversal,
|
||||
out DateTime date)
|
||||
? date
|
||||
: DateTime.MinValue;
|
||||
}
|
||||
|
||||
private double ParseSize(string sizeStr)
|
||||
{
|
||||
if (double.TryParse(sizeStr?.Trim(), System.Globalization.NumberStyles.Any,
|
||||
System.Globalization.CultureInfo.InvariantCulture, out double mb))
|
||||
return mb;
|
||||
if (string.IsNullOrEmpty(sizeStr))
|
||||
return 0;
|
||||
|
||||
// Remove whitespace
|
||||
sizeStr = sizeStr.Trim();
|
||||
|
||||
// Handle new format: "1.23 GB" or "123 MB"
|
||||
if (sizeStr.EndsWith(" GB", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
string numPart = sizeStr.Substring(0, sizeStr.Length - 3).Trim();
|
||||
if (double.TryParse(numPart, System.Globalization.NumberStyles.Any,
|
||||
System.Globalization.CultureInfo.InvariantCulture, out double gb))
|
||||
{
|
||||
return gb * 1024.0; // Convert GB to MB for consistent sorting
|
||||
}
|
||||
}
|
||||
else if (sizeStr.EndsWith(" MB", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
string numPart = sizeStr.Substring(0, sizeStr.Length - 3).Trim();
|
||||
if (double.TryParse(numPart, System.Globalization.NumberStyles.Any,
|
||||
System.Globalization.CultureInfo.InvariantCulture, out double mb))
|
||||
{
|
||||
return mb;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: try parsing as raw number
|
||||
if (double.TryParse(sizeStr, System.Globalization.NumberStyles.Any,
|
||||
System.Globalization.CultureInfo.InvariantCulture, out double rawMb))
|
||||
{
|
||||
return rawMb;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -500,12 +583,12 @@ public class FastGalleryPanel : Control
|
||||
int y = baseY - (scaledH - _tileHeight) / 2;
|
||||
|
||||
// Calculate thumbnail area
|
||||
int thumbPadding = 4;
|
||||
int thumbHeight = scaledH - 26; // Same as in DrawTile
|
||||
int thumbPadding = 2;
|
||||
int thumbHeight = scaledH - (thumbPadding * 2);
|
||||
|
||||
// Position delete button in bottom-right corner of thumbnail
|
||||
int btnX = x + scaledW - DELETE_BUTTON_SIZE - thumbPadding - DELETE_BUTTON_MARGIN;
|
||||
int btnY = y + thumbPadding + thumbHeight - DELETE_BUTTON_SIZE - DELETE_BUTTON_MARGIN;
|
||||
int btnY = y + thumbPadding + thumbHeight - DELETE_BUTTON_SIZE - DELETE_BUTTON_MARGIN - 20;
|
||||
|
||||
return new Rectangle(btnX, btnY, DELETE_BUTTON_SIZE, DELETE_BUTTON_SIZE);
|
||||
}
|
||||
@@ -792,9 +875,26 @@ public class FastGalleryPanel : Control
|
||||
}
|
||||
|
||||
// Thumbnail
|
||||
int thumbPadding = 4;
|
||||
int thumbHeight = scaledH - 26;
|
||||
var thumbRect = new Rectangle(x + thumbPadding, y + thumbPadding, scaledW - thumbPadding * 2, thumbHeight);
|
||||
int thumbPadding = 2;
|
||||
int thumbHeight = scaledH - (thumbPadding * 2);
|
||||
|
||||
var thumbRect = new Rectangle(
|
||||
x + thumbPadding,
|
||||
y + thumbPadding,
|
||||
scaledW - (thumbPadding * 2),
|
||||
thumbHeight
|
||||
);
|
||||
|
||||
// Base (non-scaled) thumbnail size for stable placeholder text layout
|
||||
int baseThumbW = _tileWidth - (thumbPadding * 2);
|
||||
int baseThumbH = _tileHeight - (thumbPadding * 2);
|
||||
|
||||
var baseThumbRect = new Rectangle(
|
||||
thumbRect.X + (thumbRect.Width - baseThumbW) / 2,
|
||||
thumbRect.Y + (thumbRect.Height - baseThumbH) / 2,
|
||||
baseThumbW,
|
||||
baseThumbH
|
||||
);
|
||||
|
||||
string packageName = item.SubItems.Count > 2 ? item.SubItems[2].Text : "";
|
||||
var thumbnail = GetCachedImage(packageName);
|
||||
@@ -817,12 +917,24 @@ public class FastGalleryPanel : Control
|
||||
{
|
||||
using (var brush = new SolidBrush(Color.FromArgb(35, 35, 40)))
|
||||
g.FillPath(brush, thumbPath);
|
||||
using (var textBrush = new SolidBrush(Color.FromArgb(70, 70, 80)))
|
||||
|
||||
// Show game name when thumbnail is missing, centered
|
||||
var nameRect = new Rectangle(baseThumbRect.X + 10, baseThumbRect.Y, baseThumbRect.Width - 20, baseThumbRect.Height);
|
||||
|
||||
using (var font = new Font("Segoe UI", 10f, FontStyle.Bold))
|
||||
{
|
||||
var sf = new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center };
|
||||
g.DrawString("🎮", new Font("Segoe UI Emoji", 18f), textBrush, thumbRect, sf);
|
||||
var sfName = new StringFormat
|
||||
{
|
||||
Alignment = StringAlignment.Center,
|
||||
LineAlignment = StringAlignment.Center,
|
||||
Trimming = StringTrimming.EllipsisCharacter
|
||||
};
|
||||
|
||||
using (var text = new SolidBrush(Color.FromArgb(110, 110, 120)))
|
||||
g.DrawString(item.Text, font, text, nameRect, sfName);
|
||||
}
|
||||
}
|
||||
|
||||
g.Clip = oldClip;
|
||||
}
|
||||
|
||||
@@ -861,7 +973,7 @@ public class FastGalleryPanel : Control
|
||||
// Size badge (top right) - always visible
|
||||
if (item.SubItems.Count > 5)
|
||||
{
|
||||
string sizeText = FormatSize(item.SubItems[5].Text);
|
||||
string sizeText = item.SubItems[5].Text;
|
||||
if (!string.IsNullOrEmpty(sizeText))
|
||||
{
|
||||
DrawRightAlignedBadge(g, sizeText, x + scaledW - thumbPadding - 4, rightBadgeY, 1.0f);
|
||||
@@ -887,12 +999,40 @@ public class FastGalleryPanel : Control
|
||||
}
|
||||
|
||||
// Game name
|
||||
var nameRect = new Rectangle(x + 6, y + thumbHeight + thumbPadding, scaledW - 12, 20);
|
||||
using (var font = new Font("Segoe UI Semibold", 8f))
|
||||
using (var brush = new SolidBrush(TextColor))
|
||||
if (state.TooltipOpacity > 0.01f)
|
||||
{
|
||||
var sf = new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center, Trimming = StringTrimming.EllipsisCharacter, FormatFlags = StringFormatFlags.NoWrap };
|
||||
g.DrawString(item.Text, font, brush, nameRect, sf);
|
||||
int overlayH = 20;
|
||||
var overlayRect = new Rectangle(thumbRect.X, thumbRect.Bottom - overlayH, thumbRect.Width, overlayH);
|
||||
|
||||
// Clip to the exact rounded thumbnail so the overlay corners match perfectly
|
||||
Region oldClip = g.Clip;
|
||||
using (var clipPath = CreateRoundedRectangle(thumbRect, THUMB_CORNER_RADIUS))
|
||||
{
|
||||
g.SetClip(clipPath, CombineMode.Intersect);
|
||||
|
||||
// Slightly overdraw to avoid 1px seams from AA / integer rounding
|
||||
var fillRect = new Rectangle(overlayRect.X - 1, overlayRect.Y, overlayRect.Width + 2, overlayRect.Height + 1);
|
||||
|
||||
using (var overlayBrush = new SolidBrush(Color.FromArgb((int)(180 * state.TooltipOpacity), 0, 0, 0)))
|
||||
g.FillRectangle(overlayBrush, fillRect);
|
||||
|
||||
g.Clip = oldClip;
|
||||
}
|
||||
|
||||
using (var font = new Font("Segoe UI", 8f, FontStyle.Bold))
|
||||
using (var brush = new SolidBrush(Color.FromArgb((int)(TextColor.A * state.TooltipOpacity), TextColor.R, TextColor.G, TextColor.B)))
|
||||
{
|
||||
var sf = new StringFormat
|
||||
{
|
||||
Alignment = StringAlignment.Center,
|
||||
LineAlignment = StringAlignment.Center,
|
||||
Trimming = StringTrimming.EllipsisCharacter,
|
||||
FormatFlags = StringFormatFlags.NoWrap
|
||||
};
|
||||
|
||||
var textRect = new Rectangle(overlayRect.X, overlayRect.Y + 1, overlayRect.Width, overlayRect.Height);
|
||||
g.DrawString(item.Text, font, brush, textRect, sf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -900,7 +1040,7 @@ public class FastGalleryPanel : Control
|
||||
{
|
||||
// Position in bottom-right corner of thumbnail
|
||||
int btnX = tileX + tileWidth - DELETE_BUTTON_SIZE - thumbPadding - DELETE_BUTTON_MARGIN;
|
||||
int btnY = tileY + thumbPadding + thumbHeight - DELETE_BUTTON_SIZE - DELETE_BUTTON_MARGIN;
|
||||
int btnY = tileY + thumbPadding + thumbHeight - DELETE_BUTTON_SIZE - DELETE_BUTTON_MARGIN - 20;
|
||||
var btnRect = new Rectangle(btnX, btnY, DELETE_BUTTON_SIZE, DELETE_BUTTON_SIZE);
|
||||
|
||||
int bgAlpha = (int)(opacity * 255);
|
||||
|
||||
104
MainForm.Designer.cs
generated
104
MainForm.Designer.cs
generated
@@ -34,9 +34,7 @@ namespace AndroidSideloader
|
||||
{
|
||||
this.components = new System.ComponentModel.Container();
|
||||
this.m_combo = new SergeUtils.EasyCompletionComboBox();
|
||||
this.progressBar = new AndroidSideloader.ModernProgressBar();
|
||||
this.speedLabel = new System.Windows.Forms.Label();
|
||||
this.etaLabel = new System.Windows.Forms.Label();
|
||||
this.freeDisclaimer = new System.Windows.Forms.Label();
|
||||
this.gamesQueListBox = new System.Windows.Forms.ListBox();
|
||||
this.devicesComboBox = new System.Windows.Forms.ComboBox();
|
||||
@@ -93,6 +91,7 @@ namespace AndroidSideloader
|
||||
this.speedLabel_Tooltip = new System.Windows.Forms.ToolTip(this.components);
|
||||
this.etaLabel_Tooltip = new System.Windows.Forms.ToolTip(this.components);
|
||||
this.progressDLbtnContainer = new System.Windows.Forms.Panel();
|
||||
this.progressBar = new AndroidSideloader.ModernProgressBar();
|
||||
this.diskLabel = new System.Windows.Forms.Label();
|
||||
this.questStorageProgressBar = new System.Windows.Forms.Panel();
|
||||
this.batteryLevImg = new System.Windows.Forms.PictureBox();
|
||||
@@ -172,60 +171,19 @@ namespace AndroidSideloader
|
||||
this.m_combo.Text = "Select an Installed App...";
|
||||
this.m_combo.Visible = false;
|
||||
//
|
||||
// progressBar
|
||||
//
|
||||
this.progressBar.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.progressBar.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(35)))), ((int)(((byte)(45)))));
|
||||
this.progressBar.BackgroundColor = System.Drawing.Color.FromArgb(((int)(((byte)(28)))), ((int)(((byte)(32)))), ((int)(((byte)(38)))));
|
||||
this.progressBar.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold);
|
||||
this.progressBar.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(93)))), ((int)(((byte)(203)))), ((int)(((byte)(173)))));
|
||||
this.progressBar.IndeterminateColor = System.Drawing.Color.FromArgb(((int)(((byte)(93)))), ((int)(((byte)(203)))), ((int)(((byte)(173)))));
|
||||
this.progressBar.IsIndeterminate = false;
|
||||
this.progressBar.Location = new System.Drawing.Point(1, 18);
|
||||
this.progressBar.Maximum = 100;
|
||||
this.progressBar.Minimum = 0;
|
||||
this.progressBar.MinimumSize = new System.Drawing.Size(200, 13);
|
||||
this.progressBar.Name = "progressBar";
|
||||
this.progressBar.OperationType = "";
|
||||
this.progressBar.ProgressEndColor = System.Drawing.Color.FromArgb(((int)(((byte)(50)))), ((int)(((byte)(160)))), ((int)(((byte)(130)))));
|
||||
this.progressBar.ProgressStartColor = System.Drawing.Color.FromArgb(((int)(((byte)(120)))), ((int)(((byte)(220)))), ((int)(((byte)(190)))));
|
||||
this.progressBar.Radius = 6;
|
||||
this.progressBar.Size = new System.Drawing.Size(983, 13);
|
||||
this.progressBar.StatusText = "";
|
||||
this.progressBar.TabIndex = 7;
|
||||
this.progressBar.TextColor = System.Drawing.Color.FromArgb(((int)(((byte)(230)))), ((int)(((byte)(230)))), ((int)(((byte)(230)))));
|
||||
this.progressBar.Value = 0;
|
||||
//
|
||||
// speedLabel
|
||||
//
|
||||
this.speedLabel.AutoSize = true;
|
||||
this.speedLabel.BackColor = System.Drawing.Color.Transparent;
|
||||
this.speedLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Bold);
|
||||
this.speedLabel.ForeColor = System.Drawing.Color.White;
|
||||
this.speedLabel.Location = new System.Drawing.Point(-2, -3);
|
||||
this.speedLabel.Location = new System.Drawing.Point(-1, 3);
|
||||
this.speedLabel.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.speedLabel.Name = "speedLabel";
|
||||
this.speedLabel.Size = new System.Drawing.Size(152, 16);
|
||||
this.speedLabel.TabIndex = 76;
|
||||
this.speedLabel.Text = "DLS: Speed in MBPS";
|
||||
this.speedLabel_Tooltip.SetToolTip(this.speedLabel, "Current download speed, updates every second, in mbps");
|
||||
//
|
||||
// etaLabel
|
||||
//
|
||||
this.etaLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.etaLabel.BackColor = System.Drawing.Color.Transparent;
|
||||
this.etaLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.etaLabel.ForeColor = System.Drawing.Color.White;
|
||||
this.etaLabel.Location = new System.Drawing.Point(790, -3);
|
||||
this.etaLabel.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.etaLabel.Name = "etaLabel";
|
||||
this.etaLabel.Size = new System.Drawing.Size(196, 18);
|
||||
this.etaLabel.TabIndex = 75;
|
||||
this.etaLabel.Text = "ETA: HH:MM:SS Left";
|
||||
this.etaLabel.TextAlign = System.Drawing.ContentAlignment.TopRight;
|
||||
this.etaLabel_Tooltip.SetToolTip(this.etaLabel, "Estimated time when game will finish download, updates every 5 seconds, format is" +
|
||||
" HH:MM:SS");
|
||||
//
|
||||
// freeDisclaimer
|
||||
//
|
||||
@@ -310,10 +268,12 @@ namespace AndroidSideloader
|
||||
this.DownloadsIndex});
|
||||
this.gamesListView.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.gamesListView.ForeColor = System.Drawing.Color.White;
|
||||
this.gamesListView.FullRowSelect = true;
|
||||
this.gamesListView.HideSelection = false;
|
||||
this.gamesListView.ImeMode = System.Windows.Forms.ImeMode.Off;
|
||||
this.gamesListView.Location = new System.Drawing.Point(258, 44);
|
||||
this.gamesListView.Name = "gamesListView";
|
||||
this.gamesListView.OwnerDraw = true;
|
||||
this.gamesListView.ShowGroups = false;
|
||||
this.gamesListView.Size = new System.Drawing.Size(984, 409);
|
||||
this.gamesListView.TabIndex = 6;
|
||||
@@ -329,39 +289,41 @@ namespace AndroidSideloader
|
||||
// GameNameIndex
|
||||
//
|
||||
this.GameNameIndex.Text = "Game Name";
|
||||
this.GameNameIndex.Width = 158;
|
||||
this.GameNameIndex.Width = 160;
|
||||
//
|
||||
// ReleaseNameIndex
|
||||
//
|
||||
this.ReleaseNameIndex.Text = "Release Name";
|
||||
this.ReleaseNameIndex.Width = 244;
|
||||
this.ReleaseNameIndex.Width = 220;
|
||||
//
|
||||
// PackageNameIndex
|
||||
//
|
||||
this.PackageNameIndex.Text = "Package Name";
|
||||
this.PackageNameIndex.Width = 87;
|
||||
this.PackageNameIndex.Width = 120;
|
||||
//
|
||||
// VersionCodeIndex
|
||||
//
|
||||
this.VersionCodeIndex.Text = "Version";
|
||||
this.VersionCodeIndex.Width = 75;
|
||||
this.VersionCodeIndex.Text = "Version (Rookie/Local)";
|
||||
this.VersionCodeIndex.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
|
||||
this.VersionCodeIndex.Width = 164;
|
||||
//
|
||||
// ReleaseAPKPathIndex
|
||||
//
|
||||
this.ReleaseAPKPathIndex.Text = "Last Updated";
|
||||
this.ReleaseAPKPathIndex.Width = 145;
|
||||
this.ReleaseAPKPathIndex.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
|
||||
this.ReleaseAPKPathIndex.Width = 135;
|
||||
//
|
||||
// VersionNameIndex
|
||||
//
|
||||
this.VersionNameIndex.Text = "Size (MB)";
|
||||
this.VersionNameIndex.TextAlign = System.Windows.Forms.HorizontalAlignment.Right;
|
||||
this.VersionNameIndex.Width = 66;
|
||||
this.VersionNameIndex.Text = "Size";
|
||||
this.VersionNameIndex.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
|
||||
this.VersionNameIndex.Width = 85;
|
||||
//
|
||||
// DownloadsIndex
|
||||
//
|
||||
this.DownloadsIndex.Text = "Popularity";
|
||||
this.DownloadsIndex.TextAlign = System.Windows.Forms.HorizontalAlignment.Right;
|
||||
this.DownloadsIndex.Width = 80;
|
||||
this.DownloadsIndex.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
|
||||
this.DownloadsIndex.Width = 100;
|
||||
//
|
||||
// gamesQueueLabel
|
||||
//
|
||||
@@ -808,14 +770,38 @@ namespace AndroidSideloader
|
||||
this.progressDLbtnContainer.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
|
||||
this.progressDLbtnContainer.BackColor = System.Drawing.Color.Transparent;
|
||||
this.progressDLbtnContainer.Controls.Add(this.progressBar);
|
||||
this.progressDLbtnContainer.Controls.Add(this.etaLabel);
|
||||
this.progressDLbtnContainer.Controls.Add(this.speedLabel);
|
||||
this.progressDLbtnContainer.Location = new System.Drawing.Point(258, 459);
|
||||
this.progressDLbtnContainer.Location = new System.Drawing.Point(258, 453);
|
||||
this.progressDLbtnContainer.MinimumSize = new System.Drawing.Size(600, 34);
|
||||
this.progressDLbtnContainer.Name = "progressDLbtnContainer";
|
||||
this.progressDLbtnContainer.Size = new System.Drawing.Size(984, 34);
|
||||
this.progressDLbtnContainer.Size = new System.Drawing.Size(984, 40);
|
||||
this.progressDLbtnContainer.TabIndex = 96;
|
||||
//
|
||||
// progressBar
|
||||
//
|
||||
this.progressBar.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.progressBar.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(35)))), ((int)(((byte)(45)))));
|
||||
this.progressBar.BackgroundColor = System.Drawing.Color.FromArgb(((int)(((byte)(28)))), ((int)(((byte)(32)))), ((int)(((byte)(38)))));
|
||||
this.progressBar.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold);
|
||||
this.progressBar.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(93)))), ((int)(((byte)(203)))), ((int)(((byte)(173)))));
|
||||
this.progressBar.IndeterminateColor = System.Drawing.Color.FromArgb(((int)(((byte)(93)))), ((int)(((byte)(203)))), ((int)(((byte)(173)))));
|
||||
this.progressBar.IsIndeterminate = false;
|
||||
this.progressBar.Location = new System.Drawing.Point(1, 23);
|
||||
this.progressBar.Maximum = 100F;
|
||||
this.progressBar.Minimum = 0F;
|
||||
this.progressBar.MinimumSize = new System.Drawing.Size(200, 13);
|
||||
this.progressBar.Name = "progressBar";
|
||||
this.progressBar.OperationType = "";
|
||||
this.progressBar.ProgressEndColor = System.Drawing.Color.FromArgb(((int)(((byte)(50)))), ((int)(((byte)(160)))), ((int)(((byte)(130)))));
|
||||
this.progressBar.ProgressStartColor = System.Drawing.Color.FromArgb(((int)(((byte)(120)))), ((int)(((byte)(220)))), ((int)(((byte)(190)))));
|
||||
this.progressBar.Radius = 6;
|
||||
this.progressBar.Size = new System.Drawing.Size(983, 13);
|
||||
this.progressBar.StatusText = "";
|
||||
this.progressBar.TabIndex = 7;
|
||||
this.progressBar.TextColor = System.Drawing.Color.FromArgb(((int)(((byte)(230)))), ((int)(((byte)(230)))), ((int)(((byte)(230)))));
|
||||
this.progressBar.Value = 0F;
|
||||
//
|
||||
// diskLabel
|
||||
//
|
||||
this.diskLabel.BackColor = System.Drawing.Color.Transparent;
|
||||
@@ -1646,7 +1632,6 @@ namespace AndroidSideloader
|
||||
#endregion
|
||||
private SergeUtils.EasyCompletionComboBox m_combo;
|
||||
private ModernProgressBar progressBar;
|
||||
private System.Windows.Forms.Label etaLabel;
|
||||
private System.Windows.Forms.Label speedLabel;
|
||||
private System.Windows.Forms.Label freeDisclaimer;
|
||||
private System.Windows.Forms.ComboBox devicesComboBox;
|
||||
@@ -1751,5 +1736,6 @@ namespace AndroidSideloader
|
||||
private Label activeMirrorLabel;
|
||||
private Label sideloadingStatusLabel;
|
||||
private Label rookieStatusLabel;
|
||||
private ModernListView _listViewRenderer;
|
||||
}
|
||||
}
|
||||
757
MainForm.cs
757
MainForm.cs
File diff suppressed because it is too large
Load Diff
@@ -120,9 +120,6 @@
|
||||
<metadata name="speedLabel_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>1165, 17</value>
|
||||
</metadata>
|
||||
<metadata name="etaLabel_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>428, 54</value>
|
||||
</metadata>
|
||||
<metadata name="startsideloadbutton_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>966, 17</value>
|
||||
</metadata>
|
||||
@@ -177,6 +174,9 @@
|
||||
<metadata name="listApkButton_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>1320, 17</value>
|
||||
</metadata>
|
||||
<metadata name="etaLabel_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>428, 54</value>
|
||||
</metadata>
|
||||
<metadata name="favoriteGame.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>1021, 91</value>
|
||||
</metadata>
|
||||
|
||||
1205
ModernListView.cs
Normal file
1205
ModernListView.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -13,9 +13,9 @@ namespace AndroidSideloader
|
||||
{
|
||||
#region Fields
|
||||
|
||||
private int _value;
|
||||
private int _minimum;
|
||||
private int _maximum = 100;
|
||||
private float _value;
|
||||
private float _minimum;
|
||||
private float _maximum = 100f;
|
||||
private int _radius = 8;
|
||||
private bool _isIndeterminate;
|
||||
private string _statusText = string.Empty;
|
||||
@@ -66,7 +66,7 @@ namespace AndroidSideloader
|
||||
|
||||
[Category("Progress")]
|
||||
[Description("The current value of the progress bar.")]
|
||||
public int Value
|
||||
public float Value
|
||||
{
|
||||
get => _value;
|
||||
set
|
||||
@@ -78,7 +78,7 @@ namespace AndroidSideloader
|
||||
|
||||
[Category("Progress")]
|
||||
[Description("The minimum value of the progress bar.")]
|
||||
public int Minimum
|
||||
public float Minimum
|
||||
{
|
||||
get => _minimum;
|
||||
set
|
||||
@@ -91,7 +91,7 @@ namespace AndroidSideloader
|
||||
|
||||
[Category("Progress")]
|
||||
[Description("The maximum value of the progress bar.")]
|
||||
public int Maximum
|
||||
public float Maximum
|
||||
{
|
||||
get => _maximum;
|
||||
set
|
||||
@@ -122,7 +122,7 @@ namespace AndroidSideloader
|
||||
set
|
||||
{
|
||||
// If there is no change, do nothing
|
||||
if (_isIndeterminate == value)
|
||||
if (_isIndeterminate == value)
|
||||
return;
|
||||
|
||||
_isIndeterminate = value;
|
||||
@@ -205,7 +205,7 @@ namespace AndroidSideloader
|
||||
|
||||
// Gets the progress as a percentage (0-100)
|
||||
public float ProgressPercent =>
|
||||
_maximum > _minimum ? (float)(_value - _minimum) / (_maximum - _minimum) * 100f : 0f;
|
||||
_maximum > _minimum ? (_value - _minimum) / (_maximum - _minimum) * 100f : 0f;
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -250,7 +250,7 @@ namespace AndroidSideloader
|
||||
private void DrawProgress(Graphics g, Rectangle outerRect)
|
||||
{
|
||||
float percent = (_maximum > _minimum)
|
||||
? (float)(_value - _minimum) / (_maximum - _minimum)
|
||||
? (_value - _minimum) / (_maximum - _minimum)
|
||||
: 0f;
|
||||
|
||||
if (percent <= 0f) return;
|
||||
@@ -363,10 +363,11 @@ namespace AndroidSideloader
|
||||
|
||||
if (!_isIndeterminate && _value > _minimum)
|
||||
{
|
||||
string percentText = $"{(int)ProgressPercent}%";
|
||||
// Show one decimal place for sub-percent precision
|
||||
string percentText = $"{ProgressPercent:0.0}%";
|
||||
if (!string.IsNullOrEmpty(_operationType))
|
||||
{
|
||||
// E.g. "Downloading · 73%"
|
||||
// E.g. "Downloading · 73.5%"
|
||||
return $"{_operationType} · {percentText}";
|
||||
}
|
||||
return percentText;
|
||||
@@ -435,4 +436,4 @@ namespace AndroidSideloader
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
2
NewApps.Designer.cs
generated
2
NewApps.Designer.cs
generated
@@ -29,7 +29,6 @@ namespace AndroidSideloader
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(NewApps));
|
||||
this.NewAppsListView = new System.Windows.Forms.ListView();
|
||||
this.GameNameIndex = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
|
||||
this.PackageNameIndex = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
|
||||
@@ -173,7 +172,6 @@ namespace AndroidSideloader
|
||||
this.Controls.Add(this.panel1);
|
||||
this.ForeColor = System.Drawing.Color.White;
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
|
||||
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
|
||||
this.Name = "NewApps";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||
this.Load += new System.EventHandler(this.NewApps_Load);
|
||||
|
||||
@@ -39,6 +39,10 @@ namespace AndroidSideloader
|
||||
public NewApps()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
// Use same icon as the executable
|
||||
this.Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath);
|
||||
|
||||
ApplyModernTheme();
|
||||
CenterToScreen();
|
||||
}
|
||||
|
||||
6849
NewApps.resx
6849
NewApps.resx
File diff suppressed because it is too large
Load Diff
3
QuestForm.Designer.cs
generated
3
QuestForm.Designer.cs
generated
@@ -30,7 +30,6 @@ namespace AndroidSideloader
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(QuestForm));
|
||||
this.lblUsernameSection = new System.Windows.Forms.Label();
|
||||
this.lblMediaSection = new System.Windows.Forms.Label();
|
||||
this.lblPerformanceSection = new System.Windows.Forms.Label();
|
||||
@@ -444,11 +443,9 @@ namespace AndroidSideloader
|
||||
this.Controls.Add(this.btnApplyTempSettings);
|
||||
this.Controls.Add(this.btnClose);
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
|
||||
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
|
||||
this.MaximizeBox = false;
|
||||
this.MinimizeBox = false;
|
||||
this.Name = "QuestForm";
|
||||
this.ShowIcon = false;
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||
this.Text = "Quest Settings";
|
||||
this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.QuestForm_FormClosed);
|
||||
|
||||
@@ -16,6 +16,9 @@ namespace AndroidSideloader
|
||||
public QuestForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
// Use same icon as the executable
|
||||
this.Icon = System.Drawing.Icon.ExtractAssociatedIcon(Application.ExecutablePath);
|
||||
}
|
||||
|
||||
private void btnApplyTempSettings_Click(object sender, EventArgs e)
|
||||
|
||||
6849
QuestForm.resx
6849
QuestForm.resx
File diff suppressed because it is too large
Load Diff
3
SettingsForm.Designer.cs
generated
3
SettingsForm.Designer.cs
generated
@@ -28,7 +28,6 @@
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SettingsForm));
|
||||
this.downloadDirectorySetter = new System.Windows.Forms.FolderBrowserDialog();
|
||||
this.backupDirectorySetter = new System.Windows.Forms.FolderBrowserDialog();
|
||||
this.crashlogID = new System.Windows.Forms.Label();
|
||||
@@ -866,11 +865,9 @@
|
||||
this.Controls.Add(this.resetSettingsButton);
|
||||
this.ForeColor = System.Drawing.Color.White;
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
|
||||
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
|
||||
this.MaximizeBox = false;
|
||||
this.MinimizeBox = false;
|
||||
this.Name = "SettingsForm";
|
||||
this.ShowIcon = false;
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||
this.Text = "Settings";
|
||||
this.Load += new System.EventHandler(this.SettingsForm_Load);
|
||||
|
||||
@@ -17,6 +17,9 @@ namespace AndroidSideloader
|
||||
public SettingsForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
// Use same icon as the executable
|
||||
this.Icon = System.Drawing.Icon.ExtractAssociatedIcon(Application.ExecutablePath);
|
||||
}
|
||||
|
||||
private void SettingsForm_Load(object sender, EventArgs e)
|
||||
|
||||
6849
SettingsForm.resx
6849
SettingsForm.resx
File diff suppressed because it is too large
Load Diff
@@ -137,7 +137,7 @@ namespace AndroidSideloader
|
||||
Application.Exit();
|
||||
}
|
||||
|
||||
string wantedRcloneVersion = "1.68.2";
|
||||
string wantedRcloneVersion = "1.72.1";
|
||||
bool rcloneSuccess = false;
|
||||
|
||||
rcloneSuccess = downloadRclone(wantedRcloneVersion, false);
|
||||
|
||||
@@ -14,8 +14,6 @@ namespace AndroidSideloader
|
||||
|
||||
public static string RcloneGamesFolder = "Quest Games";
|
||||
|
||||
//This shit sucks but i'll switch to programatically adding indexes from the gamelist txt sometimes maybe
|
||||
|
||||
public static int GameNameIndex = 0;
|
||||
public static int ReleaseNameIndex = 1;
|
||||
public static int PackageNameIndex = 2;
|
||||
@@ -23,6 +21,7 @@ namespace AndroidSideloader
|
||||
public static int ReleaseAPKPathIndex = 4;
|
||||
public static int VersionNameIndex = 5;
|
||||
public static int DownloadsIndex = 6;
|
||||
public static int InstalledVersion = 7;
|
||||
|
||||
public static List<string> gameProperties = new List<string>();
|
||||
/* Game Name
|
||||
|
||||
2
UpdateForm.Designer.cs
generated
2
UpdateForm.Designer.cs
generated
@@ -28,7 +28,6 @@
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(UpdateForm));
|
||||
this.panel1 = new System.Windows.Forms.Panel();
|
||||
this.YesUpdate = new AndroidSideloader.RoundButton();
|
||||
this.panel3 = new System.Windows.Forms.Panel();
|
||||
@@ -166,7 +165,6 @@
|
||||
this.ControlBox = false;
|
||||
this.Controls.Add(this.panel1);
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
|
||||
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
|
||||
this.Name = "UpdateForm";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||
this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.UpdateForm_MouseDown);
|
||||
|
||||
@@ -20,6 +20,10 @@ namespace AndroidSideloader
|
||||
public UpdateForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
// Use same icon as the executable
|
||||
this.Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath);
|
||||
|
||||
ApplyModernTheme();
|
||||
CenterToScreen();
|
||||
CurVerLabel.Text = $"Current Version: {Updater.LocalVersion}";
|
||||
|
||||
6849
UpdateForm.resx
6849
UpdateForm.resx
File diff suppressed because it is too large
Load Diff
20
UsernameForm.Designer.cs
generated
20
UsernameForm.Designer.cs
generated
@@ -28,28 +28,30 @@
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(UsernameForm));
|
||||
this.textBox1 = new System.Windows.Forms.TextBox();
|
||||
this.button1 = new AndroidSideloader.RoundButton();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
//
|
||||
// textBox1
|
||||
//
|
||||
//
|
||||
this.textBox1.BackColor = global::AndroidSideloader.Properties.Settings.Default.TextBoxColor;
|
||||
this.textBox1.Font = global::AndroidSideloader.Properties.Settings.Default.FontStyle;
|
||||
this.textBox1.ForeColor = System.Drawing.Color.White;
|
||||
this.textBox1.Location = new System.Drawing.Point(13, 13);
|
||||
this.textBox1.Name = "textBox1";
|
||||
this.textBox1.Size = new System.Drawing.Size(418, 24);
|
||||
this.textBox1.Size = new System.Drawing.Size(418, 23);
|
||||
this.textBox1.TabIndex = 0;
|
||||
this.textBox1.Text = "Enter your username here";
|
||||
//
|
||||
//
|
||||
// button1
|
||||
//
|
||||
//
|
||||
this.button1.Active1 = System.Drawing.Color.FromArgb(((int)(((byte)(45)))), ((int)(((byte)(45)))), ((int)(((byte)(45)))));
|
||||
this.button1.Active2 = System.Drawing.Color.FromArgb(((int)(((byte)(45)))), ((int)(((byte)(45)))), ((int)(((byte)(45)))));
|
||||
this.button1.BackColor = System.Drawing.Color.Transparent;
|
||||
this.button1.DialogResult = System.Windows.Forms.DialogResult.OK;
|
||||
this.button1.Disabled1 = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(35)))), ((int)(((byte)(45)))));
|
||||
this.button1.Disabled2 = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(28)))), ((int)(((byte)(35)))));
|
||||
this.button1.DisabledStrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(50)))), ((int)(((byte)(55)))), ((int)(((byte)(65)))));
|
||||
this.button1.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F);
|
||||
this.button1.ForeColor = System.Drawing.Color.White;
|
||||
this.button1.Inactive1 = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(25)))), ((int)(((byte)(25)))));
|
||||
@@ -64,9 +66,9 @@
|
||||
this.button1.Text = "Create User.Json";
|
||||
this.button1.Transparency = false;
|
||||
this.button1.Click += new System.EventHandler(this.button1_Click);
|
||||
//
|
||||
//
|
||||
// UsernameForm
|
||||
//
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.BackColor = global::AndroidSideloader.Properties.Settings.Default.BackColor;
|
||||
@@ -74,11 +76,9 @@
|
||||
this.Controls.Add(this.button1);
|
||||
this.Controls.Add(this.textBox1);
|
||||
this.ForeColor = System.Drawing.Color.White;
|
||||
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
|
||||
this.MaximumSize = new System.Drawing.Size(459, 139);
|
||||
this.MinimumSize = new System.Drawing.Size(459, 139);
|
||||
this.Name = "UsernameForm";
|
||||
this.ShowIcon = false;
|
||||
this.Text = "USER.JSON";
|
||||
this.Load += new System.EventHandler(this.usernameForm_Load);
|
||||
this.ResumeLayout(false);
|
||||
|
||||
@@ -12,6 +12,9 @@ namespace AndroidSideloader
|
||||
public UsernameForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
// Use same icon as the executable
|
||||
this.Icon = System.Drawing.Icon.ExtractAssociatedIcon(Application.ExecutablePath);
|
||||
}
|
||||
|
||||
private string defaultText;
|
||||
|
||||
6849
UsernameForm.resx
6849
UsernameForm.resx
File diff suppressed because it is too large
Load Diff
105
Utilities/Zip.cs
105
Utilities/Zip.cs
@@ -3,10 +3,7 @@ using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace AndroidSideloader.Utilities
|
||||
@@ -19,6 +16,11 @@ namespace AndroidSideloader.Utilities
|
||||
internal class Zip
|
||||
{
|
||||
private static readonly SettingsManager settings = SettingsManager.Instance;
|
||||
|
||||
// Progress callback: (percent, eta)
|
||||
public static Action<float, TimeSpan?> ExtractionProgressCallback { get; set; }
|
||||
public static Action<string> ExtractionStatusCallback { get; set; }
|
||||
|
||||
public static void ExtractFile(string sourceArchive, string destination)
|
||||
{
|
||||
string args = $"x \"{sourceArchive}\" -y -o\"{destination}\" -bsp1";
|
||||
@@ -33,6 +35,7 @@ namespace AndroidSideloader.Utilities
|
||||
|
||||
private static string extractionError = null;
|
||||
private static bool errorMessageShown = false;
|
||||
|
||||
private static void DoExtract(string args)
|
||||
{
|
||||
if (!File.Exists(Path.Combine(Environment.CurrentDirectory, "7z.exe")) || !File.Exists(Path.Combine(Environment.CurrentDirectory, "7z.dll")))
|
||||
@@ -68,24 +71,98 @@ namespace AndroidSideloader.Utilities
|
||||
|
||||
_ = Logger.Log($"Extract: 7z {string.Join(" ", args.Split(' ').Where(a => !a.StartsWith("-p")))}");
|
||||
|
||||
// Throttle percent reports
|
||||
float lastReportedPercent = -1;
|
||||
|
||||
// ETA engine (percent units)
|
||||
var etaEstimator = new EtaEstimator(alpha: 0.10, reanchorThreshold: 0.20, minSampleSeconds: 0.10);
|
||||
|
||||
// Smooth progress (sub-percent) interpolation (because 7z -bsp1 is integer-only)
|
||||
System.Threading.Timer smoothTimer = null;
|
||||
int extractingFlag = 1; // 1 = extracting, 0 = stop
|
||||
float smoothLastTickPercent = 0f;
|
||||
DateTime smoothLastTickTime = DateTime.UtcNow;
|
||||
float smoothLastReported = -1f;
|
||||
const int SmoothIntervalMs = 80; // ~12.5 updates/sec
|
||||
const float SmoothReportDelta = 0.10f; // report only if change >= 0.10%
|
||||
|
||||
using (Process x = new Process())
|
||||
{
|
||||
x.StartInfo = pro;
|
||||
|
||||
if (MainForm.isInDownloadExtract && x != null)
|
||||
{
|
||||
// Smooth sub-percent UI, while keeping ETA ticking
|
||||
smoothTimer = new System.Threading.Timer(_ =>
|
||||
{
|
||||
if (System.Threading.Volatile.Read(ref extractingFlag) == 0) return;
|
||||
if (smoothLastTickPercent <= 0) return; // need at least one 7z tick
|
||||
|
||||
// Use current ETA to approximate seconds-per-percent
|
||||
TimeSpan? displayEta = etaEstimator.GetDisplayEta();
|
||||
if (!displayEta.HasValue) return; // Skip until ETA exists
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
var elapsed = (now - smoothLastTickTime).TotalSeconds;
|
||||
|
||||
// Approx seconds-per-percent from remaining ETA / remaining percent
|
||||
double remainingPercent = Math.Max(1.0, 100.0 - smoothLastTickPercent);
|
||||
double spp = Math.Max(0.05, displayEta.Value.TotalSeconds / remainingPercent);
|
||||
|
||||
float candidate = smoothLastTickPercent + (float)(elapsed / spp);
|
||||
|
||||
// Clamp
|
||||
float floorTick = (float)Math.Floor(smoothLastTickPercent);
|
||||
float ceiling = Math.Min(99.99f, floorTick + 0.999f);
|
||||
|
||||
if (candidate > ceiling) candidate = ceiling;
|
||||
if (candidate < smoothLastTickPercent) candidate = smoothLastTickPercent;
|
||||
|
||||
if (smoothLastReported >= 0 && Math.Abs(candidate - smoothLastReported) < SmoothReportDelta) return;
|
||||
smoothLastReported = candidate;
|
||||
|
||||
try
|
||||
{
|
||||
MainForm mainForm = (MainForm)Application.OpenForms[0];
|
||||
if (mainForm != null && !mainForm.IsDisposed)
|
||||
{
|
||||
mainForm.BeginInvoke((Action)(() => mainForm.SetProgress(candidate)));
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
// ETA countdown ticks even if 7z percent is unchanged
|
||||
ExtractionProgressCallback?.Invoke(candidate, etaEstimator.GetDisplayEta());
|
||||
|
||||
}, null, SmoothIntervalMs, SmoothIntervalMs);
|
||||
|
||||
x.OutputDataReceived += (sender, e) =>
|
||||
{
|
||||
if (e.Data != null)
|
||||
{
|
||||
var match = Regex.Match(e.Data, @"(\d+)%");
|
||||
if (match.Success)
|
||||
var match = Regex.Match(e.Data, @"^\s*(\d+)%");
|
||||
if (match.Success && float.TryParse(match.Groups[1].Value, out float percent))
|
||||
{
|
||||
int progress = int.Parse(match.Groups[1].Value);
|
||||
MainForm mainForm = (MainForm)Application.OpenForms[0];
|
||||
if (mainForm != null)
|
||||
// Update ETA from integer percent
|
||||
if (percent <= 0.0f) etaEstimator.Reset();
|
||||
else if (percent < 100.0f) etaEstimator.Update(totalUnits: 100, doneUnits: (long)Math.Round(percent));
|
||||
|
||||
// Reset smoothing baseline on each integer tick
|
||||
smoothLastTickPercent = percent;
|
||||
smoothLastTickTime = DateTime.UtcNow;
|
||||
smoothLastReported = percent;
|
||||
|
||||
if (Math.Abs(percent - lastReportedPercent) >= 0.1f)
|
||||
{
|
||||
mainForm.Invoke((Action)(() => mainForm.SetProgress(progress)));
|
||||
lastReportedPercent = percent;
|
||||
|
||||
MainForm mainForm = (MainForm)Application.OpenForms[0];
|
||||
if (mainForm != null)
|
||||
{
|
||||
mainForm.Invoke((Action)(() => mainForm.SetProgress(percent)));
|
||||
}
|
||||
|
||||
ExtractionProgressCallback?.Invoke(percent, etaEstimator.GetDisplayEta());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -119,6 +196,16 @@ namespace AndroidSideloader.Utilities
|
||||
x.BeginOutputReadLine();
|
||||
x.BeginErrorReadLine();
|
||||
x.WaitForExit();
|
||||
|
||||
// Stop smoother
|
||||
System.Threading.Interlocked.Exchange(ref extractingFlag, 0);
|
||||
smoothTimer?.Dispose();
|
||||
smoothTimer = null;
|
||||
|
||||
// Clear callbacks
|
||||
ExtractionProgressCallback?.Invoke(100, null);
|
||||
ExtractionStatusCallback?.Invoke("");
|
||||
|
||||
errorMessageShown = false;
|
||||
|
||||
if (!string.IsNullOrEmpty(extractionError))
|
||||
|
||||
Reference in New Issue
Block a user