Compare commits
145 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
15efd91769 | ||
|
|
28fe132012 | ||
|
|
db93c7b610 | ||
|
|
3bae614d7e | ||
|
|
3e3a452ceb | ||
|
|
4dc11191f0 | ||
|
|
3e626c6390 | ||
|
|
5b44295418 | ||
|
|
95b9059bed | ||
|
|
163a0d9fce | ||
|
|
af84f2cf8c | ||
|
|
9f2e824df2 | ||
|
|
f21efcd476 | ||
|
|
50b52d963b | ||
|
|
61e6c143fa | ||
|
|
60d68cbc8c | ||
|
|
93ed359ed1 | ||
|
|
8bd7efc22e | ||
|
|
07957d93fe | ||
|
|
f91ee27c35 | ||
|
|
d8cc5dc539 | ||
|
|
ed17322983 | ||
|
|
0970b73fe7 | ||
|
|
7cc437309a | ||
|
|
a050d82c8b | ||
|
|
4c5a651b2b | ||
|
|
af17f5bb12 | ||
|
|
670ace1963 | ||
|
|
77cfb81545 | ||
|
|
3d241cc634 | ||
|
|
187f1d3689 | ||
|
|
6ccaf06ab4 | ||
|
|
f289026438 | ||
|
|
de6b0e4c70 | ||
|
|
debb204719 | ||
|
|
f80d5f841c | ||
|
|
38a1968c42 | ||
|
|
b92def0283 | ||
|
|
62343f8cf6 | ||
|
|
fdb091cee6 | ||
|
|
3993e574a8 | ||
|
|
fb36826091 | ||
|
|
84ddce1423 | ||
|
|
f363550351 | ||
|
|
02ab0f0e2a | ||
|
|
5819bc8083 | ||
|
|
311f0cfb7e | ||
|
|
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 | ||
|
|
c48043a178 | ||
|
|
15f0c1ee72 | ||
|
|
44df1666f4 | ||
|
|
6cbfdbe52c | ||
|
|
75d22ab504 | ||
|
|
0c20841db3 | ||
|
|
b33251d98b | ||
|
|
3ef0652a85 | ||
|
|
2ced3aa961 | ||
|
|
0ecb4255e9 | ||
|
|
f8dea1e135 | ||
|
|
f714d2cb92 | ||
|
|
f3a93afccc | ||
|
|
123fb7261c | ||
|
|
79225fe21d | ||
|
|
062d702877 | ||
|
|
e9e8c1298e | ||
|
|
9b6104c350 | ||
|
|
3db36a16c7 | ||
|
|
b06ee70c38 | ||
|
|
769358b999 | ||
|
|
66058b3134 | ||
|
|
a65b369836 | ||
|
|
5b16ec4ef7 | ||
|
|
3e950833f9 | ||
|
|
4c333453b4 | ||
|
|
3e4787d1a2 | ||
|
|
047f84e9e7 | ||
|
|
eb0245dfe2 | ||
|
|
602fafe779 | ||
|
|
bbe4050b40 | ||
|
|
a25ae6dbb7 | ||
|
|
d89c9dd739 | ||
|
|
9f719672ac | ||
|
|
8962cf6d4f | ||
|
|
ef3ef7d714 | ||
|
|
2c1f0ec259 | ||
|
|
31b19ca0f6 | ||
|
|
4682646d36 | ||
|
|
352a86334f | ||
|
|
6c6732c8d1 | ||
|
|
b5dfcef34f | ||
|
|
fea63cc03e | ||
|
|
39b946b94b | ||
|
|
de6c96a7ac | ||
|
|
2590eebfff | ||
|
|
b63c2c0466 | ||
|
|
dbaeea4e8b | ||
|
|
b120d18014 | ||
|
|
85804ee69c | ||
|
|
2b770b30a8 | ||
|
|
e6d178cb2a | ||
|
|
c2f5b20e83 | ||
|
|
952251be35 | ||
|
|
385fe45d5d | ||
|
|
5db0ea301d | ||
|
|
5dc8c947c9 | ||
|
|
523efba81c | ||
|
|
06dc91f130 | ||
|
|
252955da0a | ||
|
|
bd193c0d3f | ||
|
|
630fe98d31 | ||
|
|
0edc1deed0 | ||
|
|
413113e732 | ||
|
|
26f7044053 | ||
|
|
5a7c4971df | ||
|
|
a397a1a985 | ||
|
|
44e8bdef7b | ||
|
|
6aa5c7baea | ||
|
|
da09545168 |
746
ADB.cs
@@ -1,8 +1,14 @@
|
||||
using AndroidSideloader.Utilities;
|
||||
using AdvancedSharpAdbClient;
|
||||
using AdvancedSharpAdbClient.DeviceCommands;
|
||||
using AdvancedSharpAdbClient.Models;
|
||||
using AdvancedSharpAdbClient.Receivers;
|
||||
using AndroidSideloader.Utilities;
|
||||
using JR.Utils.GUI.Forms;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace AndroidSideloader
|
||||
@@ -10,35 +16,88 @@ namespace AndroidSideloader
|
||||
internal class ADB
|
||||
{
|
||||
private static readonly SettingsManager settings = SettingsManager.Instance;
|
||||
private static readonly Process adb = new Process();
|
||||
public static string adbFolderPath = Path.Combine(Path.GetPathRoot(Environment.SystemDirectory), "RSL", "platform-tools");
|
||||
public static string adbFolderPath = Path.Combine(Environment.CurrentDirectory, "platform-tools");
|
||||
public static string adbFilePath = Path.Combine(adbFolderPath, "adb.exe");
|
||||
public static string DeviceID = "";
|
||||
public static string package = "";
|
||||
public static ProcessOutput RunAdbCommandToString(string command)
|
||||
public static bool wirelessadbON;
|
||||
|
||||
// AdbClient for direct protocol communication
|
||||
private static AdbClient _adbClient;
|
||||
private static DeviceData _currentDevice;
|
||||
|
||||
// Gets or initializes the AdbClient instance
|
||||
private static AdbClient GetAdbClient()
|
||||
{
|
||||
if (_adbClient == null)
|
||||
{
|
||||
// Ensure ADB server is started
|
||||
if (!AdbServer.Instance.GetStatus().IsRunning)
|
||||
{
|
||||
var server = new AdbServer();
|
||||
var result = server.StartServer(adbFilePath, false);
|
||||
Logger.Log($"ADB server start result: {result}");
|
||||
}
|
||||
|
||||
_adbClient = new AdbClient();
|
||||
}
|
||||
return _adbClient;
|
||||
}
|
||||
|
||||
// Gets the current device for AdbClient operations
|
||||
private static DeviceData GetCurrentDevice()
|
||||
{
|
||||
var client = GetAdbClient();
|
||||
var devices = client.GetDevices();
|
||||
|
||||
if (devices == null || !devices.Any())
|
||||
{
|
||||
Logger.Log("No devices found via AdbClient", LogLevel.WARNING);
|
||||
return default;
|
||||
}
|
||||
|
||||
// If DeviceID is set, find that specific device
|
||||
if (!string.IsNullOrEmpty(DeviceID) && DeviceID.Length > 1)
|
||||
{
|
||||
var device = devices.FirstOrDefault(d => d.Serial == DeviceID || d.Serial.StartsWith(DeviceID));
|
||||
if (device.Serial != null)
|
||||
{
|
||||
_currentDevice = device;
|
||||
return device;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise return the first available device
|
||||
_currentDevice = devices.First();
|
||||
return _currentDevice;
|
||||
}
|
||||
|
||||
public static ProcessOutput RunAdbCommandToString(string command, bool suppressLogging = false)
|
||||
{
|
||||
// Replacing "adb" from command if the user added it
|
||||
command = command.Replace("adb", "");
|
||||
|
||||
settings.ADBFolder = adbFolderPath;
|
||||
settings.ADBPath = adbFilePath;
|
||||
settings.Save();
|
||||
|
||||
if (DeviceID.Length > 1)
|
||||
{
|
||||
command = $" -s {DeviceID} {command}";
|
||||
}
|
||||
if (!command.Contains("dumpsys") && !command.Contains("shell pm list packages") && !command.Contains("KEYCODE_WAKEUP"))
|
||||
|
||||
if (!suppressLogging && !command.Contains("dumpsys") && !command.Contains("shell pm list packages") && !command.Contains("KEYCODE_WAKEUP"))
|
||||
{
|
||||
string logcmd = command;
|
||||
|
||||
if (logcmd.Contains(Environment.CurrentDirectory))
|
||||
{
|
||||
logcmd = logcmd.Replace($"{Environment.CurrentDirectory}", $"CurrentDirectory");
|
||||
}
|
||||
|
||||
_ = Logger.Log($"Running command: {logcmd}");
|
||||
}
|
||||
|
||||
bool isConnectCommand = command.Contains("connect");
|
||||
int timeoutMs = isConnectCommand ? 5000 : -1; // 5 second timeout for connect commands
|
||||
|
||||
using (Process adb = new Process())
|
||||
{
|
||||
adb.StartInfo.FileName = adbFilePath;
|
||||
@@ -55,19 +114,39 @@ namespace AndroidSideloader
|
||||
|
||||
try
|
||||
{
|
||||
output = adb.StandardOutput.ReadToEnd();
|
||||
error = adb.StandardError.ReadToEnd();
|
||||
}
|
||||
catch { }
|
||||
|
||||
if (command.Contains("connect"))
|
||||
{
|
||||
bool graceful = adb.WaitForExit(3000);
|
||||
if (!graceful)
|
||||
if (isConnectCommand)
|
||||
{
|
||||
adb.Kill();
|
||||
adb.WaitForExit();
|
||||
// For connect commands, we use async reading with timeout to avoid blocking on TCP timeout
|
||||
var outputTask = adb.StandardOutput.ReadToEndAsync();
|
||||
var errorTask = adb.StandardError.ReadToEndAsync();
|
||||
|
||||
bool exited = adb.WaitForExit(timeoutMs);
|
||||
|
||||
if (!exited)
|
||||
{
|
||||
try { adb.Kill(); } catch { }
|
||||
adb.WaitForExit(1000);
|
||||
output = "Connection timed out";
|
||||
error = "cannot connect: Connection timed out";
|
||||
Logger.Log($"ADB connect command timed out after {timeoutMs}ms", LogLevel.WARNING);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Process exited within timeout, safe to read output
|
||||
output = outputTask.Result;
|
||||
error = errorTask.Result;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// For non-connect commands, read output normally
|
||||
output = adb.StandardOutput.ReadToEnd();
|
||||
error = adb.StandardError.ReadToEnd();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Log($"Error reading ADB output: {ex.Message}", LogLevel.WARNING);
|
||||
}
|
||||
|
||||
if (error.Contains("ADB_VENDOR_KEYS") && !settings.AdbDebugWarned)
|
||||
@@ -78,7 +157,7 @@ namespace AndroidSideloader
|
||||
{
|
||||
_ = FlexibleMessageBox.Show(Program.form, "There is not enough room on your device to install this package. Please clear AT LEAST 2x the amount of the app you are trying to install.");
|
||||
}
|
||||
if (!output.Contains("version") && !output.Contains("KEYCODE_WAKEUP") && !output.Contains("Filesystem") && !output.Contains("package:") && !output.Equals(null))
|
||||
if (!suppressLogging && !output.Contains("version") && !output.Contains("KEYCODE_WAKEUP") && !output.Contains("Filesystem") && !output.Contains("package:") && !output.Equals(null))
|
||||
{
|
||||
_ = Logger.Log(output);
|
||||
}
|
||||
@@ -88,6 +167,339 @@ namespace AndroidSideloader
|
||||
}
|
||||
}
|
||||
|
||||
// Executes a shell command on the device.
|
||||
private static void ExecuteShellCommand(AdbClient client, DeviceData device, string command)
|
||||
{
|
||||
var receiver = new ConsoleOutputReceiver();
|
||||
client.ExecuteRemoteCommand(command, device, receiver);
|
||||
}
|
||||
|
||||
// Copies and installs an APK with real-time progress reporting using AdvancedSharpAdbClient
|
||||
public static async Task<ProcessOutput> SideloadWithProgressAsync(
|
||||
string path,
|
||||
Action<float, TimeSpan?> progressCallback = null,
|
||||
Action<string> statusCallback = null,
|
||||
string packagename = "",
|
||||
string gameName = "")
|
||||
{
|
||||
statusCallback?.Invoke("Installing APK...");
|
||||
progressCallback?.Invoke(0, null);
|
||||
|
||||
try
|
||||
{
|
||||
var device = GetCurrentDevice();
|
||||
if (device.Serial == null)
|
||||
{
|
||||
return new ProcessOutput("", "No device connected");
|
||||
}
|
||||
|
||||
var client = GetAdbClient();
|
||||
var packageManager = new PackageManager(client, device);
|
||||
|
||||
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) =>
|
||||
{
|
||||
float percent = 0;
|
||||
string status = null;
|
||||
TimeSpan? displayEta = null;
|
||||
|
||||
switch (args.State)
|
||||
{
|
||||
case PackageInstallProgressState.Preparing:
|
||||
percent = 0;
|
||||
status = "Preparing...";
|
||||
eta.Reset();
|
||||
break;
|
||||
|
||||
case PackageInstallProgressState.Uploading:
|
||||
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;
|
||||
status = "Completing Installation...";
|
||||
displayEta = null;
|
||||
break;
|
||||
|
||||
case PackageInstallProgressState.Finished:
|
||||
percent = 100;
|
||||
status = "";
|
||||
displayEta = null;
|
||||
break;
|
||||
|
||||
default:
|
||||
percent = 100;
|
||||
status = "";
|
||||
displayEta = null;
|
||||
break;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
await Task.Run(() =>
|
||||
{
|
||||
packageManager.InstallPackage(path, installProgress);
|
||||
});
|
||||
|
||||
progressCallback?.Invoke(100, null);
|
||||
statusCallback?.Invoke("");
|
||||
|
||||
return new ProcessOutput($"{gameName}: Success\n");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Log($"SideloadWithProgressAsync error: {ex.Message}", LogLevel.ERROR);
|
||||
|
||||
// Signature mismatches and version downgrades can be fixed by reinstalling
|
||||
bool isReinstallEligible = ex.Message.Contains("signatures do not match") ||
|
||||
ex.Message.Contains("INSTALL_FAILED_VERSION_DOWNGRADE") ||
|
||||
ex.Message.Contains("failed to install");
|
||||
|
||||
// For insufficient storage, offer reinstall if it's an upgrade
|
||||
// As uninstalling old version frees space for the new one
|
||||
bool isStorageIssue = ex.Message.Contains("INSUFFICIENT_STORAGE");
|
||||
bool isUpgrade = !string.IsNullOrEmpty(packagename) &&
|
||||
settings.InstalledApps.Contains(packagename);
|
||||
|
||||
if (isStorageIssue && isUpgrade)
|
||||
{
|
||||
isReinstallEligible = true;
|
||||
}
|
||||
|
||||
if (isReinstallEligible)
|
||||
{
|
||||
bool cancelClicked = false;
|
||||
|
||||
if (!settings.AutoReinstall)
|
||||
{
|
||||
string message = isStorageIssue
|
||||
? "Installation failed due to insufficient storage. Since this is an upgrade, Rookie can uninstall the old version first to free up space, then install the new version.\n\nRookie will also 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 has failed. Rookie will 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?";
|
||||
|
||||
string title = isStorageIssue ? "Insufficient Storage" : "In place upgrade failed";
|
||||
|
||||
Program.form.Invoke(() =>
|
||||
{
|
||||
DialogResult dialogResult1 = FlexibleMessageBox.Show(Program.form,
|
||||
message, title, MessageBoxButtons.OKCancel);
|
||||
if (dialogResult1 == DialogResult.Cancel)
|
||||
cancelClicked = true;
|
||||
});
|
||||
}
|
||||
|
||||
if (cancelClicked)
|
||||
return new ProcessOutput("", "Installation cancelled by user");
|
||||
|
||||
statusCallback?.Invoke("Performing reinstall...");
|
||||
|
||||
try
|
||||
{
|
||||
var device = GetCurrentDevice();
|
||||
var client = GetAdbClient();
|
||||
var packageManager = new PackageManager(client, device);
|
||||
|
||||
statusCallback?.Invoke("Backing up save data...");
|
||||
_ = RunAdbCommandToString($"pull \"/sdcard/Android/data/{packagename}\" \"{Environment.CurrentDirectory}\"");
|
||||
|
||||
statusCallback?.Invoke("Uninstalling old version...");
|
||||
packageManager.UninstallPackage(packagename);
|
||||
|
||||
statusCallback?.Invoke("Reinstalling game...");
|
||||
Action<InstallProgressEventArgs> reinstallProgress = (args) =>
|
||||
{
|
||||
if (args.State == PackageInstallProgressState.Uploading)
|
||||
{
|
||||
progressCallback?.Invoke((float)args.UploadProgress, null);
|
||||
}
|
||||
};
|
||||
packageManager.InstallPackage(path, reinstallProgress);
|
||||
|
||||
statusCallback?.Invoke("Restoring save data...");
|
||||
_ = RunAdbCommandToString($"push \"{Environment.CurrentDirectory}\\{packagename}\" /sdcard/Android/data/");
|
||||
|
||||
string directoryToDelete = Path.Combine(Environment.CurrentDirectory, packagename);
|
||||
if (Directory.Exists(directoryToDelete) && directoryToDelete != Environment.CurrentDirectory)
|
||||
{
|
||||
FileSystemUtilities.TryDeleteDirectory(directoryToDelete);
|
||||
}
|
||||
|
||||
progressCallback?.Invoke(100, null);
|
||||
return new ProcessOutput($"{gameName}: Reinstall: Success\n", "");
|
||||
}
|
||||
catch (Exception reinstallEx)
|
||||
{
|
||||
return new ProcessOutput("", $"{gameName}: Reinstall Failed: {reinstallEx.Message}\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Return the error message so it's displayed to the user
|
||||
return new ProcessOutput("", $"\n{gameName}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// Copies OBB folder with real-time progress reporting using AdvancedSharpAdbClient
|
||||
public static async Task<ProcessOutput> CopyOBBWithProgressAsync(
|
||||
string localPath,
|
||||
Action<float, TimeSpan?> progressCallback = null,
|
||||
Action<string> statusCallback = null,
|
||||
string gameName = "")
|
||||
{
|
||||
string folderName = Path.GetFileName(localPath);
|
||||
|
||||
if (!folderName.Contains("."))
|
||||
{
|
||||
return new ProcessOutput("No OBB Folder found");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var device = GetCurrentDevice();
|
||||
if (device.Serial == null)
|
||||
{
|
||||
return new ProcessOutput("", "No device connected");
|
||||
}
|
||||
|
||||
var client = GetAdbClient();
|
||||
string remotePath = $"/sdcard/Android/obb/{folderName}";
|
||||
|
||||
statusCallback?.Invoke($"Preparing: {folderName}");
|
||||
progressCallback?.Invoke(0, null);
|
||||
|
||||
// Delete existing OBB folder and create new one
|
||||
ExecuteShellCommand(client, device, $"rm -rf \"{remotePath}\"");
|
||||
ExecuteShellCommand(client, device, $"mkdir -p \"{remotePath}\"");
|
||||
|
||||
// Get all files to push and calculate total size
|
||||
var files = Directory.GetFiles(localPath, "*", SearchOption.AllDirectories);
|
||||
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))
|
||||
{
|
||||
foreach (var file in files)
|
||||
{
|
||||
string relativePath = file.Substring(localPath.Length)
|
||||
.TrimStart('\\', '/')
|
||||
.Replace('\\', '/');
|
||||
string remoteFilePath = $"{remotePath}/{relativePath}";
|
||||
string fileName = Path.GetFileName(file);
|
||||
|
||||
// Ensure remote directory exists
|
||||
string remoteDir = remoteFilePath.Substring(0, remoteFilePath.LastIndexOf('/'));
|
||||
ExecuteShellCommand(client, device, $"mkdir -p \"{remoteDir}\"");
|
||||
|
||||
var fileInfo = new FileInfo(file);
|
||||
long fileSize = fileInfo.Length;
|
||||
long capturedTransferredBytes = transferredBytes;
|
||||
|
||||
Action<SyncProgressChangedEventArgs> progressHandler = (args) =>
|
||||
{
|
||||
long totalProgressBytes = capturedTransferredBytes + args.ReceivedBytesSize;
|
||||
|
||||
float overallPercent = totalBytes > 0
|
||||
? (float)(totalProgressBytes * 100.0 / totalBytes)
|
||||
: 0f;
|
||||
|
||||
overallPercent = Math.Max(0, Math.Min(100, overallPercent));
|
||||
|
||||
// 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);
|
||||
}
|
||||
};
|
||||
|
||||
using (var stream = File.OpenRead(file))
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
syncService.Push(
|
||||
stream,
|
||||
remoteFilePath,
|
||||
UnixFileStatus.DefaultFileMode,
|
||||
DateTime.Now,
|
||||
progressHandler,
|
||||
false);
|
||||
});
|
||||
}
|
||||
|
||||
transferredBytes += fileSize;
|
||||
}
|
||||
}
|
||||
|
||||
progressCallback?.Invoke(100, null);
|
||||
statusCallback?.Invoke("");
|
||||
|
||||
return new ProcessOutput($"{gameName}: OBB transfer: Success\n", "");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Log($"CopyOBBWithProgressAsync error: {ex.Message}", LogLevel.ERROR);
|
||||
return new ProcessOutput("", $"{gameName}: OBB transfer: Failed: {ex.Message}\n");
|
||||
}
|
||||
}
|
||||
|
||||
public static ProcessOutput RunAdbCommandToStringWOADB(string result, string path)
|
||||
{
|
||||
string command = result;
|
||||
@@ -99,54 +511,51 @@ namespace AndroidSideloader
|
||||
|
||||
_ = Logger.Log($"Running command: {logcmd}");
|
||||
|
||||
adb.StartInfo.FileName = "cmd.exe";
|
||||
adb.StartInfo.RedirectStandardError = true;
|
||||
adb.StartInfo.RedirectStandardInput = true;
|
||||
adb.StartInfo.RedirectStandardOutput = true;
|
||||
adb.StartInfo.CreateNoWindow = true;
|
||||
adb.StartInfo.UseShellExecute = false;
|
||||
adb.StartInfo.WorkingDirectory = Path.GetDirectoryName(path);
|
||||
_ = adb.Start();
|
||||
adb.StandardInput.WriteLine(command);
|
||||
adb.StandardInput.Flush();
|
||||
adb.StandardInput.Close();
|
||||
|
||||
|
||||
string output = "";
|
||||
string error = "";
|
||||
|
||||
try
|
||||
using (var adb = new Process())
|
||||
{
|
||||
output += adb.StandardOutput.ReadToEnd();
|
||||
error += adb.StandardError.ReadToEnd();
|
||||
}
|
||||
catch { }
|
||||
if (command.Contains("connect"))
|
||||
{
|
||||
bool graceful = adb.WaitForExit(3000);
|
||||
if (!graceful)
|
||||
adb.StartInfo.FileName = "cmd.exe";
|
||||
adb.StartInfo.RedirectStandardError = true;
|
||||
adb.StartInfo.RedirectStandardInput = true;
|
||||
adb.StartInfo.RedirectStandardOutput = true;
|
||||
adb.StartInfo.CreateNoWindow = true;
|
||||
adb.StartInfo.UseShellExecute = false;
|
||||
adb.StartInfo.WorkingDirectory = Path.GetDirectoryName(path);
|
||||
_ = adb.Start();
|
||||
adb.StandardInput.WriteLine(command);
|
||||
adb.StandardInput.Flush();
|
||||
adb.StandardInput.Close();
|
||||
|
||||
string output = "";
|
||||
string error = "";
|
||||
|
||||
try
|
||||
{
|
||||
adb.Kill();
|
||||
adb.WaitForExit();
|
||||
output += adb.StandardOutput.ReadToEnd();
|
||||
error += adb.StandardError.ReadToEnd();
|
||||
}
|
||||
}
|
||||
else if (command.Contains("connect"))
|
||||
{
|
||||
bool graceful = adb.WaitForExit(3000);
|
||||
if (!graceful)
|
||||
catch { }
|
||||
|
||||
if (command.Contains("connect"))
|
||||
{
|
||||
adb.Kill();
|
||||
adb.WaitForExit();
|
||||
bool graceful = adb.WaitForExit(3000);
|
||||
if (!graceful)
|
||||
{
|
||||
adb.Kill();
|
||||
adb.WaitForExit();
|
||||
}
|
||||
}
|
||||
|
||||
if (error.Contains("ADB_VENDOR_KEYS") && settings.AdbDebugWarned)
|
||||
{
|
||||
ADBDebugWarning();
|
||||
}
|
||||
|
||||
_ = Logger.Log(output);
|
||||
_ = Logger.Log(error, LogLevel.ERROR);
|
||||
return new ProcessOutput(output, error);
|
||||
}
|
||||
if (error.Contains("ADB_VENDOR_KEYS") && settings.AdbDebugWarned)
|
||||
{
|
||||
ADBDebugWarning();
|
||||
}
|
||||
_ = Logger.Log(output);
|
||||
_ = Logger.Log(error, LogLevel.ERROR);
|
||||
return new ProcessOutput(output, error);
|
||||
}
|
||||
|
||||
public static ProcessOutput RunCommandToString(string result, string path = "")
|
||||
{
|
||||
string command = result;
|
||||
@@ -160,37 +569,37 @@ namespace AndroidSideloader
|
||||
|
||||
try
|
||||
{
|
||||
using (var adb = new Process())
|
||||
using (var proc = new Process())
|
||||
{
|
||||
adb.StartInfo.FileName = $@"{Path.GetPathRoot(Environment.SystemDirectory)}\Windows\System32\cmd.exe";
|
||||
adb.StartInfo.Arguments = command;
|
||||
adb.StartInfo.RedirectStandardError = true;
|
||||
adb.StartInfo.RedirectStandardInput = true;
|
||||
adb.StartInfo.RedirectStandardOutput = true;
|
||||
adb.StartInfo.CreateNoWindow = true;
|
||||
adb.StartInfo.UseShellExecute = false;
|
||||
adb.StartInfo.WorkingDirectory = Path.GetDirectoryName(path);
|
||||
proc.StartInfo.FileName = $@"{Path.GetPathRoot(Environment.SystemDirectory)}\Windows\System32\cmd.exe";
|
||||
proc.StartInfo.Arguments = command;
|
||||
proc.StartInfo.RedirectStandardError = true;
|
||||
proc.StartInfo.RedirectStandardInput = true;
|
||||
proc.StartInfo.RedirectStandardOutput = true;
|
||||
proc.StartInfo.CreateNoWindow = true;
|
||||
proc.StartInfo.UseShellExecute = false;
|
||||
proc.StartInfo.WorkingDirectory = Path.GetDirectoryName(path);
|
||||
|
||||
adb.Start();
|
||||
adb.StandardInput.WriteLine(command);
|
||||
adb.StandardInput.Flush();
|
||||
adb.StandardInput.Close();
|
||||
proc.Start();
|
||||
proc.StandardInput.WriteLine(command);
|
||||
proc.StandardInput.Flush();
|
||||
proc.StandardInput.Close();
|
||||
|
||||
string output = adb.StandardOutput.ReadToEnd();
|
||||
string error = adb.StandardError.ReadToEnd();
|
||||
string output = proc.StandardOutput.ReadToEnd();
|
||||
string error = proc.StandardError.ReadToEnd();
|
||||
|
||||
if (command.Contains("connect"))
|
||||
{
|
||||
bool graceful = adb.WaitForExit(3000);
|
||||
bool graceful = proc.WaitForExit(3000);
|
||||
if (!graceful)
|
||||
{
|
||||
adb.Kill();
|
||||
adb.WaitForExit();
|
||||
proc.Kill();
|
||||
proc.WaitForExit();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
adb.WaitForExit();
|
||||
proc.WaitForExit();
|
||||
}
|
||||
|
||||
if (error.Contains("ADB_VENDOR_KEYS") && settings.AdbDebugWarned)
|
||||
@@ -198,12 +607,6 @@ namespace AndroidSideloader
|
||||
ADBDebugWarning();
|
||||
}
|
||||
|
||||
if (error.Contains("Asset path") && error.Contains("is neither a directory nor file"))
|
||||
{
|
||||
Logger.Log("Asset path error detected. The specified path might not exist or be accessible.", LogLevel.WARNING);
|
||||
// You might want to handle this specific error differently
|
||||
}
|
||||
|
||||
Logger.Log(output);
|
||||
Logger.Log(error, LogLevel.ERROR);
|
||||
|
||||
@@ -221,10 +624,11 @@ namespace AndroidSideloader
|
||||
{
|
||||
Program.form.Invoke(() =>
|
||||
{
|
||||
DialogResult dialogResult = FlexibleMessageBox.Show(Program.form, "On your headset, click on the Notifications Bell, and then select the USB Detected notification to enable Connections.", "ADB Debugging not enabled.", MessageBoxButtons.OKCancel);
|
||||
DialogResult dialogResult = FlexibleMessageBox.Show(Program.form,
|
||||
"On your headset, click on the Notifications Bell, and then select the USB Detected notification to enable Connections.",
|
||||
"ADB Debugging not enabled.", MessageBoxButtons.OKCancel);
|
||||
if (dialogResult == DialogResult.Cancel)
|
||||
{
|
||||
// settings.adbdebugwarned = true;
|
||||
settings.Save();
|
||||
}
|
||||
});
|
||||
@@ -234,6 +638,20 @@ namespace AndroidSideloader
|
||||
{
|
||||
ProcessOutput output = new ProcessOutput("", "");
|
||||
output += RunAdbCommandToString($"shell pm uninstall {package}");
|
||||
|
||||
// Prefix the output with the simple game name
|
||||
string label = Sideloader.gameNameToSimpleName(Sideloader.PackageNametoGameName(package));
|
||||
|
||||
if (!string.IsNullOrEmpty(output.Output))
|
||||
{
|
||||
output.Output = $"{label}: {output.Output}";
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(output.Error))
|
||||
{
|
||||
output.Error = $"{label}: {output.Error}";
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
@@ -255,84 +673,110 @@ namespace AndroidSideloader
|
||||
totalSize = long.Parse(foo[1]) / 1000;
|
||||
usedSize = long.Parse(foo[2]) / 1000;
|
||||
freeSize = long.Parse(foo[3]) / 1000;
|
||||
break; // Assuming we only need the first matching line
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 bool wirelessadbON;
|
||||
public static ProcessOutput Sideload(string path, string packagename = "")
|
||||
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)
|
||||
{
|
||||
ProcessOutput ret = new ProcessOutput();
|
||||
ret += RunAdbCommandToString($"install -g \"{path}\"");
|
||||
string out2 = ret.Output + ret.Error;
|
||||
if (out2.Contains("failed"))
|
||||
_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)
|
||||
{
|
||||
_ = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), $"Rookie Backups");
|
||||
_ = 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;
|
||||
if (!settings.AutoReinstall)
|
||||
{
|
||||
bool cancelClicked = false;
|
||||
double instUnitsPerSecond = dUnits / dt;
|
||||
|
||||
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 (_smoothedUnitsPerSecond <= 0)
|
||||
_smoothedUnitsPerSecond = instUnitsPerSecond;
|
||||
else
|
||||
_smoothedUnitsPerSecond = _alpha * instUnitsPerSecond + (1 - _alpha) * _smoothedUnitsPerSecond;
|
||||
|
||||
if (cancelClicked)
|
||||
return ret;
|
||||
}
|
||||
|
||||
Program.form.changeTitle("Performing reinstall, please wait...");
|
||||
_ = ADB.RunAdbCommandToString("kill-server");
|
||||
_ = ADB.RunAdbCommandToString("devices");
|
||||
_ = ADB.RunAdbCommandToString($"pull \"/sdcard/Android/data/{MainForm.CurrPCKG}\" \"{Environment.CurrentDirectory}\"");
|
||||
Program.form.changeTitle("Uninstalling game...");
|
||||
_ = Sideloader.UninstallGame(MainForm.CurrPCKG);
|
||||
Program.form.changeTitle("Reinstalling Game");
|
||||
ret += ADB.RunAdbCommandToString($"install -g \"{path}\"");
|
||||
_ = ADB.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)
|
||||
{
|
||||
Directory.Delete(directoryToDelete, true);
|
||||
}
|
||||
}
|
||||
|
||||
Program.form.changeTitle(" \n\n");
|
||||
return ret;
|
||||
}
|
||||
_lastSampleTimeUtc = now;
|
||||
_lastSampleDoneUnits = doneUnits;
|
||||
}
|
||||
|
||||
Program.form.changeTitle(string.Empty);
|
||||
return ret;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static ProcessOutput CopyOBB(string path)
|
||||
// Returns a countdown ETA for UI display
|
||||
public TimeSpan? GetDisplayEta()
|
||||
{
|
||||
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");
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
244
AdbCommandForm.cs
Normal file
@@ -0,0 +1,244 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace AndroidSideloader
|
||||
{
|
||||
public partial class AdbCommandForm : Form
|
||||
{
|
||||
public string Command { get; private set; }
|
||||
public bool ToggleUpdatesClicked { get; private set; }
|
||||
|
||||
public AdbCommandForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
// Use same icon as the executable
|
||||
this.Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath);
|
||||
this.ShowIcon = true; // Enable icon
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.lblTitle = new Label();
|
||||
this.txtCommand = new TextBox();
|
||||
this.btnSend = new RoundButton();
|
||||
this.btnToggleUpdates = new RoundButton();
|
||||
this.btnClose = new RoundButton();
|
||||
this.separator = new Panel();
|
||||
this.lblHint = new Label();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// lblTitle
|
||||
//
|
||||
this.lblTitle.AutoSize = true;
|
||||
this.lblTitle.Font = new Font("Segoe UI", 11F, FontStyle.Bold);
|
||||
this.lblTitle.ForeColor = Color.FromArgb(93, 203, 173);
|
||||
this.lblTitle.Location = new Point(20, 15);
|
||||
this.lblTitle.Name = "lblTitle";
|
||||
this.lblTitle.Size = new Size(140, 20);
|
||||
this.lblTitle.TabIndex = 0;
|
||||
this.lblTitle.Text = "Run ADB Command";
|
||||
//
|
||||
// txtCommand
|
||||
//
|
||||
this.txtCommand.BackColor = Color.FromArgb(40, 44, 52);
|
||||
this.txtCommand.BorderStyle = BorderStyle.FixedSingle;
|
||||
this.txtCommand.Font = new Font("Consolas", 10F);
|
||||
this.txtCommand.ForeColor = Color.White;
|
||||
this.txtCommand.Location = new Point(24, 50);
|
||||
this.txtCommand.Name = "txtCommand";
|
||||
this.txtCommand.Size = new Size(292, 23);
|
||||
this.txtCommand.TabIndex = 1;
|
||||
this.txtCommand.KeyPress += TxtCommand_KeyPress;
|
||||
//
|
||||
// lblHint
|
||||
//
|
||||
this.lblHint.AutoSize = true;
|
||||
this.lblHint.Font = new Font("Segoe UI", 8F);
|
||||
this.lblHint.ForeColor = Color.FromArgb(120, 120, 120);
|
||||
this.lblHint.Location = new Point(24, 78);
|
||||
this.lblHint.Name = "lblHint";
|
||||
this.lblHint.Size = new Size(200, 13);
|
||||
this.lblHint.TabIndex = 2;
|
||||
this.lblHint.Text = "Enter command without \"adb\" prefix";
|
||||
//
|
||||
// separator
|
||||
//
|
||||
this.separator.BackColor = Color.FromArgb(50, 55, 65);
|
||||
this.separator.Location = new Point(20, 105);
|
||||
this.separator.Name = "separator";
|
||||
this.separator.Size = new Size(300, 1);
|
||||
this.separator.TabIndex = 3;
|
||||
//
|
||||
// btnSend
|
||||
//
|
||||
this.btnSend.Active1 = Color.FromArgb(113, 223, 193);
|
||||
this.btnSend.Active2 = Color.FromArgb(113, 223, 193);
|
||||
this.btnSend.BackColor = Color.Transparent;
|
||||
this.btnSend.Cursor = Cursors.Hand;
|
||||
this.btnSend.DialogResult = DialogResult.OK;
|
||||
this.btnSend.Disabled1 = Color.FromArgb(32, 35, 45);
|
||||
this.btnSend.Disabled2 = Color.FromArgb(25, 28, 35);
|
||||
this.btnSend.DisabledStrokeColor = Color.FromArgb(50, 55, 65);
|
||||
this.btnSend.Font = new Font("Segoe UI", 9F, FontStyle.Bold);
|
||||
this.btnSend.ForeColor = Color.FromArgb(20, 24, 29);
|
||||
this.btnSend.Inactive1 = Color.FromArgb(93, 203, 173);
|
||||
this.btnSend.Inactive2 = Color.FromArgb(93, 203, 173);
|
||||
this.btnSend.Location = new Point(24, 120);
|
||||
this.btnSend.Name = "btnSend";
|
||||
this.btnSend.Radius = 5;
|
||||
this.btnSend.Size = new Size(140, 30);
|
||||
this.btnSend.Stroke = false;
|
||||
this.btnSend.StrokeColor = Color.FromArgb(93, 203, 173);
|
||||
this.btnSend.TabIndex = 4;
|
||||
this.btnSend.Text = "SEND COMMAND";
|
||||
this.btnSend.Transparency = false;
|
||||
this.btnSend.Click += BtnSend_Click;
|
||||
//
|
||||
// btnToggleUpdates
|
||||
//
|
||||
this.btnToggleUpdates.Active1 = Color.FromArgb(50, 55, 65);
|
||||
this.btnToggleUpdates.Active2 = Color.FromArgb(50, 55, 65);
|
||||
this.btnToggleUpdates.BackColor = Color.Transparent;
|
||||
this.btnToggleUpdates.Cursor = Cursors.Hand;
|
||||
this.btnToggleUpdates.DialogResult = DialogResult.None;
|
||||
this.btnToggleUpdates.Disabled1 = Color.FromArgb(32, 35, 45);
|
||||
this.btnToggleUpdates.Disabled2 = Color.FromArgb(25, 28, 35);
|
||||
this.btnToggleUpdates.DisabledStrokeColor = Color.FromArgb(50, 55, 65);
|
||||
this.btnToggleUpdates.Font = new Font("Segoe UI", 9F);
|
||||
this.btnToggleUpdates.ForeColor = Color.White;
|
||||
this.btnToggleUpdates.Inactive1 = Color.FromArgb(40, 44, 52);
|
||||
this.btnToggleUpdates.Inactive2 = Color.FromArgb(40, 44, 52);
|
||||
this.btnToggleUpdates.Location = new Point(176, 120);
|
||||
this.btnToggleUpdates.Name = "btnToggleUpdates";
|
||||
this.btnToggleUpdates.Radius = 5;
|
||||
this.btnToggleUpdates.Size = new Size(140, 30);
|
||||
this.btnToggleUpdates.Stroke = true;
|
||||
this.btnToggleUpdates.StrokeColor = Color.FromArgb(60, 65, 75);
|
||||
this.btnToggleUpdates.TabIndex = 5;
|
||||
this.btnToggleUpdates.Text = "Toggle OS Updates";
|
||||
this.btnToggleUpdates.Transparency = false;
|
||||
this.btnToggleUpdates.Click += BtnToggleUpdates_Click;
|
||||
//
|
||||
// btnClose
|
||||
//
|
||||
this.btnClose.Active1 = Color.FromArgb(60, 65, 75);
|
||||
this.btnClose.Active2 = Color.FromArgb(60, 65, 75);
|
||||
this.btnClose.BackColor = Color.Transparent;
|
||||
this.btnClose.Cursor = Cursors.Hand;
|
||||
this.btnClose.DialogResult = DialogResult.Cancel;
|
||||
this.btnClose.Disabled1 = Color.FromArgb(32, 35, 45);
|
||||
this.btnClose.Disabled2 = Color.FromArgb(25, 28, 35);
|
||||
this.btnClose.DisabledStrokeColor = Color.FromArgb(50, 55, 65);
|
||||
this.btnClose.Font = new Font("Segoe UI", 9F);
|
||||
this.btnClose.ForeColor = Color.White;
|
||||
this.btnClose.Inactive1 = Color.FromArgb(50, 55, 65);
|
||||
this.btnClose.Inactive2 = Color.FromArgb(50, 55, 65);
|
||||
this.btnClose.Location = new Point(24, 160);
|
||||
this.btnClose.Name = "btnClose";
|
||||
this.btnClose.Radius = 5;
|
||||
this.btnClose.Size = new Size(292, 30);
|
||||
this.btnClose.Stroke = true;
|
||||
this.btnClose.StrokeColor = Color.FromArgb(74, 74, 74);
|
||||
this.btnClose.TabIndex = 6;
|
||||
this.btnClose.Text = "Close";
|
||||
this.btnClose.Transparency = false;
|
||||
this.btnClose.Click += BtnClose_Click;
|
||||
//
|
||||
// AdbCommandForm
|
||||
//
|
||||
this.AcceptButton = this.btnSend;
|
||||
this.AutoScaleDimensions = new SizeF(6F, 13F);
|
||||
this.AutoScaleMode = AutoScaleMode.Font;
|
||||
this.BackColor = Color.FromArgb(20, 24, 29);
|
||||
this.CancelButton = this.btnClose;
|
||||
this.ClientSize = new Size(340, 210);
|
||||
this.Controls.Add(this.lblTitle);
|
||||
this.Controls.Add(this.txtCommand);
|
||||
this.Controls.Add(this.lblHint);
|
||||
this.Controls.Add(this.separator);
|
||||
this.Controls.Add(this.btnSend);
|
||||
this.Controls.Add(this.btnToggleUpdates);
|
||||
this.Controls.Add(this.btnClose);
|
||||
this.FormBorderStyle = FormBorderStyle.FixedSingle;
|
||||
this.MaximizeBox = false;
|
||||
this.MinimizeBox = false;
|
||||
this.Name = "AdbCommandForm";
|
||||
this.ShowIcon = false;
|
||||
this.StartPosition = FormStartPosition.CenterParent;
|
||||
this.Text = "ADB Command";
|
||||
this.Load += AdbCommandForm_Load;
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
}
|
||||
|
||||
private void AdbCommandForm_Load(object sender, EventArgs e)
|
||||
{
|
||||
txtCommand.Focus();
|
||||
}
|
||||
|
||||
private void TxtCommand_KeyPress(object sender, KeyPressEventArgs e)
|
||||
{
|
||||
if (e.KeyChar == (char)Keys.Enter)
|
||||
{
|
||||
e.Handled = true;
|
||||
BtnSend_Click(sender, e);
|
||||
}
|
||||
else if (e.KeyChar == (char)Keys.Escape)
|
||||
{
|
||||
e.Handled = true;
|
||||
this.DialogResult = DialogResult.Cancel;
|
||||
this.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private void BtnSend_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(txtCommand.Text))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Command = txtCommand.Text;
|
||||
ToggleUpdatesClicked = false;
|
||||
this.DialogResult = DialogResult.OK;
|
||||
this.Close();
|
||||
}
|
||||
|
||||
private void BtnToggleUpdates_Click(object sender, EventArgs e)
|
||||
{
|
||||
// Check current state and set the appropriate command
|
||||
string adbResult = ADB.RunAdbCommandToString("shell pm list packages -d").Output;
|
||||
bool isUpdatesDisabled = adbResult.Contains("com.oculus.updater");
|
||||
|
||||
if (isUpdatesDisabled)
|
||||
{
|
||||
Command = "shell pm enable com.oculus.updater";
|
||||
}
|
||||
else
|
||||
{
|
||||
Command = "shell pm disable-user --user 0 com.oculus.updater";
|
||||
}
|
||||
|
||||
ToggleUpdatesClicked = true;
|
||||
this.DialogResult = DialogResult.OK;
|
||||
this.Close();
|
||||
}
|
||||
|
||||
private void BtnClose_Click(object sender, EventArgs e)
|
||||
{
|
||||
this.DialogResult = DialogResult.Cancel;
|
||||
this.Close();
|
||||
}
|
||||
|
||||
private Label lblTitle;
|
||||
private TextBox txtCommand;
|
||||
private Label lblHint;
|
||||
private Panel separator;
|
||||
private RoundButton btnSend;
|
||||
private RoundButton btnToggleUpdates;
|
||||
private RoundButton btnClose;
|
||||
}
|
||||
}
|
||||
@@ -136,6 +136,9 @@
|
||||
<ManifestKeyFile>AndroidSideloader_TemporaryKey.pfx</ManifestKeyFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="AdvancedSharpAdbClient, Version=3.5.15.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>packages\AdvancedSharpAdbClient.3.5.15\lib\net45\AdvancedSharpAdbClient.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Costura, Version=5.7.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>packages\Costura.Fody.5.7.0\lib\netstandard1.0\Costura.dll</HintPath>
|
||||
</Reference>
|
||||
@@ -155,7 +158,7 @@
|
||||
<HintPath>.\SergeUtils.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System">
|
||||
<HintPath>C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.2\System.dll</HintPath>
|
||||
<HintPath>..\..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.2\System.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.ComponentModel.Composition" />
|
||||
<Reference Include="System.Core" />
|
||||
@@ -172,7 +175,7 @@
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Deployment" />
|
||||
<Reference Include="System.Drawing">
|
||||
<HintPath>C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.2\System.Drawing.dll</HintPath>
|
||||
<HintPath>..\..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.2\System.Drawing.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
@@ -181,6 +184,24 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="ADB.cs" />
|
||||
<Compile Include="AdbCommandForm.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="GalleryView.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="ModernListView.cs" />
|
||||
<Compile Include="ModernProgessBar.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="ModernQueuePanel.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="RoundButton.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
@@ -196,12 +217,6 @@
|
||||
<Compile Include="RoundedRectangleF.cs" />
|
||||
<Compile Include="Settings.cs" />
|
||||
<Compile Include="Sideloader\GetDependencies.cs" />
|
||||
<Compile Include="Splash.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Splash.Designer.cs">
|
||||
<DependentUpon>Splash.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Models\PublicConfig.cs" />
|
||||
<Compile Include="NewApps.cs">
|
||||
<SubType>Form</SubType>
|
||||
@@ -209,6 +224,9 @@
|
||||
<Compile Include="NewApps.Designer.cs">
|
||||
<DependentUpon>NewApps.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="ToggleSwitch.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Transparenter.cs" />
|
||||
<Compile Include="UpdateForm.cs">
|
||||
<SubType>Form</SubType>
|
||||
@@ -225,6 +243,8 @@
|
||||
<Compile Include="Sideloader\ProcessOutput.cs" />
|
||||
<Compile Include="Sideloader\RCLONE.cs" />
|
||||
<Compile Include="Sideloader\Utilities.cs" />
|
||||
<Compile Include="Utilities\DnsHelper.cs" />
|
||||
<Compile Include="Utilities\FileSystemUtilities.cs" />
|
||||
<Compile Include="Utilities\Logger.cs" />
|
||||
<Compile Include="QuestForm.cs">
|
||||
<SubType>Form</SubType>
|
||||
@@ -260,9 +280,6 @@
|
||||
<EmbeddedResource Include="DonorsListView.resx">
|
||||
<DependentUpon>DonorsListView.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Splash.resx">
|
||||
<DependentUpon>Splash.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="NewApps.resx">
|
||||
<DependentUpon>NewApps.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
@@ -275,14 +292,9 @@
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
<SubType>Designer</SubType>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
<DesignTime>True</DesignTime>
|
||||
</Compile>
|
||||
<EmbeddedResource Include="QuestForm.resx">
|
||||
<DependentUpon>QuestForm.cs</DependentUpon>
|
||||
<SubType>Designer</SubType>
|
||||
@@ -326,12 +338,6 @@
|
||||
<Content Include="changelog.txt" />
|
||||
<Content Include="icon.ico" />
|
||||
<Content Include="ChangelogHistory.txt" />
|
||||
<Content Include="Resources\pattern_cubes.png" />
|
||||
<Content Include="Resources\pattern_herringbone.png" />
|
||||
<Content Include="Resources\splashimage.png" />
|
||||
<Content Include="Resources\splashimage_deps.png" />
|
||||
<Content Include="Resources\splashimage_offline.png" />
|
||||
<Content Include="Resources\splashimage_rclone.png" />
|
||||
<None Include="Resources\battery.png" />
|
||||
<None Include="Resources\ajax-loader.gif" />
|
||||
<None Include="Resources\SearchGlass.PNG" />
|
||||
|
||||
20
App.config
@@ -33,7 +33,7 @@
|
||||
<value>True</value>
|
||||
</setting>
|
||||
<setting name="FontStyle" serializeAs="String">
|
||||
<value>Microsoft Sans Serif, 11.25pt</value>
|
||||
<value>Microsoft Sans Serif, 10pt</value>
|
||||
</setting>
|
||||
<setting name="BackPicturePath" serializeAs="String">
|
||||
<value />
|
||||
@@ -141,16 +141,16 @@
|
||||
<value>25, 25, 25</value>
|
||||
</setting>
|
||||
<setting name="SubButtonColor" serializeAs="String">
|
||||
<value>25, 25, 25</value>
|
||||
<value>42, 45, 58</value>
|
||||
</setting>
|
||||
<setting name="TextBoxColor" serializeAs="String">
|
||||
<value>25, 25, 25</value>
|
||||
</setting>
|
||||
<setting name="ButtonColor" serializeAs="String">
|
||||
<value>Black</value>
|
||||
<value>32, 35, 45</value>
|
||||
</setting>
|
||||
<setting name="BackColor" serializeAs="String">
|
||||
<value>1, 1, 1</value>
|
||||
<value>31, 34, 42</value>
|
||||
</setting>
|
||||
<setting name="AppPackages" serializeAs="String">
|
||||
<value />
|
||||
@@ -191,6 +191,18 @@
|
||||
<setting name="bandwidthLimit" serializeAs="String">
|
||||
<value>0</value>
|
||||
</setting>
|
||||
<setting name="useProxy" serializeAs="String">
|
||||
<value>False</value>
|
||||
</setting>
|
||||
<setting name="proxyAddress" serializeAs="String">
|
||||
<value />
|
||||
</setting>
|
||||
<setting name="proxyPort" serializeAs="String">
|
||||
<value />
|
||||
</setting>
|
||||
<setting name="selectedMirror" serializeAs="String">
|
||||
<value />
|
||||
</setting>
|
||||
</AndroidSideloader.Properties.Settings>
|
||||
<AndroidADB.Sideloader.Properties.Settings>
|
||||
<setting name="checkForUpdates" serializeAs="String">
|
||||
|
||||
@@ -1,4 +1,27 @@
|
||||
RSL 2.33
|
||||
RSL 3.0
|
||||
|
||||
Major Rookie overhaul with modernized UI, significant performance improvements and upgraded UX.
|
||||
|
||||
- Added high-performance Gallery View with search, filters, sorting, favorites, hover animations, smooth scrolling and uninstall buttons
|
||||
- Toggle seamlessly between List and Gallery views with your preference remembered across launches
|
||||
- Complete UI redesign with new dark theme, modernized components and subtle animations throughout
|
||||
- Refined navigation, layouts, sizing and color consistency across the entire application
|
||||
- Added uninstall buttons directly in List and Gallery views for quicker app management
|
||||
- Improved startup performance through overhaul of initialization logic, removal of splash screen, parallelized async loading, batched version retrieval, optimized metadata extraction and game list initialization
|
||||
- Instant list filtering through caching and streamlined filter logic (INSTALLED / UPDATE AVAILABLE / NEWER THAN LIST)
|
||||
- Improved search speed and responsiveness
|
||||
- Fixed and improved trailer handling with faster trailer loading
|
||||
- Fixed multiple startup issues including connection errors and zombie ADB instances
|
||||
- Added local blacklist support allowing users to permanently suppress donation prompts for specific apps
|
||||
- Reduced application size by removal of now unused assets
|
||||
|
||||
RSL 2.34
|
||||
|
||||
- Feature: Allow users to favorite games (right click on game)
|
||||
- Fix: Release Notes not showing with trailers enabled
|
||||
- Fix: Correct Discord Invite link on connection error
|
||||
|
||||
RSL 2.33
|
||||
|
||||
- Feature: Allow users to cancel backups
|
||||
- QoL: Lower ADB Version
|
||||
|
||||
@@ -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>
|
||||
|
||||
338
DonorsListView.Designer.cs
generated
@@ -30,88 +30,31 @@ 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();
|
||||
this.SkipButton = new AndroidSideloader.RoundButton();
|
||||
this.DonateButton = new AndroidSideloader.RoundButton();
|
||||
this.panel2 = new System.Windows.Forms.Panel();
|
||||
this.DonorsListView = 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()));
|
||||
this.VersionCodeIndex = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
|
||||
this.UpdateOrNew = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
|
||||
this.panel1 = new System.Windows.Forms.Panel();
|
||||
this.SkipButton = new AndroidSideloader.RoundButton();
|
||||
this.DonateButton = new AndroidSideloader.RoundButton();
|
||||
this.panel2 = new System.Windows.Forms.Panel();
|
||||
this.bothdet = new System.Windows.Forms.Label();
|
||||
this.newdet = new System.Windows.Forms.Label();
|
||||
this.upddet = new System.Windows.Forms.Label();
|
||||
this.label2 = new System.Windows.Forms.Label();
|
||||
this.TimerDesc = new System.Windows.Forms.Label();
|
||||
this.DonationTimer = new System.Windows.Forms.Timer(this.components);
|
||||
this.panel1.SuspendLayout();
|
||||
this.panel2.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// DonorsListView
|
||||
//
|
||||
this.DonorsListView.AccessibleRole = System.Windows.Forms.AccessibleRole.None;
|
||||
this.DonorsListView.BackColor = global::AndroidSideloader.Properties.Settings.Default.ComboBoxColor;
|
||||
this.DonorsListView.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||||
this.DonorsListView.CausesValidation = false;
|
||||
this.DonorsListView.CheckBoxes = true;
|
||||
this.DonorsListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
|
||||
this.GameNameIndex,
|
||||
this.PackageNameIndex,
|
||||
this.VersionCodeIndex,
|
||||
this.UpdateOrNew});
|
||||
this.DonorsListView.DataBindings.Add(new System.Windows.Forms.Binding("BackColor", global::AndroidSideloader.Properties.Settings.Default, "ComboBoxColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
|
||||
this.DonorsListView.DataBindings.Add(new System.Windows.Forms.Binding("Font", global::AndroidSideloader.Properties.Settings.Default, "FontStyle", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
|
||||
this.DonorsListView.DataBindings.Add(new System.Windows.Forms.Binding("ForeColor", global::AndroidSideloader.Properties.Settings.Default, "FontColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
|
||||
this.DonorsListView.Font = global::AndroidSideloader.Properties.Settings.Default.FontStyle;
|
||||
this.DonorsListView.ForeColor = global::AndroidSideloader.Properties.Settings.Default.FontColor;
|
||||
this.DonorsListView.FullRowSelect = true;
|
||||
this.DonorsListView.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable;
|
||||
this.DonorsListView.HideSelection = false;
|
||||
this.DonorsListView.ImeMode = System.Windows.Forms.ImeMode.On;
|
||||
this.DonorsListView.Location = new System.Drawing.Point(6, 6);
|
||||
this.DonorsListView.MinimumSize = new System.Drawing.Size(100, 100);
|
||||
this.DonorsListView.Name = "DonorsListView";
|
||||
this.DonorsListView.RightToLeftLayout = true;
|
||||
this.DonorsListView.Size = new System.Drawing.Size(419, 219);
|
||||
this.DonorsListView.TabIndex = 0;
|
||||
this.DonorsListView.TileSize = new System.Drawing.Size(100, 100);
|
||||
this.DonorsListView.UseCompatibleStateImageBehavior = false;
|
||||
this.DonorsListView.View = System.Windows.Forms.View.Details;
|
||||
this.DonorsListView.ItemChecked += new System.Windows.Forms.ItemCheckedEventHandler(this.DonorsListView_ItemChecked);
|
||||
this.DonorsListView.MouseDown += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseDown);
|
||||
this.DonorsListView.MouseMove += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseMove);
|
||||
this.DonorsListView.MouseUp += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseUp);
|
||||
//
|
||||
// GameNameIndex
|
||||
//
|
||||
this.GameNameIndex.Text = "Game Name";
|
||||
this.GameNameIndex.Width = 219;
|
||||
//
|
||||
// PackageNameIndex
|
||||
//
|
||||
this.PackageNameIndex.DisplayIndex = 2;
|
||||
this.PackageNameIndex.Text = "Packagename";
|
||||
this.PackageNameIndex.Width = 0;
|
||||
//
|
||||
// VersionCodeIndex
|
||||
//
|
||||
this.VersionCodeIndex.DisplayIndex = 3;
|
||||
this.VersionCodeIndex.Text = "Version";
|
||||
this.VersionCodeIndex.Width = 113;
|
||||
//
|
||||
// UpdateOrNew
|
||||
//
|
||||
this.UpdateOrNew.DisplayIndex = 1;
|
||||
this.UpdateOrNew.Text = "Donation Type";
|
||||
this.UpdateOrNew.Width = 85;
|
||||
//
|
||||
//
|
||||
// panel1
|
||||
//
|
||||
this.panel1.BackColor = global::AndroidSideloader.Properties.Settings.Default.BackColor;
|
||||
this.panel1.BackgroundImage = global::AndroidSideloader.Properties.Resources.pattern_cubes;
|
||||
//
|
||||
this.panel1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(24)))), ((int)(((byte)(29)))));
|
||||
this.panel1.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None;
|
||||
this.panel1.Controls.Add(this.skip_forever);
|
||||
this.panel1.Controls.Add(this.SkipButton);
|
||||
this.panel1.Controls.Add(this.DonateButton);
|
||||
this.panel1.Controls.Add(this.panel2);
|
||||
@@ -120,166 +63,252 @@ namespace AndroidSideloader
|
||||
this.panel1.Controls.Add(this.upddet);
|
||||
this.panel1.Controls.Add(this.label2);
|
||||
this.panel1.Controls.Add(this.TimerDesc);
|
||||
this.panel1.DataBindings.Add(new System.Windows.Forms.Binding("BackColor", global::AndroidSideloader.Properties.Settings.Default, "BackColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
|
||||
this.panel1.Location = new System.Drawing.Point(-7, -7);
|
||||
this.panel1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.panel1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(160)))), ((int)(((byte)(165)))), ((int)(((byte)(175)))));
|
||||
this.panel1.Location = new System.Drawing.Point(0, 0);
|
||||
this.panel1.Name = "panel1";
|
||||
this.panel1.Size = new System.Drawing.Size(463, 345);
|
||||
this.panel1.Padding = new System.Windows.Forms.Padding(16);
|
||||
this.panel1.Size = new System.Drawing.Size(460, 420);
|
||||
this.panel1.TabIndex = 1;
|
||||
this.panel1.MouseDown += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseDown);
|
||||
this.panel1.MouseMove += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseMove);
|
||||
this.panel1.MouseUp += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseUp);
|
||||
//
|
||||
//
|
||||
// skip_forever
|
||||
//
|
||||
this.skip_forever.Active1 = System.Drawing.Color.FromArgb(((int)(((byte)(45)))), ((int)(((byte)(52)))), ((int)(((byte)(62)))));
|
||||
this.skip_forever.Active2 = System.Drawing.Color.FromArgb(((int)(((byte)(45)))), ((int)(((byte)(52)))), ((int)(((byte)(62)))));
|
||||
this.skip_forever.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.skip_forever.BackColor = System.Drawing.Color.Transparent;
|
||||
this.skip_forever.DialogResult = System.Windows.Forms.DialogResult.OK;
|
||||
this.skip_forever.Disabled1 = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(35)))), ((int)(((byte)(45)))));
|
||||
this.skip_forever.Disabled2 = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(35)))), ((int)(((byte)(45)))));
|
||||
this.skip_forever.DisabledStrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(50)))), ((int)(((byte)(55)))), ((int)(((byte)(65)))));
|
||||
this.skip_forever.Font = new System.Drawing.Font("Segoe UI Semibold", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.skip_forever.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(160)))), ((int)(((byte)(165)))), ((int)(((byte)(175)))));
|
||||
this.skip_forever.Inactive1 = System.Drawing.Color.FromArgb(((int)(((byte)(30)))), ((int)(((byte)(35)))), ((int)(((byte)(42)))));
|
||||
this.skip_forever.Inactive2 = System.Drawing.Color.FromArgb(((int)(((byte)(30)))), ((int)(((byte)(35)))), ((int)(((byte)(42)))));
|
||||
this.skip_forever.Location = new System.Drawing.Point(20, 380);
|
||||
this.skip_forever.Margin = new System.Windows.Forms.Padding(0);
|
||||
this.skip_forever.Name = "skip_forever";
|
||||
this.skip_forever.Radius = 4;
|
||||
this.skip_forever.Size = new System.Drawing.Size(420, 32);
|
||||
this.skip_forever.Stroke = true;
|
||||
this.skip_forever.StrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(93)))), ((int)(((byte)(203)))), ((int)(((byte)(173)))));
|
||||
this.skip_forever.TabIndex = 97;
|
||||
this.skip_forever.Text = "Add to blacklist / Never ask for the selected apps again";
|
||||
this.skip_forever.Transparency = false;
|
||||
this.skip_forever.Click += new System.EventHandler(this.skip_forever_Click);
|
||||
//
|
||||
// SkipButton
|
||||
//
|
||||
this.SkipButton.Active1 = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(40)))), ((int)(((byte)(40)))));
|
||||
this.SkipButton.Active2 = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(40)))), ((int)(((byte)(40)))));
|
||||
this.SkipButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.SkipButton.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(20)))), ((int)(((byte)(20)))));
|
||||
this.SkipButton.DataBindings.Add(new System.Windows.Forms.Binding("BackColor", global::AndroidSideloader.Properties.Settings.Default, "SubButtonColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
|
||||
this.SkipButton.DataBindings.Add(new System.Windows.Forms.Binding("ForeColor", global::AndroidSideloader.Properties.Settings.Default, "FontColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
|
||||
this.SkipButton.DataBindings.Add(new System.Windows.Forms.Binding("Font", global::AndroidSideloader.Properties.Settings.Default, "FontStyle", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
|
||||
//
|
||||
this.SkipButton.Active1 = System.Drawing.Color.FromArgb(((int)(((byte)(45)))), ((int)(((byte)(52)))), ((int)(((byte)(62)))));
|
||||
this.SkipButton.Active2 = System.Drawing.Color.FromArgb(((int)(((byte)(45)))), ((int)(((byte)(52)))), ((int)(((byte)(62)))));
|
||||
this.SkipButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
this.SkipButton.BackColor = System.Drawing.Color.Transparent;
|
||||
this.SkipButton.DialogResult = System.Windows.Forms.DialogResult.OK;
|
||||
this.SkipButton.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F);
|
||||
this.SkipButton.ForeColor = global::AndroidSideloader.Properties.Settings.Default.FontColor;
|
||||
this.SkipButton.Inactive1 = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(20)))), ((int)(((byte)(20)))));
|
||||
this.SkipButton.Inactive2 = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(20)))), ((int)(((byte)(20)))));
|
||||
this.SkipButton.Location = new System.Drawing.Point(22, 277);
|
||||
this.SkipButton.Disabled1 = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(35)))), ((int)(((byte)(45)))));
|
||||
this.SkipButton.Disabled2 = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(35)))), ((int)(((byte)(45)))));
|
||||
this.SkipButton.DisabledStrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(50)))), ((int)(((byte)(55)))), ((int)(((byte)(65)))));
|
||||
this.SkipButton.Font = new System.Drawing.Font("Segoe UI Semibold", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.SkipButton.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(160)))), ((int)(((byte)(165)))), ((int)(((byte)(175)))));
|
||||
this.SkipButton.Inactive1 = System.Drawing.Color.FromArgb(((int)(((byte)(30)))), ((int)(((byte)(35)))), ((int)(((byte)(42)))));
|
||||
this.SkipButton.Inactive2 = System.Drawing.Color.FromArgb(((int)(((byte)(30)))), ((int)(((byte)(35)))), ((int)(((byte)(42)))));
|
||||
this.SkipButton.Location = new System.Drawing.Point(20, 326);
|
||||
this.SkipButton.Margin = new System.Windows.Forms.Padding(0);
|
||||
this.SkipButton.Name = "SkipButton";
|
||||
this.SkipButton.Radius = 5;
|
||||
this.SkipButton.Size = new System.Drawing.Size(102, 36);
|
||||
this.SkipButton.Radius = 4;
|
||||
this.SkipButton.Size = new System.Drawing.Size(100, 32);
|
||||
this.SkipButton.Stroke = true;
|
||||
this.SkipButton.StrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(74)))), ((int)(((byte)(74)))), ((int)(((byte)(74)))));
|
||||
this.SkipButton.StrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(93)))), ((int)(((byte)(203)))), ((int)(((byte)(173)))));
|
||||
this.SkipButton.TabIndex = 96;
|
||||
this.SkipButton.Text = "Skip";
|
||||
this.SkipButton.Transparency = false;
|
||||
this.SkipButton.Click += new System.EventHandler(this.SkipButton_Click);
|
||||
//
|
||||
//
|
||||
// DonateButton
|
||||
//
|
||||
this.DonateButton.Active1 = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(40)))), ((int)(((byte)(40)))));
|
||||
this.DonateButton.Active2 = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(40)))), ((int)(((byte)(40)))));
|
||||
this.DonateButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.DonateButton.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(20)))), ((int)(((byte)(20)))));
|
||||
this.DonateButton.DataBindings.Add(new System.Windows.Forms.Binding("BackColor", global::AndroidSideloader.Properties.Settings.Default, "SubButtonColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
|
||||
this.DonateButton.DataBindings.Add(new System.Windows.Forms.Binding("ForeColor", global::AndroidSideloader.Properties.Settings.Default, "FontColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
|
||||
this.DonateButton.DataBindings.Add(new System.Windows.Forms.Binding("Font", global::AndroidSideloader.Properties.Settings.Default, "FontStyle", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
|
||||
//
|
||||
this.DonateButton.Active1 = System.Drawing.Color.FromArgb(((int)(((byte)(50)))), ((int)(((byte)(140)))), ((int)(((byte)(115)))));
|
||||
this.DonateButton.Active2 = System.Drawing.Color.FromArgb(((int)(((byte)(45)))), ((int)(((byte)(125)))), ((int)(((byte)(105)))));
|
||||
this.DonateButton.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.DonateButton.BackColor = System.Drawing.Color.Transparent;
|
||||
this.DonateButton.DialogResult = System.Windows.Forms.DialogResult.OK;
|
||||
this.DonateButton.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F);
|
||||
this.DonateButton.ForeColor = global::AndroidSideloader.Properties.Settings.Default.FontColor;
|
||||
this.DonateButton.Inactive1 = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(20)))), ((int)(((byte)(20)))));
|
||||
this.DonateButton.Inactive2 = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(20)))), ((int)(((byte)(20)))));
|
||||
this.DonateButton.Location = new System.Drawing.Point(130, 277);
|
||||
this.DonateButton.Disabled1 = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(35)))), ((int)(((byte)(45)))));
|
||||
this.DonateButton.Disabled2 = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(28)))), ((int)(((byte)(35)))));
|
||||
this.DonateButton.DisabledStrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(50)))), ((int)(((byte)(55)))), ((int)(((byte)(65)))));
|
||||
this.DonateButton.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Bold);
|
||||
this.DonateButton.ForeColor = System.Drawing.Color.White;
|
||||
this.DonateButton.Inactive1 = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(120)))), ((int)(((byte)(100)))));
|
||||
this.DonateButton.Inactive2 = System.Drawing.Color.FromArgb(((int)(((byte)(35)))), ((int)(((byte)(100)))), ((int)(((byte)(85)))));
|
||||
this.DonateButton.Location = new System.Drawing.Point(128, 326);
|
||||
this.DonateButton.Margin = new System.Windows.Forms.Padding(0);
|
||||
this.DonateButton.Name = "DonateButton";
|
||||
this.DonateButton.Radius = 5;
|
||||
this.DonateButton.Size = new System.Drawing.Size(311, 36);
|
||||
this.DonateButton.Radius = 4;
|
||||
this.DonateButton.Size = new System.Drawing.Size(312, 32);
|
||||
this.DonateButton.Stroke = true;
|
||||
this.DonateButton.StrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(74)))), ((int)(((byte)(74)))), ((int)(((byte)(74)))));
|
||||
this.DonateButton.StrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(60)))), ((int)(((byte)(150)))), ((int)(((byte)(125)))));
|
||||
this.DonateButton.TabIndex = 95;
|
||||
this.DonateButton.Text = "Automatically share selected apps";
|
||||
this.DonateButton.Text = "Share Selected Apps";
|
||||
this.DonateButton.Transparency = false;
|
||||
this.DonateButton.Click += new System.EventHandler(this.DonateButton_Click);
|
||||
//
|
||||
//
|
||||
// panel2
|
||||
//
|
||||
this.panel2.BackColor = global::AndroidSideloader.Properties.Settings.Default.SubButtonColor;
|
||||
//
|
||||
this.panel2.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.panel2.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(28)))), ((int)(((byte)(32)))), ((int)(((byte)(38)))));
|
||||
this.panel2.Controls.Add(this.DonorsListView);
|
||||
this.panel2.DataBindings.Add(new System.Windows.Forms.Binding("BackColor", global::AndroidSideloader.Properties.Settings.Default, "SubButtonColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
|
||||
this.panel2.Location = new System.Drawing.Point(16, 43);
|
||||
this.panel2.Location = new System.Drawing.Point(20, 70);
|
||||
this.panel2.Name = "panel2";
|
||||
this.panel2.Size = new System.Drawing.Size(430, 230);
|
||||
this.panel2.Padding = new System.Windows.Forms.Padding(1);
|
||||
this.panel2.Size = new System.Drawing.Size(420, 250);
|
||||
this.panel2.TabIndex = 2;
|
||||
//
|
||||
//
|
||||
// DonorsListView
|
||||
//
|
||||
this.DonorsListView.AccessibleRole = System.Windows.Forms.AccessibleRole.None;
|
||||
this.DonorsListView.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(28)))), ((int)(((byte)(32)))), ((int)(((byte)(38)))));
|
||||
this.DonorsListView.BorderStyle = System.Windows.Forms.BorderStyle.None;
|
||||
this.DonorsListView.CausesValidation = false;
|
||||
this.DonorsListView.CheckBoxes = true;
|
||||
this.DonorsListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
|
||||
this.GameNameIndex,
|
||||
this.PackageNameIndex,
|
||||
this.VersionCodeIndex,
|
||||
this.UpdateOrNew});
|
||||
this.DonorsListView.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.DonorsListView.Font = new System.Drawing.Font("Segoe UI", 9.5F);
|
||||
this.DonorsListView.ForeColor = System.Drawing.Color.White;
|
||||
this.DonorsListView.FullRowSelect = true;
|
||||
this.DonorsListView.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable;
|
||||
this.DonorsListView.HideSelection = false;
|
||||
this.DonorsListView.Location = new System.Drawing.Point(1, 1);
|
||||
this.DonorsListView.MinimumSize = new System.Drawing.Size(100, 100);
|
||||
this.DonorsListView.Name = "DonorsListView";
|
||||
this.DonorsListView.Size = new System.Drawing.Size(418, 248);
|
||||
this.DonorsListView.TabIndex = 0;
|
||||
this.DonorsListView.UseCompatibleStateImageBehavior = false;
|
||||
this.DonorsListView.View = System.Windows.Forms.View.Details;
|
||||
this.DonorsListView.ItemChecked += new System.Windows.Forms.ItemCheckedEventHandler(this.DonorsListView_ItemChecked);
|
||||
this.DonorsListView.MouseDown += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseDown);
|
||||
this.DonorsListView.MouseMove += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseMove);
|
||||
this.DonorsListView.MouseUp += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseUp);
|
||||
//
|
||||
// GameNameIndex
|
||||
//
|
||||
this.GameNameIndex.Text = "App Name";
|
||||
this.GameNameIndex.Width = 220;
|
||||
//
|
||||
// PackageNameIndex
|
||||
//
|
||||
this.PackageNameIndex.DisplayIndex = 2;
|
||||
this.PackageNameIndex.Text = "Package";
|
||||
this.PackageNameIndex.Width = 0;
|
||||
//
|
||||
// VersionCodeIndex
|
||||
//
|
||||
this.VersionCodeIndex.DisplayIndex = 3;
|
||||
this.VersionCodeIndex.Text = "Version";
|
||||
this.VersionCodeIndex.Width = 100;
|
||||
//
|
||||
// UpdateOrNew
|
||||
//
|
||||
this.UpdateOrNew.DisplayIndex = 1;
|
||||
this.UpdateOrNew.Text = "Type";
|
||||
this.UpdateOrNew.Width = 80;
|
||||
//
|
||||
// bothdet
|
||||
//
|
||||
//
|
||||
this.bothdet.AutoSize = true;
|
||||
this.bothdet.BackColor = System.Drawing.Color.Transparent;
|
||||
this.bothdet.Font = new System.Drawing.Font("Microsoft Sans Serif", 10.25F, System.Drawing.FontStyle.Bold);
|
||||
this.bothdet.Location = new System.Drawing.Point(125, 7);
|
||||
this.bothdet.Font = new System.Drawing.Font("Segoe UI", 11F, System.Drawing.FontStyle.Bold);
|
||||
this.bothdet.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(93)))), ((int)(((byte)(203)))), ((int)(((byte)(173)))));
|
||||
this.bothdet.Location = new System.Drawing.Point(20, 15);
|
||||
this.bothdet.Name = "bothdet";
|
||||
this.bothdet.Size = new System.Drawing.Size(213, 17);
|
||||
this.bothdet.Size = new System.Drawing.Size(228, 20);
|
||||
this.bothdet.TabIndex = 3;
|
||||
this.bothdet.Text = "Updates/new apps detected!";
|
||||
this.bothdet.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
||||
this.bothdet.Text = "Updates && New Apps Available";
|
||||
this.bothdet.Visible = false;
|
||||
this.bothdet.MouseDown += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseDown);
|
||||
this.bothdet.MouseMove += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseMove);
|
||||
this.bothdet.MouseUp += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseUp);
|
||||
//
|
||||
//
|
||||
// newdet
|
||||
//
|
||||
//
|
||||
this.newdet.AutoSize = true;
|
||||
this.newdet.BackColor = System.Drawing.Color.Transparent;
|
||||
this.newdet.Font = new System.Drawing.Font("Microsoft Sans Serif", 10.25F, System.Drawing.FontStyle.Bold);
|
||||
this.newdet.Location = new System.Drawing.Point(120, 7);
|
||||
this.newdet.Font = new System.Drawing.Font("Segoe UI", 11F, System.Drawing.FontStyle.Bold);
|
||||
this.newdet.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(93)))), ((int)(((byte)(203)))), ((int)(((byte)(173)))));
|
||||
this.newdet.Location = new System.Drawing.Point(20, 15);
|
||||
this.newdet.Name = "newdet";
|
||||
this.newdet.Size = new System.Drawing.Size(150, 17);
|
||||
this.newdet.Size = new System.Drawing.Size(149, 20);
|
||||
this.newdet.TabIndex = 3;
|
||||
this.newdet.Text = "New apps detected!";
|
||||
this.newdet.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
||||
this.newdet.Text = "New Apps Available";
|
||||
this.newdet.Visible = false;
|
||||
this.newdet.MouseDown += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseDown);
|
||||
this.newdet.MouseMove += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseMove);
|
||||
this.newdet.MouseUp += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseUp);
|
||||
//
|
||||
//
|
||||
// upddet
|
||||
//
|
||||
//
|
||||
this.upddet.AutoSize = true;
|
||||
this.upddet.BackColor = System.Drawing.Color.Transparent;
|
||||
this.upddet.Font = new System.Drawing.Font("Microsoft Sans Serif", 10.25F, System.Drawing.FontStyle.Bold);
|
||||
this.upddet.Location = new System.Drawing.Point(120, 7);
|
||||
this.upddet.Font = new System.Drawing.Font("Segoe UI", 11F, System.Drawing.FontStyle.Bold);
|
||||
this.upddet.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(93)))), ((int)(((byte)(203)))), ((int)(((byte)(173)))));
|
||||
this.upddet.Location = new System.Drawing.Point(20, 15);
|
||||
this.upddet.Name = "upddet";
|
||||
this.upddet.Size = new System.Drawing.Size(185, 17);
|
||||
this.upddet.Size = new System.Drawing.Size(135, 20);
|
||||
this.upddet.TabIndex = 3;
|
||||
this.upddet.Text = "Game Updates Detected";
|
||||
this.upddet.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
||||
this.upddet.Text = "Updates Available";
|
||||
this.upddet.Visible = false;
|
||||
this.upddet.MouseDown += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseDown);
|
||||
this.upddet.MouseMove += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseMove);
|
||||
this.upddet.MouseUp += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseUp);
|
||||
//
|
||||
//
|
||||
// label2
|
||||
//
|
||||
//
|
||||
this.label2.AutoSize = true;
|
||||
this.label2.BackColor = System.Drawing.Color.Transparent;
|
||||
this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.label2.Location = new System.Drawing.Point(23, 23);
|
||||
this.label2.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||
this.label2.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(160)))), ((int)(((byte)(165)))), ((int)(((byte)(175)))));
|
||||
this.label2.Location = new System.Drawing.Point(20, 42);
|
||||
this.label2.Name = "label2";
|
||||
this.label2.Size = new System.Drawing.Size(416, 15);
|
||||
this.label2.Size = new System.Drawing.Size(338, 15);
|
||||
this.label2.TabIndex = 3;
|
||||
this.label2.Text = "All Apps are donated by users! Without them none of this would be possible!";
|
||||
this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
||||
this.label2.Text = "All apps are donated by users! Help the community by sharing.";
|
||||
this.label2.MouseDown += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseDown);
|
||||
this.label2.MouseMove += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseMove);
|
||||
this.label2.MouseUp += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseUp);
|
||||
//
|
||||
//
|
||||
// TimerDesc
|
||||
//
|
||||
//
|
||||
this.TimerDesc.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
this.TimerDesc.AutoSize = true;
|
||||
this.TimerDesc.BackColor = System.Drawing.Color.Transparent;
|
||||
this.TimerDesc.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.TimerDesc.Location = new System.Drawing.Point(28, 321);
|
||||
this.TimerDesc.Font = new System.Drawing.Font("Segoe UI", 8F);
|
||||
this.TimerDesc.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(120)))), ((int)(((byte)(125)))), ((int)(((byte)(135)))));
|
||||
this.TimerDesc.Location = new System.Drawing.Point(79, 362);
|
||||
this.TimerDesc.Name = "TimerDesc";
|
||||
this.TimerDesc.Size = new System.Drawing.Size(406, 13);
|
||||
this.TimerDesc.Size = new System.Drawing.Size(292, 13);
|
||||
this.TimerDesc.TabIndex = 3;
|
||||
this.TimerDesc.Text = "Don\'t share free apps. Rookie will extract/upload apps in background.";
|
||||
this.TimerDesc.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
||||
this.TimerDesc.Text = "Don\'t share free apps. Upload happens in background.";
|
||||
this.TimerDesc.MouseDown += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseDown);
|
||||
this.TimerDesc.MouseMove += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseMove);
|
||||
this.TimerDesc.MouseUp += new System.Windows.Forms.MouseEventHandler(this.DonorsListViewForm_MouseUp);
|
||||
//
|
||||
//
|
||||
// DonorsListViewForm
|
||||
//
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.BackColor = System.Drawing.Color.Gainsboro;
|
||||
this.ClientSize = new System.Drawing.Size(449, 336);
|
||||
this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(24)))), ((int)(((byte)(29)))));
|
||||
this.ClientSize = new System.Drawing.Size(460, 420);
|
||||
this.ControlBox = false;
|
||||
this.Controls.Add(this.panel1);
|
||||
this.ForeColor = System.Drawing.Color.White;
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
|
||||
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
|
||||
this.Name = "DonorsListViewForm";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||
this.Load += new System.EventHandler(this.DonorsListViewForm_Load);
|
||||
@@ -310,5 +339,6 @@ namespace AndroidSideloader
|
||||
private System.Windows.Forms.Panel panel2;
|
||||
private RoundButton DonateButton;
|
||||
private RoundButton SkipButton;
|
||||
private RoundButton skip_forever;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,69 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace AndroidSideloader
|
||||
{
|
||||
public partial class DonorsListViewForm : Form
|
||||
{
|
||||
// Modern theme colors
|
||||
private static readonly Color BackgroundColor = Color.FromArgb(20, 24, 29);
|
||||
private static readonly Color BorderColor = Color.FromArgb(70, 80, 100);
|
||||
private static readonly Color UpdateHighlightColor = Color.FromArgb(0, 79, 97);
|
||||
|
||||
private bool mouseDown;
|
||||
private Point lastLocation;
|
||||
// Shadow and corner settings
|
||||
private const int CS_DROPSHADOW = 0x00020000;
|
||||
private const int WM_NCLBUTTONDOWN = 0xA1;
|
||||
private const int HT_CAPTION = 0x2;
|
||||
private const int SHADOW_SIZE = 2;
|
||||
private const int CONTENT_RADIUS = 10;
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern bool ReleaseCapture();
|
||||
|
||||
protected override CreateParams CreateParams
|
||||
{
|
||||
get
|
||||
{
|
||||
CreateParams cp = base.CreateParams;
|
||||
cp.ClassStyle |= CS_DROPSHADOW;
|
||||
return cp;
|
||||
}
|
||||
}
|
||||
|
||||
public DonorsListViewForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
// Use same icon as the executable
|
||||
this.Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath);
|
||||
|
||||
ApplyModernTheme();
|
||||
CenterToScreen();
|
||||
|
||||
Donors.initDonorGames();
|
||||
List<ListViewItem> DGameList = new List<ListViewItem>();
|
||||
|
||||
var seen = new HashSet<string>();
|
||||
var DGameList = new List<ListViewItem>();
|
||||
|
||||
foreach (string[] release in Donors.donorGames)
|
||||
{
|
||||
ListViewItem DGame = new ListViewItem(release);
|
||||
DGameList.Add(DGame);
|
||||
if (release.Length == 0) continue;
|
||||
string key = release[0];
|
||||
if (seen.Add(key))
|
||||
{
|
||||
DGameList.Add(new ListViewItem(release));
|
||||
}
|
||||
}
|
||||
|
||||
ListViewItem[] arr = DGameList.ToArray();
|
||||
DonorsListView.BeginUpdate();
|
||||
DonorsListView.Items.Clear();
|
||||
@@ -28,35 +71,145 @@ namespace AndroidSideloader
|
||||
DonorsListView.EndUpdate();
|
||||
}
|
||||
|
||||
private void ApplyModernTheme()
|
||||
{
|
||||
this.FormBorderStyle = FormBorderStyle.None;
|
||||
this.BackColor = Color.FromArgb(25, 25, 30);
|
||||
this.Padding = new Padding(5);
|
||||
|
||||
panel1.BackColor = BackgroundColor;
|
||||
panel1.Location = new Point(6, 6);
|
||||
panel1.Size = new Size(this.ClientSize.Width - 12, this.ClientSize.Height - 12);
|
||||
panel1.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
|
||||
|
||||
this.Paint += Form_Paint;
|
||||
|
||||
// Close button
|
||||
var closeButton = new Button
|
||||
{
|
||||
Text = "✕",
|
||||
Font = new Font("Segoe UI", 9F),
|
||||
ForeColor = Color.White,
|
||||
BackColor = BackgroundColor,
|
||||
FlatStyle = FlatStyle.Flat,
|
||||
Size = new Size(30, 28),
|
||||
Location = new Point(panel1.Width - 35, 5),
|
||||
Cursor = Cursors.Hand,
|
||||
TabStop = false
|
||||
};
|
||||
closeButton.FlatAppearance.BorderSize = 0;
|
||||
closeButton.FlatAppearance.MouseOverBackColor = Color.FromArgb(200, 60, 60);
|
||||
closeButton.Click += (s, e) => Close();
|
||||
panel1.Controls.Add(closeButton);
|
||||
closeButton.BringToFront();
|
||||
|
||||
// Enable dragging
|
||||
panel1.MouseDown += TitleArea_MouseDown;
|
||||
foreach (Control ctrl in panel1.Controls)
|
||||
{
|
||||
if (ctrl is Label)
|
||||
{
|
||||
ctrl.MouseDown += TitleArea_MouseDown;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void TitleArea_MouseDown(object sender, MouseEventArgs e)
|
||||
{
|
||||
if (e.Button == MouseButtons.Left)
|
||||
{
|
||||
ReleaseCapture();
|
||||
SendMessage(this.Handle, WM_NCLBUTTONDOWN, (IntPtr)HT_CAPTION, IntPtr.Zero);
|
||||
}
|
||||
}
|
||||
|
||||
private void Form_Paint(object sender, PaintEventArgs e)
|
||||
{
|
||||
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
|
||||
e.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
|
||||
|
||||
int w = this.Width;
|
||||
int h = this.Height;
|
||||
|
||||
// Draw shadow layers
|
||||
for (int i = SHADOW_SIZE; i >= 1; i--)
|
||||
{
|
||||
int alpha = (SHADOW_SIZE - i + 1) * 12;
|
||||
Rectangle shadowRect = new Rectangle(
|
||||
SHADOW_SIZE - i,
|
||||
SHADOW_SIZE - i,
|
||||
w - (SHADOW_SIZE - i) * 2 - 1,
|
||||
h - (SHADOW_SIZE - i) * 2 - 1);
|
||||
|
||||
using (Pen shadowPen = new Pen(Color.FromArgb(alpha, 0, 0, 0), 1))
|
||||
using (GraphicsPath shadowPath = CreateRoundedRectPath(shadowRect, CONTENT_RADIUS + i))
|
||||
{
|
||||
e.Graphics.DrawPath(shadowPen, shadowPath);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw content background
|
||||
Rectangle contentRect = new Rectangle(SHADOW_SIZE, SHADOW_SIZE, w - SHADOW_SIZE * 2, h - SHADOW_SIZE * 2);
|
||||
using (GraphicsPath contentPath = CreateRoundedRectPath(contentRect, CONTENT_RADIUS))
|
||||
{
|
||||
using (SolidBrush bgBrush = new SolidBrush(BackgroundColor))
|
||||
{
|
||||
e.Graphics.FillPath(bgBrush, contentPath);
|
||||
}
|
||||
|
||||
using (Pen borderPen = new Pen(BorderColor, 1f))
|
||||
{
|
||||
e.Graphics.DrawPath(borderPen, contentPath);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply rounded region
|
||||
using (GraphicsPath regionPath = CreateRoundedRectPath(new Rectangle(0, 0, w, h), CONTENT_RADIUS + SHADOW_SIZE))
|
||||
{
|
||||
this.Region = new Region(regionPath);
|
||||
}
|
||||
}
|
||||
|
||||
private GraphicsPath CreateRoundedRectPath(Rectangle rect, int radius)
|
||||
{
|
||||
GraphicsPath path = new GraphicsPath();
|
||||
if (radius <= 0)
|
||||
{
|
||||
path.AddRectangle(rect);
|
||||
return path;
|
||||
}
|
||||
|
||||
int diameter = Math.Min(radius * 2, Math.Min(rect.Width, rect.Height));
|
||||
radius = diameter / 2;
|
||||
Rectangle arcRect = new Rectangle(rect.Location, new Size(diameter, diameter));
|
||||
|
||||
path.AddArc(arcRect, 180, 90);
|
||||
arcRect.X = rect.Right - diameter;
|
||||
path.AddArc(arcRect, 270, 90);
|
||||
arcRect.Y = rect.Bottom - diameter;
|
||||
path.AddArc(arcRect, 0, 90);
|
||||
arcRect.X = rect.Left;
|
||||
path.AddArc(arcRect, 90, 90);
|
||||
path.CloseFigure();
|
||||
return path;
|
||||
}
|
||||
|
||||
public static string DonorsLocal = MainForm.donorApps;
|
||||
public static bool ifuploads = false;
|
||||
public static string newAppsForList = "";
|
||||
|
||||
|
||||
private void DonorsListViewForm_Load(object sender, EventArgs e)
|
||||
{
|
||||
MainForm.updatesNotified = true;
|
||||
if (MainForm.updates && MainForm.newapps)
|
||||
{
|
||||
bothdet.Visible = true;
|
||||
}
|
||||
else if (MainForm.updates && !MainForm.newapps)
|
||||
{
|
||||
upddet.Visible = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
newdet.Visible = true;
|
||||
}
|
||||
bothdet.Visible = MainForm.updates && MainForm.newapps;
|
||||
upddet.Visible = MainForm.updates && !MainForm.newapps;
|
||||
newdet.Visible = !MainForm.updates;
|
||||
|
||||
foreach (ListViewItem listItem in DonorsListView.Items)
|
||||
{
|
||||
if (listItem.SubItems[Donors.UpdateOrNew].Text.Contains("Update"))
|
||||
{
|
||||
listItem.BackColor = Color.FromArgb(0, 79, 97);
|
||||
}
|
||||
listItem.BackColor = UpdateHighlightColor;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private async void DonateButton_Click(object sender, EventArgs e)
|
||||
@@ -66,48 +219,36 @@ namespace AndroidSideloader
|
||||
bool uncheckednewapps = false;
|
||||
foreach (ListViewItem listItem in DonorsListView.Items)
|
||||
{
|
||||
if (!listItem.Checked)
|
||||
if (!listItem.Checked && listItem.SubItems[Donors.UpdateOrNew].Text.Contains("New"))
|
||||
{
|
||||
if (listItem.SubItems[Donors.UpdateOrNew].Text.Contains("New"))
|
||||
{
|
||||
uncheckednewapps = true;
|
||||
newAppsForList += listItem.SubItems[Donors.GameNameIndex].Text + ";" + listItem.SubItems[Donors.PackageNameIndex].Text + "\n";
|
||||
}
|
||||
uncheckednewapps = true;
|
||||
newAppsForList += listItem.SubItems[Donors.GameNameIndex].Text + ";" + listItem.SubItems[Donors.PackageNameIndex].Text + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (uncheckednewapps)
|
||||
{
|
||||
|
||||
NewApps NewAppForm = new NewApps();
|
||||
_ = NewAppForm.ShowDialog();
|
||||
new NewApps().ShowDialog();
|
||||
Hide();
|
||||
}
|
||||
else
|
||||
{
|
||||
Hide();
|
||||
}
|
||||
int count = DonorsListView.CheckedItems.Count;
|
||||
_ = new string[count];
|
||||
for (int i = 0; i < count; i++)
|
||||
|
||||
for (int i = 0; i < DonorsListView.CheckedItems.Count; i++)
|
||||
{
|
||||
ulong vcode = Convert.ToUInt64(DonorsListView.CheckedItems[i].SubItems[Donors.VersionCodeIndex].Text);
|
||||
if (DonorsListView.CheckedItems[i].SubItems[Donors.UpdateOrNew].Text.Contains("Update"))
|
||||
{
|
||||
await Program.form.extractAndPrepareGameToUploadAsync(DonorsListView.CheckedItems[i].SubItems[Donors.GameNameIndex].Text, DonorsListView.CheckedItems[i].SubItems[Donors.PackageNameIndex].Text, vcode, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
await Program.form.extractAndPrepareGameToUploadAsync(DonorsListView.CheckedItems[i].SubItems[Donors.GameNameIndex].Text, DonorsListView.CheckedItems[i].SubItems[Donors.PackageNameIndex].Text, vcode, false);
|
||||
}
|
||||
|
||||
bool isUpdate = DonorsListView.CheckedItems[i].SubItems[Donors.UpdateOrNew].Text.Contains("Update");
|
||||
await Program.form.extractAndPrepareGameToUploadAsync(
|
||||
DonorsListView.CheckedItems[i].SubItems[Donors.GameNameIndex].Text,
|
||||
DonorsListView.CheckedItems[i].SubItems[Donors.PackageNameIndex].Text,
|
||||
vcode, isUpdate);
|
||||
ifuploads = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (ifuploads)
|
||||
{
|
||||
MainForm.doUpload();
|
||||
}
|
||||
if (ifuploads) MainForm.doUpload();
|
||||
Close();
|
||||
}
|
||||
|
||||
@@ -115,49 +256,69 @@ namespace AndroidSideloader
|
||||
{
|
||||
SkipButton.Enabled = DonorsListView.CheckedItems.Count == 0;
|
||||
DonateButton.Enabled = !SkipButton.Enabled;
|
||||
skip_forever.Enabled = DonorsListView.CheckedItems.Count > 0;
|
||||
}
|
||||
|
||||
private void SkipButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
bool uncheckednewapps = false;
|
||||
foreach (ListViewItem listItem in DonorsListView.Items)
|
||||
{
|
||||
if (!listItem.Checked)
|
||||
{
|
||||
if (listItem.SubItems[Donors.UpdateOrNew].Text.Contains("New"))
|
||||
{
|
||||
uncheckednewapps = true;
|
||||
newAppsForList += listItem.SubItems[Donors.GameNameIndex].Text + ";" + listItem.SubItems[Donors.PackageNameIndex].Text + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (uncheckednewapps)
|
||||
{
|
||||
NewApps NewAppForm = new NewApps();
|
||||
_ = NewAppForm.ShowDialog();
|
||||
if (!listItem.Checked && listItem.SubItems[Donors.UpdateOrNew].Text.Contains("New"))
|
||||
newAppsForList += listItem.SubItems[Donors.GameNameIndex].Text + ";" + listItem.SubItems[Donors.PackageNameIndex].Text + "\n";
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(newAppsForList))
|
||||
new NewApps().ShowDialog();
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
private void DonorsListViewForm_MouseDown(object sender, MouseEventArgs e)
|
||||
{
|
||||
mouseDown = true;
|
||||
lastLocation = e.Location;
|
||||
}
|
||||
private void DonorsListViewForm_MouseDown(object sender, MouseEventArgs e) => TitleArea_MouseDown(sender, e);
|
||||
private void DonorsListViewForm_MouseMove(object sender, MouseEventArgs e) { }
|
||||
private void DonorsListViewForm_MouseUp(object sender, MouseEventArgs e) { }
|
||||
|
||||
private void DonorsListViewForm_MouseMove(object sender, MouseEventArgs e)
|
||||
private void skip_forever_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (mouseDown)
|
||||
var appsToBlacklist = DonorsListView.CheckedItems.Cast<ListViewItem>()
|
||||
.Select(item => item.SubItems[Donors.PackageNameIndex].Text).ToList();
|
||||
|
||||
if (appsToBlacklist.Count == 0)
|
||||
{
|
||||
Location = new Point(
|
||||
Location.X - lastLocation.X + e.X, Location.Y - lastLocation.Y + e.Y);
|
||||
Update();
|
||||
MessageBox.Show("No apps selected to blacklist.", "Info", MessageBoxButtons.OK);
|
||||
return;
|
||||
}
|
||||
|
||||
if (MessageBox.Show(
|
||||
$"Permanently skip donation requests for {appsToBlacklist.Count} app(s)?",
|
||||
"Confirm Blacklist", MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes)
|
||||
return;
|
||||
|
||||
string blacklistPath = Path.Combine(Environment.CurrentDirectory, "blacklist.json");
|
||||
try
|
||||
{
|
||||
var existingBlacklist = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
if (File.Exists(blacklistPath))
|
||||
{
|
||||
var jsonArray = Newtonsoft.Json.JsonConvert.DeserializeObject<string[]>(File.ReadAllText(blacklistPath));
|
||||
if (jsonArray != null)
|
||||
foreach (string entry in jsonArray.Where(ee => !string.IsNullOrWhiteSpace(ee)))
|
||||
existingBlacklist.Add(entry.Trim());
|
||||
}
|
||||
|
||||
foreach (string pkg in appsToBlacklist) existingBlacklist.Add(pkg);
|
||||
|
||||
File.WriteAllText(blacklistPath, Newtonsoft.Json.JsonConvert.SerializeObject(existingBlacklist.ToArray(), Newtonsoft.Json.Formatting.Indented));
|
||||
Logger.Log($"Added {appsToBlacklist.Count} apps to local blacklist");
|
||||
MessageBox.Show($"{appsToBlacklist.Count} {(appsToBlacklist.Count == 1 ? "app" : "apps")} added to blacklist.",
|
||||
"Success",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
Close();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Log($"Error saving blacklist: {ex.Message}", LogLevel.ERROR);
|
||||
MessageBox.Show($"Error: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void DonorsListViewForm_MouseUp(object sender, MouseEventArgs e)
|
||||
{
|
||||
mouseDown = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
6849
DonorsListView.resx
1773
GalleryView.cs
Normal file
1737
MainForm.Designer.cs
generated
8850
MainForm.cs
Normal file → Executable file
@@ -118,72 +118,72 @@
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<metadata name="speedLabel_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>798, 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, 56</value>
|
||||
<value>1165, 17</value>
|
||||
</metadata>
|
||||
<metadata name="startsideloadbutton_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>596, 17</value>
|
||||
<value>966, 17</value>
|
||||
</metadata>
|
||||
<metadata name="devicesbutton_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>539, 95</value>
|
||||
<value>705, 91</value>
|
||||
</metadata>
|
||||
<metadata name="obbcopybutton_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>359, 95</value>
|
||||
<value>526, 91</value>
|
||||
</metadata>
|
||||
<metadata name="backupadbbutton_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
<value>390, 17</value>
|
||||
</metadata>
|
||||
<metadata name="backupbutton_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>187, 95</value>
|
||||
<value>355, 91</value>
|
||||
</metadata>
|
||||
<metadata name="restorebutton_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 95</value>
|
||||
<value>187, 91</value>
|
||||
</metadata>
|
||||
<metadata name="getApkButton_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>1363, 56</value>
|
||||
<value>17, 91</value>
|
||||
</metadata>
|
||||
<metadata name="uninstallAppButton_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>1163, 56</value>
|
||||
<value>1157, 54</value>
|
||||
</metadata>
|
||||
<metadata name="pullAppToDesktopBtn_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>948, 56</value>
|
||||
<value>945, 54</value>
|
||||
</metadata>
|
||||
<metadata name="copyBulkObbButton_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>743, 56</value>
|
||||
<value>741, 54</value>
|
||||
</metadata>
|
||||
<metadata name="aboutBtn_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>711, 95</value>
|
||||
<value>876, 91</value>
|
||||
</metadata>
|
||||
<metadata name="settingsButton_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>569, 56</value>
|
||||
<value>568, 54</value>
|
||||
</metadata>
|
||||
<metadata name="QuestOptionsButton_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>221, 56</value>
|
||||
<value>223, 54</value>
|
||||
</metadata>
|
||||
<metadata name="btnOpenDownloads_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>392, 17</value>
|
||||
<value>764, 17</value>
|
||||
</metadata>
|
||||
<metadata name="btnRunAdbCmd_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>209, 17</value>
|
||||
<value>581, 17</value>
|
||||
</metadata>
|
||||
<metadata name="ADBWirelessDisable_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 56</value>
|
||||
</metadata>
|
||||
<metadata name="ADBWirelessEnable_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>1333, 17</value>
|
||||
<metadata name="ADBWirelessToggle_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
<metadata name="UpdateGamesButton_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>1124, 17</value>
|
||||
<value>17, 54</value>
|
||||
</metadata>
|
||||
<metadata name="listApkButton_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>955, 17</value>
|
||||
<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>855, 95</value>
|
||||
<value>1021, 91</value>
|
||||
</metadata>
|
||||
<metadata name="btnViewToggle_Tooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>215, 17</value>
|
||||
</metadata>
|
||||
<metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>161</value>
|
||||
<value>113</value>
|
||||
</metadata>
|
||||
</root>
|
||||
1308
ModernListView.cs
Normal file
439
ModernProgessBar.cs
Normal file
@@ -0,0 +1,439 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace AndroidSideloader
|
||||
{
|
||||
// A modern progress bar with rounded corners, left-to-right gradient fill,
|
||||
// animated indeterminate mode, and optional status text overlay
|
||||
[Description("Modern Themed Progress Bar")]
|
||||
public class ModernProgressBar : Control
|
||||
{
|
||||
#region Fields
|
||||
|
||||
private float _value;
|
||||
private float _minimum;
|
||||
private float _maximum = 100f;
|
||||
private int _radius = 8;
|
||||
private bool _isIndeterminate;
|
||||
private string _statusText = string.Empty;
|
||||
private string _operationType = string.Empty;
|
||||
|
||||
// Indeterminate animation
|
||||
private readonly Timer _animationTimer;
|
||||
private float _animationOffset;
|
||||
private const float AnimationSpeed = 4f;
|
||||
private const int IndeterminateBlockWidth = 80;
|
||||
|
||||
// Colors
|
||||
private Color _backgroundColor = Color.FromArgb(28, 32, 38);
|
||||
private Color _progressStartColor = Color.FromArgb(120, 220, 190); // lighter accent
|
||||
private Color _progressEndColor = Color.FromArgb(50, 160, 130); // darker accent
|
||||
private Color _indeterminateColor = Color.FromArgb(93, 203, 173); // accent
|
||||
private Color _textColor = Color.FromArgb(230, 230, 230);
|
||||
private Color _textShadowColor = Color.FromArgb(90, 0, 0, 0);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
public ModernProgressBar()
|
||||
{
|
||||
SetStyle(
|
||||
ControlStyles.AllPaintingInWmPaint |
|
||||
ControlStyles.OptimizedDoubleBuffer |
|
||||
ControlStyles.ResizeRedraw |
|
||||
ControlStyles.UserPaint |
|
||||
ControlStyles.SupportsTransparentBackColor,
|
||||
true);
|
||||
|
||||
BackColor = Color.Transparent;
|
||||
|
||||
// Size + Font
|
||||
Height = 28;
|
||||
Width = 220;
|
||||
Font = new Font("Segoe UI", 9f, FontStyle.Bold);
|
||||
|
||||
_animationTimer = new Timer { Interval = 16 }; // ~60fps
|
||||
_animationTimer.Tick += AnimationTimer_Tick;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
[Category("Progress")]
|
||||
[Description("The current value of the progress bar.")]
|
||||
public float Value
|
||||
{
|
||||
get => _value;
|
||||
set
|
||||
{
|
||||
_value = Math.Max(_minimum, Math.Min(_maximum, value));
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
[Category("Progress")]
|
||||
[Description("The minimum value of the progress bar.")]
|
||||
public float Minimum
|
||||
{
|
||||
get => _minimum;
|
||||
set
|
||||
{
|
||||
_minimum = value;
|
||||
if (_value < _minimum) _value = _minimum;
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
[Category("Progress")]
|
||||
[Description("The maximum value of the progress bar.")]
|
||||
public float Maximum
|
||||
{
|
||||
get => _maximum;
|
||||
set
|
||||
{
|
||||
_maximum = value;
|
||||
if (_value > _maximum) _value = _maximum;
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
[Category("Appearance")]
|
||||
[Description("The corner radius of the progress bar.")]
|
||||
public int Radius
|
||||
{
|
||||
get => _radius;
|
||||
set
|
||||
{
|
||||
_radius = Math.Max(0, value);
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
[Category("Progress")]
|
||||
[Description("Whether the progress bar shows indeterminate (marquee) progress.")]
|
||||
public bool IsIndeterminate
|
||||
{
|
||||
get => _isIndeterminate;
|
||||
set
|
||||
{
|
||||
// If there is no change, do nothing
|
||||
if (_isIndeterminate == value)
|
||||
return;
|
||||
|
||||
_isIndeterminate = value;
|
||||
if (_isIndeterminate)
|
||||
{
|
||||
_animationOffset = -IndeterminateBlockWidth;
|
||||
_animationTimer.Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
_animationTimer.Stop();
|
||||
}
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
[Category("Appearance")]
|
||||
[Description("Optional status text to display on the progress bar.")]
|
||||
public string StatusText
|
||||
{
|
||||
get => _statusText;
|
||||
set
|
||||
{
|
||||
_statusText = value ?? string.Empty;
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
[Category("Appearance")]
|
||||
[Description("Operation type label (e.g., 'Downloading', 'Installing').")]
|
||||
public string OperationType
|
||||
{
|
||||
get => _operationType;
|
||||
set
|
||||
{
|
||||
_operationType = value ?? string.Empty;
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
[Category("Appearance")]
|
||||
[Description("Background color of the progress bar track.")]
|
||||
public Color BackgroundColor
|
||||
{
|
||||
get => _backgroundColor;
|
||||
set { _backgroundColor = value; Invalidate(); }
|
||||
}
|
||||
|
||||
[Category("Appearance")]
|
||||
[Description("Start color of the progress gradient (left side).")]
|
||||
public Color ProgressStartColor
|
||||
{
|
||||
get => _progressStartColor;
|
||||
set { _progressStartColor = value; Invalidate(); }
|
||||
}
|
||||
|
||||
[Category("Appearance")]
|
||||
[Description("End color of the progress gradient (right side).")]
|
||||
public Color ProgressEndColor
|
||||
{
|
||||
get => _progressEndColor;
|
||||
set { _progressEndColor = value; Invalidate(); }
|
||||
}
|
||||
|
||||
[Category("Appearance")]
|
||||
[Description("Color used for indeterminate animation.")]
|
||||
public Color IndeterminateColor
|
||||
{
|
||||
get => _indeterminateColor;
|
||||
set { _indeterminateColor = value; Invalidate(); }
|
||||
}
|
||||
|
||||
[Category("Appearance")]
|
||||
[Description("Text color for status overlay.")]
|
||||
public Color TextColor
|
||||
{
|
||||
get => _textColor;
|
||||
set { _textColor = value; Invalidate(); }
|
||||
}
|
||||
|
||||
// Gets the progress as a percentage (0-100)
|
||||
public float ProgressPercent =>
|
||||
_maximum > _minimum ? (_value - _minimum) / (_maximum - _minimum) * 100f : 0f;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Painting
|
||||
|
||||
protected override void OnPaint(PaintEventArgs e)
|
||||
{
|
||||
var g = e.Graphics;
|
||||
g.SmoothingMode = SmoothingMode.AntiAlias;
|
||||
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
|
||||
g.Clear(BackColor);
|
||||
|
||||
int w = ClientSize.Width;
|
||||
int h = ClientSize.Height;
|
||||
if (w <= 0 || h <= 0) return;
|
||||
|
||||
var outerRect = new Rectangle(0, 0, w - 1, h - 1);
|
||||
|
||||
// Draw background track
|
||||
using (var path = CreateRoundedPath(outerRect, _radius))
|
||||
using (var bgBrush = new SolidBrush(_backgroundColor))
|
||||
{
|
||||
g.FillPath(bgBrush, path);
|
||||
}
|
||||
|
||||
// Draw progress or indeterminate animation
|
||||
if (_isIndeterminate)
|
||||
{
|
||||
DrawIndeterminate(g, outerRect);
|
||||
}
|
||||
else if (_value > _minimum)
|
||||
{
|
||||
DrawProgress(g, outerRect);
|
||||
}
|
||||
|
||||
// Draw text overlay
|
||||
DrawTextOverlay(g, outerRect);
|
||||
|
||||
base.OnPaint(e);
|
||||
}
|
||||
|
||||
private void DrawProgress(Graphics g, Rectangle outerRect)
|
||||
{
|
||||
float percent = (_maximum > _minimum)
|
||||
? (_value - _minimum) / (_maximum - _minimum)
|
||||
: 0f;
|
||||
|
||||
if (percent <= 0f) return;
|
||||
if (percent > 1f) percent = 1f;
|
||||
|
||||
int progressWidth = (int)Math.Round(outerRect.Width * percent);
|
||||
if (progressWidth <= 0) return;
|
||||
if (progressWidth > outerRect.Width) progressWidth = outerRect.Width;
|
||||
|
||||
using (var outerPath = CreateRoundedPath(outerRect, _radius))
|
||||
{
|
||||
// Clip to progress area inside rounded track
|
||||
Rectangle progressRect = new Rectangle(outerRect.X, outerRect.Y, progressWidth, outerRect.Height);
|
||||
using (var progressClip = new Region(progressRect))
|
||||
using (var trackRegion = new Region(outerPath))
|
||||
{
|
||||
trackRegion.Intersect(progressClip);
|
||||
|
||||
Region prevClip = g.Clip;
|
||||
try
|
||||
{
|
||||
g.SetClip(trackRegion, CombineMode.Replace);
|
||||
|
||||
// Left-to-right gradient, based on accent color
|
||||
using (var gradientBrush = new LinearGradientBrush(
|
||||
progressRect,
|
||||
_progressStartColor,
|
||||
_progressEndColor,
|
||||
LinearGradientMode.Horizontal))
|
||||
{
|
||||
g.FillPath(gradientBrush, outerPath);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
g.Clip = prevClip;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawIndeterminate(Graphics g, Rectangle outerRect)
|
||||
{
|
||||
using (var outerPath = CreateRoundedPath(outerRect, _radius))
|
||||
{
|
||||
Region prevClip = g.Clip;
|
||||
try
|
||||
{
|
||||
g.SetClip(outerPath, CombineMode.Replace);
|
||||
|
||||
int blockWidth = Math.Min(IndeterminateBlockWidth, outerRect.Width);
|
||||
int blockX = (int)_animationOffset;
|
||||
var blockRect = new Rectangle(blockX, outerRect.Y, blockWidth, outerRect.Height);
|
||||
|
||||
// Solid bar with slight left-to-right gradient
|
||||
using (var brush = new LinearGradientBrush(
|
||||
blockRect,
|
||||
ControlPaint.Light(_indeterminateColor, 0.1f),
|
||||
ControlPaint.Dark(_indeterminateColor, 0.1f),
|
||||
LinearGradientMode.Horizontal))
|
||||
{
|
||||
g.FillRectangle(brush, blockRect);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
g.Clip = prevClip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawTextOverlay(Graphics g, Rectangle outerRect)
|
||||
{
|
||||
string displayText = BuildDisplayText();
|
||||
if (string.IsNullOrEmpty(displayText)) return;
|
||||
|
||||
using (var sf = new StringFormat
|
||||
{
|
||||
Alignment = StringAlignment.Center,
|
||||
LineAlignment = StringAlignment.Center,
|
||||
Trimming = StringTrimming.EllipsisCharacter
|
||||
})
|
||||
{
|
||||
// Slight shadow for legibility on accent background
|
||||
var shadowRect = new Rectangle(outerRect.X + 1, outerRect.Y + 1, outerRect.Width, outerRect.Height);
|
||||
using (var shadowBrush = new SolidBrush(_textShadowColor))
|
||||
{
|
||||
g.DrawString(displayText, Font, shadowBrush, shadowRect, sf);
|
||||
}
|
||||
|
||||
using (var textBrush = new SolidBrush(_textColor))
|
||||
{
|
||||
g.DrawString(displayText, Font, textBrush, outerRect, sf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string BuildDisplayText()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_statusText))
|
||||
{
|
||||
return _statusText;
|
||||
}
|
||||
|
||||
if (_isIndeterminate && !string.IsNullOrEmpty(_operationType))
|
||||
{
|
||||
// E.g. "Downloading..."
|
||||
return _operationType + "...";
|
||||
}
|
||||
|
||||
if (!_isIndeterminate && _value > _minimum)
|
||||
{
|
||||
// Show one decimal place for sub-percent precision
|
||||
string percentText = $"{ProgressPercent:0.0}%";
|
||||
if (!string.IsNullOrEmpty(_operationType))
|
||||
{
|
||||
// E.g. "Downloading · 73.5%"
|
||||
return $"{_operationType} · {percentText}";
|
||||
}
|
||||
return percentText;
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
private GraphicsPath CreateRoundedPath(Rectangle rect, int radius)
|
||||
{
|
||||
var path = new GraphicsPath();
|
||||
|
||||
if (radius <= 0)
|
||||
{
|
||||
path.AddRectangle(rect);
|
||||
return path;
|
||||
}
|
||||
|
||||
int diameter = radius * 2;
|
||||
diameter = Math.Min(diameter, Math.Min(rect.Width, rect.Height));
|
||||
radius = diameter / 2;
|
||||
|
||||
var arcRect = new Rectangle(rect.Location, new Size(diameter, diameter));
|
||||
|
||||
path.AddArc(arcRect, 180, 90);
|
||||
arcRect.X = rect.Right - diameter;
|
||||
path.AddArc(arcRect, 270, 90);
|
||||
arcRect.Y = rect.Bottom - diameter;
|
||||
path.AddArc(arcRect, 0, 90);
|
||||
arcRect.X = rect.Left;
|
||||
path.AddArc(arcRect, 90, 90);
|
||||
|
||||
path.CloseFigure();
|
||||
return path;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Animation
|
||||
|
||||
private void AnimationTimer_Tick(object sender, EventArgs e)
|
||||
{
|
||||
_animationOffset += AnimationSpeed;
|
||||
|
||||
if (_animationOffset > ClientSize.Width + IndeterminateBlockWidth)
|
||||
{
|
||||
_animationOffset = -IndeterminateBlockWidth;
|
||||
}
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Cleanup
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_animationTimer?.Stop();
|
||||
_animationTimer?.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
511
ModernQueuePanel.cs
Normal file
@@ -0,0 +1,511 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace AndroidSideloader
|
||||
{
|
||||
// Modern download queue panel with drag-reorder, cancel buttons
|
||||
// and custom scrollbar with auto-scrolling during drag
|
||||
public sealed class ModernQueuePanel : Control
|
||||
{
|
||||
// Layout constants
|
||||
private const int ItemHeight = 28, ItemMargin = 4, ItemRadius = 5;
|
||||
private const int XButtonSize = 18, DragHandleWidth = 20, TextPadding = 6;
|
||||
private const int ScrollbarWidth = 6, ScrollbarWidthHover = 8, ScrollbarMargin = 2;
|
||||
private const int ScrollbarRadius = 3, MinThumbHeight = 20;
|
||||
private const int AutoScrollZoneHeight = 30, AutoScrollSpeed = 3;
|
||||
|
||||
// Color palette
|
||||
private static readonly Color BgColor = Color.FromArgb(24, 26, 30);
|
||||
private static readonly Color ItemBg = Color.FromArgb(32, 36, 44);
|
||||
private static readonly Color ItemHoverBg = Color.FromArgb(42, 46, 54);
|
||||
private static readonly Color ItemDragBg = Color.FromArgb(45, 55, 70);
|
||||
private static readonly Color TextColor = Color.FromArgb(210, 210, 210);
|
||||
private static readonly Color TextDimColor = Color.FromArgb(140, 140, 140);
|
||||
private static readonly Color AccentColor = Color.FromArgb(93, 203, 173);
|
||||
private static readonly Color XButtonBg = Color.FromArgb(55, 60, 70);
|
||||
private static readonly Color XButtonHoverBg = Color.FromArgb(200, 60, 60);
|
||||
private static readonly Color GripColor = Color.FromArgb(70, 75, 85);
|
||||
private static readonly Color ItemDragBorder = Color.FromArgb(55, 65, 80);
|
||||
private static readonly Color ScrollTrackColor = Color.FromArgb(35, 38, 45);
|
||||
private static readonly Color ScrollThumbColor = Color.FromArgb(70, 75, 85);
|
||||
private static readonly Color ScrollThumbHoverColor = Color.FromArgb(90, 95, 105);
|
||||
private static readonly Color ScrollThumbDragColor = Color.FromArgb(110, 115, 125);
|
||||
|
||||
private readonly List<string> _items = new List<string>();
|
||||
private readonly Timer _autoScrollTimer;
|
||||
|
||||
// State tracking
|
||||
private int _hoveredIndex = -1, _dragIndex = -1, _dropIndex = -1, _scrollOffset;
|
||||
private bool _hoveringX, _scrollbarHovered, _scrollbarDragging;
|
||||
private int _scrollDragStartY, _scrollDragStartOffset, _autoScrollDirection;
|
||||
private Rectangle _scrollThumbRect, _scrollTrackRect;
|
||||
|
||||
public event EventHandler<int> ItemRemoved;
|
||||
public event EventHandler<ReorderEventArgs> ItemReordered;
|
||||
|
||||
public ModernQueuePanel()
|
||||
{
|
||||
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |
|
||||
ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);
|
||||
BackColor = BgColor;
|
||||
_autoScrollTimer = new Timer { Interval = 16 }; // ~60 FPS
|
||||
_autoScrollTimer.Tick += (s, e) => HandleAutoScroll();
|
||||
}
|
||||
|
||||
public bool IsDownloading { get; set; }
|
||||
public int Count => _items.Count;
|
||||
private int ContentHeight => _items.Count * (ItemHeight + ItemMargin) + ItemMargin;
|
||||
private int MaxScroll => Math.Max(0, ContentHeight - Height);
|
||||
private bool ScrollbarVisible => ContentHeight > Height;
|
||||
|
||||
public void SetItems(IEnumerable<string> items)
|
||||
{
|
||||
_items.Clear();
|
||||
_items.AddRange(items);
|
||||
ResetState();
|
||||
}
|
||||
|
||||
private void ResetState()
|
||||
{
|
||||
_hoveredIndex = _dragIndex = -1;
|
||||
ClampScroll();
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
private void ClampScroll() =>
|
||||
_scrollOffset = Math.Max(0, Math.Min(MaxScroll, _scrollOffset));
|
||||
|
||||
// Auto-scroll when dragging near edges
|
||||
private void HandleAutoScroll()
|
||||
{
|
||||
if (_dragIndex < 0 || _autoScrollDirection == 0)
|
||||
{
|
||||
_autoScrollTimer.Stop();
|
||||
return;
|
||||
}
|
||||
|
||||
int oldOffset = _scrollOffset;
|
||||
_scrollOffset += _autoScrollDirection * AutoScrollSpeed;
|
||||
ClampScroll();
|
||||
|
||||
if (_scrollOffset != oldOffset)
|
||||
{
|
||||
UpdateDropIndex(PointToClient(MousePosition).Y);
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateAutoScroll(int mouseY)
|
||||
{
|
||||
if (_dragIndex < 0 || MaxScroll <= 0)
|
||||
{
|
||||
StopAutoScroll();
|
||||
return;
|
||||
}
|
||||
|
||||
_autoScrollDirection = mouseY < AutoScrollZoneHeight && _scrollOffset > 0 ? -1 :
|
||||
mouseY > Height - AutoScrollZoneHeight && _scrollOffset < MaxScroll ? 1 : 0;
|
||||
|
||||
if (_autoScrollDirection != 0 && !_autoScrollTimer.Enabled)
|
||||
_autoScrollTimer.Start();
|
||||
else if (_autoScrollDirection == 0)
|
||||
_autoScrollTimer.Stop();
|
||||
}
|
||||
|
||||
private void StopAutoScroll()
|
||||
{
|
||||
_autoScrollDirection = 0;
|
||||
_autoScrollTimer.Stop();
|
||||
}
|
||||
|
||||
private void UpdateDropIndex(int mouseY) =>
|
||||
_dropIndex = Math.Max(1, Math.Min(_items.Count, (mouseY + _scrollOffset + ItemHeight / 2) / (ItemHeight + ItemMargin)));
|
||||
|
||||
protected override void OnPaint(PaintEventArgs e)
|
||||
{
|
||||
var g = e.Graphics;
|
||||
g.SmoothingMode = SmoothingMode.AntiAlias;
|
||||
g.Clear(BgColor);
|
||||
|
||||
if (_items.Count == 0)
|
||||
{
|
||||
DrawEmptyState(g);
|
||||
return;
|
||||
}
|
||||
|
||||
// Draw visible items
|
||||
for (int i = 0; i < _items.Count; i++)
|
||||
{
|
||||
var rect = GetItemRect(i);
|
||||
if (rect.Bottom >= 0 && rect.Top <= Height)
|
||||
DrawItem(g, i, rect);
|
||||
}
|
||||
|
||||
// Draw drop indicator and scrollbar
|
||||
if (_dragIndex >= 0 && _dropIndex >= 0 && _dropIndex != _dragIndex)
|
||||
DrawDropIndicator(g);
|
||||
if (ScrollbarVisible)
|
||||
DrawScrollbar(g);
|
||||
}
|
||||
|
||||
private void DrawEmptyState(Graphics g)
|
||||
{
|
||||
using (var brush = new SolidBrush(TextDimColor))
|
||||
using (var font = new Font("Segoe UI", 8.5f, FontStyle.Italic))
|
||||
{
|
||||
var sf = new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center };
|
||||
g.DrawString("Queue is empty", font, brush, ClientRectangle, sf);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawItem(Graphics g, int index, Rectangle rect)
|
||||
{
|
||||
bool isFirst = index == 0;
|
||||
bool isDragging = index == _dragIndex;
|
||||
bool isHovered = !isDragging && index == _hoveredIndex;
|
||||
Color bg = isDragging ? ItemDragBg : isHovered ? ItemHoverBg : ItemBg;
|
||||
|
||||
// Draw item background
|
||||
using (var path = CreateRoundedRect(rect, ItemRadius))
|
||||
using (var brush = new SolidBrush(bg))
|
||||
g.FillPath(brush, path);
|
||||
|
||||
// Active download (first item) gets gradient accent and border
|
||||
if (isFirst)
|
||||
DrawFirstItemAccent(g, rect);
|
||||
// Dragged items get subtle highlight border
|
||||
else if (isDragging)
|
||||
DrawBorder(g, rect, ItemDragBorder, 0.5f);
|
||||
|
||||
// Draw drag handle, text, and close button
|
||||
if (!isFirst)
|
||||
DrawDragHandle(g, rect);
|
||||
DrawItemText(g, index, rect, isFirst);
|
||||
DrawXButton(g, rect, isHovered && _hoveringX);
|
||||
}
|
||||
|
||||
// Draw gradient accent and border for active download (first item)
|
||||
private void DrawFirstItemAccent(Graphics g, Rectangle rect)
|
||||
{
|
||||
using (var path = CreateRoundedRect(rect, ItemRadius))
|
||||
using (var gradBrush = new LinearGradientBrush(rect,
|
||||
Color.FromArgb(60, AccentColor), Color.FromArgb(0, AccentColor), LinearGradientMode.Horizontal))
|
||||
{
|
||||
var oldClip = g.Clip;
|
||||
g.SetClip(path);
|
||||
g.FillRectangle(gradBrush, rect);
|
||||
g.Clip = oldClip;
|
||||
}
|
||||
DrawBorder(g, rect, AccentColor, 1.5f);
|
||||
}
|
||||
|
||||
private void DrawBorder(Graphics g, Rectangle rect, Color color, float width)
|
||||
{
|
||||
using (var path = CreateRoundedRect(rect, ItemRadius))
|
||||
using (var pen = new Pen(color, width))
|
||||
g.DrawPath(pen, path);
|
||||
}
|
||||
|
||||
private void DrawDragHandle(Graphics g, Rectangle rect)
|
||||
{
|
||||
int cx = rect.X + 8, cy = rect.Y + rect.Height / 2;
|
||||
using (var brush = new SolidBrush(GripColor))
|
||||
{
|
||||
for (int row = -1; row <= 1; row++)
|
||||
for (int col = 0; col < 2; col++)
|
||||
g.FillEllipse(brush, cx + col * 4, cy + row * 4 - 1, 2, 2);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawItemText(Graphics g, int index, Rectangle rect, bool isFirst)
|
||||
{
|
||||
int textLeft = isFirst ? rect.X + TextPadding : rect.X + DragHandleWidth;
|
||||
int rightPad = ScrollbarVisible ? ScrollbarWidthHover + ScrollbarMargin * 2 : 0;
|
||||
var textRect = new Rectangle(textLeft, rect.Y, rect.Right - XButtonSize - 6 - textLeft - rightPad, rect.Height);
|
||||
|
||||
using (var brush = new SolidBrush(TextColor))
|
||||
using (var font = new Font("Segoe UI", isFirst ? 8.5f : 8f, isFirst ? FontStyle.Bold : FontStyle.Regular))
|
||||
{
|
||||
var sf = new StringFormat
|
||||
{
|
||||
Alignment = StringAlignment.Near,
|
||||
LineAlignment = StringAlignment.Center,
|
||||
Trimming = StringTrimming.EllipsisCharacter,
|
||||
FormatFlags = StringFormatFlags.NoWrap
|
||||
};
|
||||
g.DrawString(_items[index], font, brush, textRect, sf);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawXButton(Graphics g, Rectangle itemRect, bool hovered)
|
||||
{
|
||||
var xRect = GetXButtonRect(itemRect);
|
||||
using (var path = CreateRoundedRect(xRect, 3))
|
||||
using (var brush = new SolidBrush(hovered ? XButtonHoverBg : XButtonBg))
|
||||
g.FillPath(brush, path);
|
||||
|
||||
using (var pen = new Pen(Color.White, 1.4f) { StartCap = LineCap.Round, EndCap = LineCap.Round })
|
||||
{
|
||||
int p = 4;
|
||||
g.DrawLine(pen, xRect.X + p, xRect.Y + p, xRect.Right - p, xRect.Bottom - p);
|
||||
g.DrawLine(pen, xRect.Right - p, xRect.Y + p, xRect.X + p, xRect.Bottom - p);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawDropIndicator(Graphics g)
|
||||
{
|
||||
int y = (_dropIndex >= _items.Count ? _items.Count : _dropIndex) * (ItemHeight + ItemMargin) + ItemMargin / 2 - _scrollOffset;
|
||||
int left = ItemMargin + 2;
|
||||
int right = Width - ItemMargin - 2 - (ScrollbarVisible ? ScrollbarWidthHover + ScrollbarMargin : 0);
|
||||
|
||||
using (var pen = new Pen(AccentColor, 2.5f) { StartCap = LineCap.Round, EndCap = LineCap.Round })
|
||||
g.DrawLine(pen, left, y, right, y);
|
||||
}
|
||||
|
||||
// Draw custom scrollbar with hover expansion
|
||||
private void DrawScrollbar(Graphics g)
|
||||
{
|
||||
if (MaxScroll <= 0) return;
|
||||
|
||||
bool expanded = _scrollbarHovered || _scrollbarDragging;
|
||||
int sbWidth = expanded ? ScrollbarWidthHover : ScrollbarWidth;
|
||||
int trackX = Width - ScrollbarWidth - ScrollbarMargin - (expanded ? (ScrollbarWidthHover - ScrollbarWidth) / 2 : 0);
|
||||
|
||||
_scrollTrackRect = new Rectangle(trackX, ScrollbarMargin, sbWidth, Height - ScrollbarMargin * 2);
|
||||
|
||||
using (var trackBrush = new SolidBrush(Color.FromArgb(40, ScrollTrackColor)))
|
||||
using (var trackPath = CreateRoundedRect(_scrollTrackRect, ScrollbarRadius))
|
||||
g.FillPath(trackBrush, trackPath);
|
||||
|
||||
// Calculate thumb position and size
|
||||
int trackHeight = _scrollTrackRect.Height;
|
||||
int thumbHeight = Math.Max(MinThumbHeight, (int)(trackHeight * ((float)Height / ContentHeight)));
|
||||
float scrollRatio = MaxScroll > 0 ? (float)_scrollOffset / MaxScroll : 0;
|
||||
int thumbY = ScrollbarMargin + (int)((trackHeight - thumbHeight) * scrollRatio);
|
||||
|
||||
_scrollThumbRect = new Rectangle(trackX, thumbY, sbWidth, thumbHeight);
|
||||
Color thumbColor = _scrollbarDragging ? ScrollThumbDragColor : _scrollbarHovered ? ScrollThumbHoverColor : ScrollThumbColor;
|
||||
|
||||
using (var thumbBrush = new SolidBrush(thumbColor))
|
||||
using (var thumbPath = CreateRoundedRect(_scrollThumbRect, ScrollbarRadius))
|
||||
g.FillPath(thumbBrush, thumbPath);
|
||||
}
|
||||
|
||||
private Rectangle GetItemRect(int index)
|
||||
{
|
||||
int y = index * (ItemHeight + ItemMargin) + ItemMargin - _scrollOffset;
|
||||
int w = Width - ItemMargin * 2 - (ScrollbarVisible ? ScrollbarWidthHover + ScrollbarMargin + 2 : 0);
|
||||
return new Rectangle(ItemMargin, y, w, ItemHeight);
|
||||
}
|
||||
|
||||
private Rectangle GetXButtonRect(Rectangle itemRect) =>
|
||||
new Rectangle(itemRect.Right - XButtonSize - 3, itemRect.Y + (itemRect.Height - XButtonSize) / 2, XButtonSize, XButtonSize);
|
||||
|
||||
private int HitTest(Point pt)
|
||||
{
|
||||
for (int i = 0; i < _items.Count; i++)
|
||||
if (GetItemRect(i).Contains(pt)) return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
private bool HitTestScrollbar(Point pt) =>
|
||||
ScrollbarVisible && new Rectangle(_scrollTrackRect.X - 4, _scrollTrackRect.Y, _scrollTrackRect.Width + 8, _scrollTrackRect.Height).Contains(pt);
|
||||
|
||||
protected override void OnMouseMove(MouseEventArgs e)
|
||||
{
|
||||
base.OnMouseMove(e);
|
||||
|
||||
if (_scrollbarDragging)
|
||||
{
|
||||
HandleScrollbarDrag(e.Y);
|
||||
return;
|
||||
}
|
||||
|
||||
// Update scrollbar hover state
|
||||
bool wasHovered = _scrollbarHovered;
|
||||
_scrollbarHovered = HitTestScrollbar(e.Location);
|
||||
if (_scrollbarHovered != wasHovered) Invalidate();
|
||||
|
||||
if (_scrollbarHovered)
|
||||
{
|
||||
Cursor = Cursors.Default;
|
||||
_hoveredIndex = -1;
|
||||
_hoveringX = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle drag operation
|
||||
if (_dragIndex >= 0)
|
||||
{
|
||||
UpdateAutoScroll(e.Y);
|
||||
int newDrop = Math.Max(1, Math.Min(_items.Count, (e.Y + _scrollOffset + ItemHeight / 2) / (ItemHeight + ItemMargin)));
|
||||
if (newDrop != _dropIndex) { _dropIndex = newDrop; Invalidate(); }
|
||||
return;
|
||||
}
|
||||
|
||||
// Update hover state
|
||||
int hit = HitTest(e.Location);
|
||||
bool overX = hit >= 0 && GetXButtonRect(GetItemRect(hit)).Contains(e.Location);
|
||||
|
||||
if (hit != _hoveredIndex || overX != _hoveringX)
|
||||
{
|
||||
_hoveredIndex = hit;
|
||||
_hoveringX = overX;
|
||||
Cursor = overX ? Cursors.Hand : hit > 0 ? Cursors.SizeNS : Cursors.Default;
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleScrollbarDrag(int mouseY)
|
||||
{
|
||||
int trackHeight = Height - ScrollbarMargin * 2;
|
||||
int thumbHeight = Math.Max(MinThumbHeight, (int)(trackHeight * ((float)Height / ContentHeight)));
|
||||
int scrollableHeight = trackHeight - thumbHeight;
|
||||
|
||||
if (scrollableHeight > 0)
|
||||
{
|
||||
float scrollRatio = (float)(mouseY - _scrollDragStartY) / scrollableHeight;
|
||||
_scrollOffset = _scrollDragStartOffset + (int)(scrollRatio * MaxScroll);
|
||||
ClampScroll();
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnMouseDown(MouseEventArgs e)
|
||||
{
|
||||
base.OnMouseDown(e);
|
||||
if (e.Button != MouseButtons.Left) return;
|
||||
|
||||
// Handle scrollbar thumb drag
|
||||
if (ScrollbarVisible && _scrollThumbRect.Contains(e.Location))
|
||||
{
|
||||
_scrollbarDragging = true;
|
||||
_scrollDragStartY = e.Y;
|
||||
_scrollDragStartOffset = _scrollOffset;
|
||||
Capture = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle scrollbar track click
|
||||
if (ScrollbarVisible && HitTestScrollbar(e.Location))
|
||||
{
|
||||
_scrollOffset += e.Y < _scrollThumbRect.Top ? -Height : Height;
|
||||
ClampScroll();
|
||||
Invalidate();
|
||||
return;
|
||||
}
|
||||
|
||||
int hit = HitTest(e.Location);
|
||||
if (hit < 0) return;
|
||||
|
||||
// Handle close button click
|
||||
if (GetXButtonRect(GetItemRect(hit)).Contains(e.Location))
|
||||
{
|
||||
ItemRemoved?.Invoke(this, hit);
|
||||
return;
|
||||
}
|
||||
|
||||
// Start drag operation (only for non-first items)
|
||||
if (hit > 0)
|
||||
{
|
||||
_dragIndex = _dropIndex = hit;
|
||||
Capture = true;
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnMouseUp(MouseEventArgs e)
|
||||
{
|
||||
base.OnMouseUp(e);
|
||||
StopAutoScroll();
|
||||
|
||||
if (_scrollbarDragging)
|
||||
{
|
||||
_scrollbarDragging = false;
|
||||
Capture = false;
|
||||
Invalidate();
|
||||
return;
|
||||
}
|
||||
|
||||
// Complete drag reorder operation
|
||||
if (_dragIndex > 0 && _dropIndex > 0 && _dropIndex != _dragIndex)
|
||||
{
|
||||
int from = _dragIndex;
|
||||
int to = Math.Max(1, _dropIndex > _dragIndex ? _dropIndex - 1 : _dropIndex);
|
||||
var item = _items[from];
|
||||
_items.RemoveAt(from);
|
||||
_items.Insert(to, item);
|
||||
ItemReordered?.Invoke(this, new ReorderEventArgs(from, to));
|
||||
}
|
||||
|
||||
_dragIndex = _dropIndex = -1;
|
||||
Capture = false;
|
||||
Cursor = Cursors.Default;
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
protected override void OnMouseLeave(EventArgs e)
|
||||
{
|
||||
base.OnMouseLeave(e);
|
||||
if (_dragIndex < 0 && !_scrollbarDragging)
|
||||
{
|
||||
_hoveredIndex = -1;
|
||||
_hoveringX = _scrollbarHovered = false;
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnMouseWheel(MouseEventArgs e)
|
||||
{
|
||||
base.OnMouseWheel(e);
|
||||
if (MaxScroll <= 0) return;
|
||||
_scrollOffset -= e.Delta / 4;
|
||||
ClampScroll();
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
protected override void OnResize(EventArgs e)
|
||||
{
|
||||
base.OnResize(e);
|
||||
ClampScroll();
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_autoScrollTimer?.Stop();
|
||||
_autoScrollTimer?.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
private static GraphicsPath CreateRoundedRect(Rectangle rect, int radius)
|
||||
{
|
||||
var path = new GraphicsPath();
|
||||
if (radius <= 0 || rect.Width <= 0 || rect.Height <= 0)
|
||||
{
|
||||
path.AddRectangle(rect);
|
||||
return path;
|
||||
}
|
||||
int d = Math.Min(radius * 2, Math.Min(rect.Width, rect.Height));
|
||||
path.AddArc(rect.X, rect.Y, d, d, 180, 90);
|
||||
path.AddArc(rect.Right - d, rect.Y, d, d, 270, 90);
|
||||
path.AddArc(rect.Right - d, rect.Bottom - d, d, d, 0, 90);
|
||||
path.AddArc(rect.X, rect.Bottom - d, d, d, 90, 90);
|
||||
path.CloseFigure();
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
public class ReorderEventArgs : EventArgs
|
||||
{
|
||||
public int FromIndex { get; }
|
||||
public int ToIndex { get; }
|
||||
public ReorderEventArgs(int from, int to) { FromIndex = from; ToIndex = to; }
|
||||
}
|
||||
}
|
||||
176
NewApps.Designer.cs
generated
@@ -29,121 +29,159 @@ 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()));
|
||||
this.panel2 = new System.Windows.Forms.Panel();
|
||||
this.label2 = new System.Windows.Forms.Label();
|
||||
this.panel1 = new System.Windows.Forms.Panel();
|
||||
this.NewAppsButton = new AndroidSideloader.RoundButton();
|
||||
this.label2 = new System.Windows.Forms.Label();
|
||||
this.titleLabel = new System.Windows.Forms.Label();
|
||||
this.panel2 = new System.Windows.Forms.Panel();
|
||||
this.panel1.SuspendLayout();
|
||||
this.panel2.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
//
|
||||
// NewAppsListView
|
||||
//
|
||||
//
|
||||
this.NewAppsListView.AccessibleRole = System.Windows.Forms.AccessibleRole.None;
|
||||
this.NewAppsListView.BackColor = global::AndroidSideloader.Properties.Settings.Default.BackColor;
|
||||
this.NewAppsListView.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(28)))), ((int)(((byte)(32)))), ((int)(((byte)(38)))));
|
||||
this.NewAppsListView.BorderStyle = System.Windows.Forms.BorderStyle.None;
|
||||
this.NewAppsListView.CausesValidation = false;
|
||||
this.NewAppsListView.CheckBoxes = true;
|
||||
this.NewAppsListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
|
||||
this.GameNameIndex,
|
||||
this.PackageNameIndex});
|
||||
this.NewAppsListView.DataBindings.Add(new System.Windows.Forms.Binding("BackColor", global::AndroidSideloader.Properties.Settings.Default, "BackColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
|
||||
this.NewAppsListView.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.NewAppsListView.Font = new System.Drawing.Font("Segoe UI", 9.5F);
|
||||
this.NewAppsListView.ForeColor = System.Drawing.Color.White;
|
||||
this.NewAppsListView.FullRowSelect = true;
|
||||
this.NewAppsListView.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable;
|
||||
this.NewAppsListView.HideSelection = false;
|
||||
this.NewAppsListView.Location = new System.Drawing.Point(3, 5);
|
||||
this.NewAppsListView.Location = new System.Drawing.Point(1, 1);
|
||||
this.NewAppsListView.Name = "NewAppsListView";
|
||||
this.NewAppsListView.RightToLeftLayout = true;
|
||||
this.NewAppsListView.Size = new System.Drawing.Size(288, 167);
|
||||
this.NewAppsListView.Size = new System.Drawing.Size(298, 168);
|
||||
this.NewAppsListView.TabIndex = 1;
|
||||
this.NewAppsListView.UseCompatibleStateImageBehavior = false;
|
||||
this.NewAppsListView.View = System.Windows.Forms.View.Details;
|
||||
this.NewAppsListView.MouseDown += new System.Windows.Forms.MouseEventHandler(this.label2_MouseDown);
|
||||
this.NewAppsListView.MouseMove += new System.Windows.Forms.MouseEventHandler(this.label2_MouseMove);
|
||||
this.NewAppsListView.MouseUp += new System.Windows.Forms.MouseEventHandler(this.label2_MouseUp);
|
||||
//
|
||||
//
|
||||
// GameNameIndex
|
||||
//
|
||||
this.GameNameIndex.Text = "Game Name";
|
||||
this.GameNameIndex.Width = 284;
|
||||
//
|
||||
//
|
||||
this.GameNameIndex.Text = "App Name";
|
||||
this.GameNameIndex.Width = 280;
|
||||
//
|
||||
// PackageNameIndex
|
||||
//
|
||||
//
|
||||
this.PackageNameIndex.Width = 0;
|
||||
//
|
||||
// panel2
|
||||
//
|
||||
this.panel2.BackColor = global::AndroidSideloader.Properties.Settings.Default.SubButtonColor;
|
||||
this.panel2.Controls.Add(this.NewAppsListView);
|
||||
this.panel2.DataBindings.Add(new System.Windows.Forms.Binding("BackColor", global::AndroidSideloader.Properties.Settings.Default, "SubButtonColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
|
||||
this.panel2.Location = new System.Drawing.Point(9, 31);
|
||||
this.panel2.Name = "panel2";
|
||||
this.panel2.Size = new System.Drawing.Size(295, 175);
|
||||
this.panel2.TabIndex = 8;
|
||||
//
|
||||
//
|
||||
// panel1
|
||||
//
|
||||
this.panel1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(24)))), ((int)(((byte)(29)))));
|
||||
this.panel1.Controls.Add(this.NewAppsButton);
|
||||
this.panel1.Controls.Add(this.label2);
|
||||
this.panel1.Controls.Add(this.titleLabel);
|
||||
this.panel1.Controls.Add(this.panel2);
|
||||
this.panel1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.panel1.Location = new System.Drawing.Point(0, 0);
|
||||
this.panel1.Name = "panel1";
|
||||
this.panel1.Padding = new System.Windows.Forms.Padding(16);
|
||||
this.panel1.Size = new System.Drawing.Size(340, 300);
|
||||
this.panel1.TabIndex = 10;
|
||||
this.panel1.MouseDown += new System.Windows.Forms.MouseEventHandler(this.label2_MouseDown);
|
||||
this.panel1.MouseMove += new System.Windows.Forms.MouseEventHandler(this.label2_MouseMove);
|
||||
this.panel1.MouseUp += new System.Windows.Forms.MouseEventHandler(this.label2_MouseUp);
|
||||
//
|
||||
// NewAppsButton
|
||||
//
|
||||
this.NewAppsButton.Active1 = System.Drawing.Color.FromArgb(((int)(((byte)(50)))), ((int)(((byte)(140)))), ((int)(((byte)(115)))));
|
||||
this.NewAppsButton.Active2 = System.Drawing.Color.FromArgb(((int)(((byte)(45)))), ((int)(((byte)(125)))), ((int)(((byte)(105)))));
|
||||
this.NewAppsButton.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.NewAppsButton.BackColor = System.Drawing.Color.Transparent;
|
||||
this.NewAppsButton.DialogResult = System.Windows.Forms.DialogResult.OK;
|
||||
this.NewAppsButton.Disabled1 = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(35)))), ((int)(((byte)(45)))));
|
||||
this.NewAppsButton.Disabled2 = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(28)))), ((int)(((byte)(35)))));
|
||||
this.NewAppsButton.DisabledStrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(50)))), ((int)(((byte)(55)))), ((int)(((byte)(65)))));
|
||||
this.NewAppsButton.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Bold);
|
||||
this.NewAppsButton.ForeColor = System.Drawing.Color.White;
|
||||
this.NewAppsButton.Inactive1 = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(120)))), ((int)(((byte)(100)))));
|
||||
this.NewAppsButton.Inactive2 = System.Drawing.Color.FromArgb(((int)(((byte)(35)))), ((int)(((byte)(100)))), ((int)(((byte)(85)))));
|
||||
this.NewAppsButton.Location = new System.Drawing.Point(20, 252);
|
||||
this.NewAppsButton.Name = "NewAppsButton";
|
||||
this.NewAppsButton.Radius = 4;
|
||||
this.NewAppsButton.Size = new System.Drawing.Size(300, 36);
|
||||
this.NewAppsButton.Stroke = true;
|
||||
this.NewAppsButton.StrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(60)))), ((int)(((byte)(150)))), ((int)(((byte)(125)))));
|
||||
this.NewAppsButton.TabIndex = 2;
|
||||
this.NewAppsButton.Text = "Continue";
|
||||
this.NewAppsButton.Transparency = false;
|
||||
this.NewAppsButton.Click += new System.EventHandler(this.DonateButton_Click);
|
||||
//
|
||||
// label2
|
||||
//
|
||||
//
|
||||
this.label2.AutoSize = true;
|
||||
this.label2.BackColor = System.Drawing.Color.Transparent;
|
||||
this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 10.25F, System.Drawing.FontStyle.Bold);
|
||||
this.label2.ForeColor = System.Drawing.SystemColors.ControlLightLight;
|
||||
this.label2.Location = new System.Drawing.Point(28, 7);
|
||||
this.label2.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||
this.label2.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(160)))), ((int)(((byte)(165)))), ((int)(((byte)(175)))));
|
||||
this.label2.Location = new System.Drawing.Point(20, 42);
|
||||
this.label2.Name = "label2";
|
||||
this.label2.Size = new System.Drawing.Size(256, 17);
|
||||
this.label2.Size = new System.Drawing.Size(223, 15);
|
||||
this.label2.TabIndex = 9;
|
||||
this.label2.Text = "Check box of all free/non-VR apps";
|
||||
this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
||||
this.label2.Text = "Check the box for all free or non-VR apps";
|
||||
this.label2.MouseDown += new System.Windows.Forms.MouseEventHandler(this.label2_MouseDown);
|
||||
this.label2.MouseMove += new System.Windows.Forms.MouseEventHandler(this.label2_MouseMove);
|
||||
this.label2.MouseUp += new System.Windows.Forms.MouseEventHandler(this.label2_MouseUp);
|
||||
//
|
||||
// NewAppsButton
|
||||
//
|
||||
this.NewAppsButton.Active1 = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(40)))), ((int)(((byte)(40)))));
|
||||
this.NewAppsButton.Active2 = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(40)))), ((int)(((byte)(40)))));
|
||||
this.NewAppsButton.BackColor = System.Drawing.Color.Transparent;
|
||||
this.NewAppsButton.DialogResult = System.Windows.Forms.DialogResult.OK;
|
||||
this.NewAppsButton.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F);
|
||||
this.NewAppsButton.ForeColor = System.Drawing.Color.White;
|
||||
this.NewAppsButton.Inactive1 = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(25)))), ((int)(((byte)(25)))));
|
||||
this.NewAppsButton.Inactive2 = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(25)))), ((int)(((byte)(25)))));
|
||||
this.NewAppsButton.Location = new System.Drawing.Point(12, 212);
|
||||
this.NewAppsButton.Name = "NewAppsButton";
|
||||
this.NewAppsButton.Radius = 5;
|
||||
this.NewAppsButton.Size = new System.Drawing.Size(288, 29);
|
||||
this.NewAppsButton.Stroke = true;
|
||||
this.NewAppsButton.StrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(74)))), ((int)(((byte)(74)))), ((int)(((byte)(74)))));
|
||||
this.NewAppsButton.TabIndex = 2;
|
||||
this.NewAppsButton.Text = "Accept";
|
||||
this.NewAppsButton.Transparency = false;
|
||||
this.NewAppsButton.Click += new System.EventHandler(this.DonateButton_Click);
|
||||
//
|
||||
//
|
||||
// titleLabel
|
||||
//
|
||||
this.titleLabel.AutoSize = true;
|
||||
this.titleLabel.BackColor = System.Drawing.Color.Transparent;
|
||||
this.titleLabel.Font = new System.Drawing.Font("Segoe UI", 11F, System.Drawing.FontStyle.Bold);
|
||||
this.titleLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(93)))), ((int)(((byte)(203)))), ((int)(((byte)(173)))));
|
||||
this.titleLabel.Location = new System.Drawing.Point(20, 15);
|
||||
this.titleLabel.Name = "titleLabel";
|
||||
this.titleLabel.Size = new System.Drawing.Size(129, 20);
|
||||
this.titleLabel.TabIndex = 11;
|
||||
this.titleLabel.Text = "New Apps Found";
|
||||
this.titleLabel.MouseDown += new System.Windows.Forms.MouseEventHandler(this.label2_MouseDown);
|
||||
this.titleLabel.MouseMove += new System.Windows.Forms.MouseEventHandler(this.label2_MouseMove);
|
||||
this.titleLabel.MouseUp += new System.Windows.Forms.MouseEventHandler(this.label2_MouseUp);
|
||||
//
|
||||
// panel2
|
||||
//
|
||||
this.panel2.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.panel2.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(28)))), ((int)(((byte)(32)))), ((int)(((byte)(38)))));
|
||||
this.panel2.Controls.Add(this.NewAppsListView);
|
||||
this.panel2.Location = new System.Drawing.Point(20, 70);
|
||||
this.panel2.Name = "panel2";
|
||||
this.panel2.Padding = new System.Windows.Forms.Padding(1);
|
||||
this.panel2.Size = new System.Drawing.Size(300, 170);
|
||||
this.panel2.TabIndex = 8;
|
||||
//
|
||||
// NewApps
|
||||
//
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.BackColor = global::AndroidSideloader.Properties.Settings.Default.BackColor;
|
||||
this.BackgroundImage = global::AndroidSideloader.Properties.Resources.pattern_cubes;
|
||||
this.ClientSize = new System.Drawing.Size(313, 248);
|
||||
this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(24)))), ((int)(((byte)(29)))));
|
||||
this.ClientSize = new System.Drawing.Size(340, 300);
|
||||
this.ControlBox = false;
|
||||
this.Controls.Add(this.NewAppsButton);
|
||||
this.Controls.Add(this.label2);
|
||||
this.Controls.Add(this.panel2);
|
||||
this.DataBindings.Add(new System.Windows.Forms.Binding("BackColor", global::AndroidSideloader.Properties.Settings.Default, "BackColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Fixed3D;
|
||||
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
|
||||
this.Controls.Add(this.panel1);
|
||||
this.ForeColor = System.Drawing.Color.White;
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
|
||||
this.Name = "NewApps";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||
this.Load += new System.EventHandler(this.NewApps_Load);
|
||||
this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.label2_MouseDown);
|
||||
this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.label2_MouseMove);
|
||||
this.MouseUp += new System.Windows.Forms.MouseEventHandler(this.label2_MouseUp);
|
||||
this.panel1.ResumeLayout(false);
|
||||
this.panel1.PerformLayout();
|
||||
this.panel2.ResumeLayout(false);
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
@@ -151,9 +189,11 @@ namespace AndroidSideloader
|
||||
|
||||
private System.Windows.Forms.ListView NewAppsListView;
|
||||
private System.Windows.Forms.ColumnHeader GameNameIndex;
|
||||
private System.Windows.Forms.Panel panel1;
|
||||
private System.Windows.Forms.Panel panel2;
|
||||
private System.Windows.Forms.Label label2;
|
||||
private System.Windows.Forms.Label titleLabel;
|
||||
private System.Windows.Forms.ColumnHeader PackageNameIndex;
|
||||
private RoundButton NewAppsButton;
|
||||
}
|
||||
}
|
||||
}
|
||||
177
NewApps.cs
@@ -1,57 +1,184 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace AndroidSideloader
|
||||
{
|
||||
public partial class NewApps : Form
|
||||
{
|
||||
private bool mouseDown;
|
||||
private Point lastLocation;
|
||||
// Modern theme colors
|
||||
private static readonly Color BackgroundColor = Color.FromArgb(20, 24, 29);
|
||||
private static readonly Color BorderColor = Color.FromArgb(70, 80, 100);
|
||||
|
||||
// Shadow and corner settings
|
||||
private const int CS_DROPSHADOW = 0x00020000;
|
||||
private const int WM_NCLBUTTONDOWN = 0xA1;
|
||||
private const int HT_CAPTION = 0x2;
|
||||
private const int SHADOW_SIZE = 2;
|
||||
private const int CONTENT_RADIUS = 10;
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern bool ReleaseCapture();
|
||||
|
||||
protected override CreateParams CreateParams
|
||||
{
|
||||
get
|
||||
{
|
||||
CreateParams cp = base.CreateParams;
|
||||
cp.ClassStyle |= CS_DROPSHADOW;
|
||||
return cp;
|
||||
}
|
||||
}
|
||||
|
||||
public NewApps()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
// Use same icon as the executable
|
||||
this.Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath);
|
||||
|
||||
ApplyModernTheme();
|
||||
CenterToScreen();
|
||||
}
|
||||
|
||||
private void label2_MouseDown(object sender, MouseEventArgs e)
|
||||
private void ApplyModernTheme()
|
||||
{
|
||||
mouseDown = true;
|
||||
lastLocation = e.Location;
|
||||
}
|
||||
this.FormBorderStyle = FormBorderStyle.None;
|
||||
this.BackColor = Color.FromArgb(25, 25, 30);
|
||||
this.Padding = new Padding(5);
|
||||
|
||||
private void label2_MouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
if (mouseDown)
|
||||
panel1.BackColor = BackgroundColor;
|
||||
panel1.Location = new Point(6, 6);
|
||||
panel1.Size = new Size(this.ClientSize.Width - 12, this.ClientSize.Height - 12);
|
||||
panel1.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
|
||||
|
||||
this.Paint += Form_Paint;
|
||||
|
||||
// Close button
|
||||
var closeButton = new Button
|
||||
{
|
||||
Location = new Point(
|
||||
Location.X - lastLocation.X + e.X, Location.Y - lastLocation.Y + e.Y);
|
||||
Text = "✕",
|
||||
Font = new Font("Segoe UI", 9F),
|
||||
ForeColor = Color.White,
|
||||
BackColor = BackgroundColor,
|
||||
FlatStyle = FlatStyle.Flat,
|
||||
Size = new Size(30, 28),
|
||||
Location = new Point(panel1.Width - 35, 5),
|
||||
Cursor = Cursors.Hand,
|
||||
TabStop = false
|
||||
};
|
||||
closeButton.FlatAppearance.BorderSize = 0;
|
||||
closeButton.FlatAppearance.MouseOverBackColor = Color.FromArgb(200, 60, 60);
|
||||
closeButton.Click += (s, e) => Close();
|
||||
panel1.Controls.Add(closeButton);
|
||||
closeButton.BringToFront();
|
||||
|
||||
Update();
|
||||
// Enable dragging
|
||||
panel1.MouseDown += TitleArea_MouseDown;
|
||||
label2.MouseDown += TitleArea_MouseDown;
|
||||
titleLabel.MouseDown += TitleArea_MouseDown;
|
||||
}
|
||||
|
||||
private void TitleArea_MouseDown(object sender, MouseEventArgs e)
|
||||
{
|
||||
if (e.Button == MouseButtons.Left)
|
||||
{
|
||||
ReleaseCapture();
|
||||
SendMessage(this.Handle, WM_NCLBUTTONDOWN, (IntPtr)HT_CAPTION, IntPtr.Zero);
|
||||
}
|
||||
}
|
||||
|
||||
private void label2_MouseUp(object sender, MouseEventArgs e)
|
||||
private void Form_Paint(object sender, PaintEventArgs e)
|
||||
{
|
||||
mouseDown = false;
|
||||
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
|
||||
e.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
|
||||
|
||||
int w = this.Width;
|
||||
int h = this.Height;
|
||||
|
||||
// Draw shadow layers
|
||||
for (int i = SHADOW_SIZE; i >= 1; i--)
|
||||
{
|
||||
int alpha = (SHADOW_SIZE - i + 1) * 12;
|
||||
Rectangle shadowRect = new Rectangle(
|
||||
SHADOW_SIZE - i,
|
||||
SHADOW_SIZE - i,
|
||||
w - (SHADOW_SIZE - i) * 2 - 1,
|
||||
h - (SHADOW_SIZE - i) * 2 - 1);
|
||||
|
||||
using (Pen shadowPen = new Pen(Color.FromArgb(alpha, 0, 0, 0), 1))
|
||||
using (GraphicsPath shadowPath = CreateRoundedRectPath(shadowRect, CONTENT_RADIUS + i))
|
||||
{
|
||||
e.Graphics.DrawPath(shadowPen, shadowPath);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw content background
|
||||
Rectangle contentRect = new Rectangle(SHADOW_SIZE, SHADOW_SIZE, w - SHADOW_SIZE * 2, h - SHADOW_SIZE * 2);
|
||||
using (GraphicsPath contentPath = CreateRoundedRectPath(contentRect, CONTENT_RADIUS))
|
||||
{
|
||||
using (SolidBrush bgBrush = new SolidBrush(BackgroundColor))
|
||||
{
|
||||
e.Graphics.FillPath(bgBrush, contentPath);
|
||||
}
|
||||
|
||||
using (Pen borderPen = new Pen(BorderColor, 1f))
|
||||
{
|
||||
e.Graphics.DrawPath(borderPen, contentPath);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply rounded region
|
||||
using (GraphicsPath regionPath = CreateRoundedRectPath(new Rectangle(0, 0, w, h), CONTENT_RADIUS + SHADOW_SIZE))
|
||||
{
|
||||
this.Region = new Region(regionPath);
|
||||
}
|
||||
}
|
||||
|
||||
private GraphicsPath CreateRoundedRectPath(Rectangle rect, int radius)
|
||||
{
|
||||
GraphicsPath path = new GraphicsPath();
|
||||
if (radius <= 0)
|
||||
{
|
||||
path.AddRectangle(rect);
|
||||
return path;
|
||||
}
|
||||
|
||||
int diameter = Math.Min(radius * 2, Math.Min(rect.Width, rect.Height));
|
||||
radius = diameter / 2;
|
||||
Rectangle arcRect = new Rectangle(rect.Location, new Size(diameter, diameter));
|
||||
|
||||
path.AddArc(arcRect, 180, 90);
|
||||
arcRect.X = rect.Right - diameter;
|
||||
path.AddArc(arcRect, 270, 90);
|
||||
arcRect.Y = rect.Bottom - diameter;
|
||||
path.AddArc(arcRect, 0, 90);
|
||||
arcRect.X = rect.Left;
|
||||
path.AddArc(arcRect, 90, 90);
|
||||
path.CloseFigure();
|
||||
return path;
|
||||
}
|
||||
|
||||
private void label2_MouseDown(object sender, MouseEventArgs e) => TitleArea_MouseDown(sender, e);
|
||||
private void label2_MouseMove(object sender, MouseEventArgs e) { }
|
||||
private void label2_MouseUp(object sender, MouseEventArgs e) { }
|
||||
|
||||
private void DonateButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
string HWID = SideloaderUtilities.UUID();
|
||||
foreach (ListViewItem listItem in NewAppsListView.Items)
|
||||
{
|
||||
if (listItem.Checked)
|
||||
{
|
||||
Properties.Settings.Default.NonAppPackages += listItem.SubItems[Donors.PackageNameIndex].Text + ";" + HWID + "\n";
|
||||
Properties.Settings.Default.Save();
|
||||
}
|
||||
else
|
||||
{
|
||||
Properties.Settings.Default.AppPackages += listItem.SubItems[Donors.PackageNameIndex].Text + "\n";
|
||||
Properties.Settings.Default.Save();
|
||||
}
|
||||
Properties.Settings.Default.Save();
|
||||
}
|
||||
MainForm.newPackageUpload();
|
||||
Close();
|
||||
@@ -61,20 +188,16 @@ namespace AndroidSideloader
|
||||
{
|
||||
NewAppsListView.Items.Clear();
|
||||
Donors.initNewApps();
|
||||
List<ListViewItem> NewAppList = new List<ListViewItem>();
|
||||
var NewAppList = new List<ListViewItem>();
|
||||
foreach (string[] release in Donors.newApps)
|
||||
{
|
||||
ListViewItem NGame = new ListViewItem(release);
|
||||
if (!NewAppList.Contains(NGame))
|
||||
{
|
||||
NewAppList.Add(NGame);
|
||||
}
|
||||
}
|
||||
ListViewItem[] arr = NewAppList.ToArray();
|
||||
NewAppsListView.BeginUpdate();
|
||||
NewAppsListView.Items.Clear();
|
||||
NewAppsListView.Items.AddRange(arr);
|
||||
NewAppsListView.Items.AddRange(NewAppList.ToArray());
|
||||
NewAppsListView.EndUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
6849
NewApps.resx
70
Program.cs
@@ -8,7 +8,7 @@ namespace AndroidSideloader
|
||||
{
|
||||
internal static class Program
|
||||
{
|
||||
private static readonly SettingsManager settings = SettingsManager.Instance;
|
||||
private static SettingsManager settings;
|
||||
/// <summary>
|
||||
/// The main entry point for the application.
|
||||
/// </summary>
|
||||
@@ -16,7 +16,54 @@ namespace AndroidSideloader
|
||||
[SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.ControlAppDomain)]
|
||||
private static void Main()
|
||||
{
|
||||
// Handle corrupted user.config files
|
||||
bool configFixed = false;
|
||||
Exception configException = null;
|
||||
|
||||
try
|
||||
{
|
||||
// Force settings initialization to trigger any config errors early
|
||||
var test = AndroidSideloader.Properties.Settings.Default.FontStyle;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
configException = ex;
|
||||
// Delete the corrupted config file and retry
|
||||
try
|
||||
{
|
||||
string configPath = GetUserConfigPath();
|
||||
if (!string.IsNullOrEmpty(configPath) && File.Exists(configPath))
|
||||
{
|
||||
File.Delete(configPath);
|
||||
configFixed = true;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// If we can't delete it, try to continue anyway
|
||||
}
|
||||
}
|
||||
|
||||
if (configFixed)
|
||||
{
|
||||
// Restart the application after fixing config
|
||||
Application.Restart();
|
||||
return;
|
||||
}
|
||||
|
||||
if (configException != null)
|
||||
{
|
||||
MessageBox.Show(
|
||||
"Settings file is corrupted and could not be repaired automatically.\n\n" +
|
||||
"Please delete this folder and restart the application:\n" +
|
||||
Path.GetDirectoryName(GetUserConfigPath()),
|
||||
"Configuration Error",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
settings = SettingsManager.Instance;
|
||||
AppDomain currentDomain = AppDomain.CurrentDomain;
|
||||
currentDomain.UnhandledException += new UnhandledExceptionEventHandler(CrashHandler);
|
||||
Application.EnableVisualStyles();
|
||||
@@ -25,6 +72,23 @@ namespace AndroidSideloader
|
||||
Application.Run(form);
|
||||
//form.Show();
|
||||
}
|
||||
|
||||
private static string GetUserConfigPath()
|
||||
{
|
||||
try
|
||||
{
|
||||
string appData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
||||
string companyName = "Rookie.AndroidSideloader";
|
||||
string exeName = "AndroidSideloader.exe_Url_dkp0unsd4fjaabhwwafgfxvvbrerf10b";
|
||||
string version = "2.0.0.0";
|
||||
return Path.Combine(appData, companyName, exeName, version, "user.config");
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static MainForm form;
|
||||
|
||||
private static void CrashHandler(object sender, UnhandledExceptionEventArgs args)
|
||||
@@ -37,10 +101,10 @@ namespace AndroidSideloader
|
||||
string date_time = DateTime.Now.ToString("dddd, MMMM dd @ hh:mmtt (UTC)");
|
||||
File.WriteAllText(Sideloader.CrashLogPath, $"Date/Time of crash: {date_time}\nMessage: {e.Message}\nInner Message: {innerExceptionMessage}\nData: {e.Data}\nSource: {e.Source}\nTargetSite: {e.TargetSite}\nStack Trace: \n{e.StackTrace}\n\n\nDebuglog: \n\n\n");
|
||||
// If a debuglog exists we append it to the crashlog.
|
||||
if (File.Exists(settings.CurrentLogPath))
|
||||
if (settings != null && File.Exists(settings.CurrentLogPath))
|
||||
{
|
||||
File.AppendAllText(Sideloader.CrashLogPath, File.ReadAllText($"{settings.CurrentLogPath}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
72
Properties/Resources.Designer.cs
generated
@@ -19,7 +19,7 @@ namespace AndroidSideloader.Properties {
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "18.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
public class Resources {
|
||||
@@ -80,36 +80,6 @@ namespace AndroidSideloader.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
public static System.Drawing.Bitmap Pattern {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("Pattern", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
public static System.Drawing.Bitmap pattern_cubes {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("pattern_cubes", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
public static System.Drawing.Bitmap pattern_herringbone {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("pattern_herringbone", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
@@ -119,45 +89,5 @@ namespace AndroidSideloader.Properties {
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
public static System.Drawing.Bitmap splashimage {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("splashimage", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
public static System.Drawing.Bitmap splashimage_deps {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("splashimage_deps", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
public static System.Drawing.Bitmap splashimage_offline {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("splashimage_offline", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
public static System.Drawing.Bitmap splashimage_rclone {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("splashimage_rclone", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,25 +127,4 @@
|
||||
<data name="SearchGlass" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\SearchGlass.PNG;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="Pattern" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\Pattern.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="splashimage" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\splashimage.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="splashimage_deps" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\splashimage_deps.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="splashimage_rclone" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\splashimage_rclone.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="pattern_cubes" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\pattern_cubes.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="pattern_herringbone" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\pattern_herringbone.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="splashimage_offline" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\splashimage_offline.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
</root>
|
||||
10
Properties/Settings.Designer.cs
generated
@@ -12,7 +12,7 @@ namespace AndroidSideloader.Properties {
|
||||
|
||||
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.10.0.0")]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.14.0.0")]
|
||||
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
|
||||
|
||||
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||
@@ -109,7 +109,7 @@ namespace AndroidSideloader.Properties {
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("Microsoft Sans Serif, 11.25pt")]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("Microsoft Sans Serif, 10pt")]
|
||||
public global::System.Drawing.Font FontStyle {
|
||||
get {
|
||||
return ((global::System.Drawing.Font)(this["FontStyle"]));
|
||||
@@ -540,7 +540,7 @@ namespace AndroidSideloader.Properties {
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("25, 25, 25")]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("42, 45, 58")]
|
||||
public global::System.Drawing.Color SubButtonColor {
|
||||
get {
|
||||
return ((global::System.Drawing.Color)(this["SubButtonColor"]));
|
||||
@@ -564,7 +564,7 @@ namespace AndroidSideloader.Properties {
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("Black")]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("32, 35, 45")]
|
||||
public global::System.Drawing.Color ButtonColor {
|
||||
get {
|
||||
return ((global::System.Drawing.Color)(this["ButtonColor"]));
|
||||
@@ -576,7 +576,7 @@ namespace AndroidSideloader.Properties {
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("1, 1, 1")]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("31, 34, 42")]
|
||||
public global::System.Drawing.Color BackColor {
|
||||
get {
|
||||
return ((global::System.Drawing.Color)(this["BackColor"]));
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<Value Profile="(Default)">True</Value>
|
||||
</Setting>
|
||||
<Setting Name="FontStyle" Type="System.Drawing.Font" Scope="User">
|
||||
<Value Profile="(Default)">Microsoft Sans Serif, 11.25pt</Value>
|
||||
<Value Profile="(Default)">Microsoft Sans Serif, 10pt</Value>
|
||||
</Setting>
|
||||
<Setting Name="BackPicturePath" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)" />
|
||||
@@ -132,16 +132,16 @@
|
||||
<Value Profile="(Default)">25, 25, 25</Value>
|
||||
</Setting>
|
||||
<Setting Name="SubButtonColor" Type="System.Drawing.Color" Scope="User">
|
||||
<Value Profile="(Default)">25, 25, 25</Value>
|
||||
<Value Profile="(Default)">42, 45, 58</Value>
|
||||
</Setting>
|
||||
<Setting Name="TextBoxColor" Type="System.Drawing.Color" Scope="User">
|
||||
<Value Profile="(Default)">25, 25, 25</Value>
|
||||
</Setting>
|
||||
<Setting Name="ButtonColor" Type="System.Drawing.Color" Scope="User">
|
||||
<Value Profile="(Default)">Black</Value>
|
||||
<Value Profile="(Default)">32, 35, 45</Value>
|
||||
</Setting>
|
||||
<Setting Name="BackColor" Type="System.Drawing.Color" Scope="User">
|
||||
<Value Profile="(Default)">1, 1, 1</Value>
|
||||
<Value Profile="(Default)">31, 34, 42</Value>
|
||||
</Setting>
|
||||
<Setting Name="AppPackages" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)" />
|
||||
@@ -182,5 +182,17 @@
|
||||
<Setting Name="bandwidthLimit" Type="System.Single" Scope="User">
|
||||
<Value Profile="(Default)">0</Value>
|
||||
</Setting>
|
||||
<Setting Name="useProxy" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">False</Value>
|
||||
</Setting>
|
||||
<Setting Name="proxyAddress" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)" />
|
||||
</Setting>
|
||||
<Setting Name="proxyPort" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)" />
|
||||
</Setting>
|
||||
<Setting Name="selectedMirror" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)" />
|
||||
</Setting>
|
||||
</Settings>
|
||||
</SettingsFile>
|
||||
778
QuestForm.Designer.cs
generated
@@ -7,8 +7,6 @@ namespace AndroidSideloader
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
///
|
||||
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
@@ -32,380 +30,424 @@ namespace AndroidSideloader
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(QuestForm));
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.label2 = new System.Windows.Forms.Label();
|
||||
this.label11 = new System.Windows.Forms.Label();
|
||||
this.label12 = new System.Windows.Forms.Label();
|
||||
this.DeleteShots = new System.Windows.Forms.CheckBox();
|
||||
this.splitter1 = new System.Windows.Forms.Splitter();
|
||||
this.label10 = new System.Windows.Forms.Label();
|
||||
this.label14 = new System.Windows.Forms.Label();
|
||||
this.label16 = new System.Windows.Forms.Label();
|
||||
this.CPUComboBox = new System.Windows.Forms.ComboBox();
|
||||
this.GPUComboBox = new System.Windows.Forms.ComboBox();
|
||||
this.ResolutionLabel = new System.Windows.Forms.Label();
|
||||
this.lblUsernameSection = new System.Windows.Forms.Label();
|
||||
this.lblMediaSection = new System.Windows.Forms.Label();
|
||||
this.lblPerformanceSection = new System.Windows.Forms.Label();
|
||||
this.GlobalUsername = new System.Windows.Forms.TextBox();
|
||||
this.TextureResTextBox = new System.Windows.Forms.TextBox();
|
||||
this.RefreshRateComboBox = new System.Windows.Forms.ComboBox();
|
||||
this.btnApplyTempSettings = new AndroidSideloader.RoundButton();
|
||||
this.questVids = new AndroidSideloader.RoundButton();
|
||||
this.questPics = new AndroidSideloader.RoundButton();
|
||||
this.btnApplyUsername = new AndroidSideloader.RoundButton();
|
||||
this.label3 = new System.Windows.Forms.Label();
|
||||
this.questPics = new AndroidSideloader.RoundButton();
|
||||
this.questVids = new AndroidSideloader.RoundButton();
|
||||
this.lblScreenshotsPath = new System.Windows.Forms.Label();
|
||||
this.lblRecordingsPath = new System.Windows.Forms.Label();
|
||||
this.toggleDeleteAfterTransfer = new AndroidSideloader.ToggleSwitch();
|
||||
this.lblDeleteAfterTransfer = new System.Windows.Forms.Label();
|
||||
this.lblPerformanceNote = new System.Windows.Forms.Label();
|
||||
this.lblRefreshRate = new System.Windows.Forms.Label();
|
||||
this.RefreshRateComboBox = new System.Windows.Forms.ComboBox();
|
||||
this.lblGpuLevel = new System.Windows.Forms.Label();
|
||||
this.GPUComboBox = new System.Windows.Forms.ComboBox();
|
||||
this.lblCpuLevel = new System.Windows.Forms.Label();
|
||||
this.CPUComboBox = new System.Windows.Forms.ComboBox();
|
||||
this.lblResolution = new System.Windows.Forms.Label();
|
||||
this.TextureResTextBox = new System.Windows.Forms.TextBox();
|
||||
this.btnApplyTempSettings = new AndroidSideloader.RoundButton();
|
||||
this.separator1 = new System.Windows.Forms.Panel();
|
||||
this.separator2 = new System.Windows.Forms.Panel();
|
||||
this.btnClose = new AndroidSideloader.RoundButton();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// label1
|
||||
// lblUsernameSection
|
||||
//
|
||||
this.label1.AutoSize = true;
|
||||
this.label1.BackColor = System.Drawing.Color.Transparent;
|
||||
this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.label1.ForeColor = System.Drawing.Color.LightCyan;
|
||||
this.label1.Location = new System.Drawing.Point(72, 276);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Size = new System.Drawing.Size(165, 20);
|
||||
this.label1.TabIndex = 6;
|
||||
this.label1.Text = "Temporary Settings";
|
||||
this.label1.TextAlign = System.Drawing.ContentAlignment.TopCenter;
|
||||
this.lblUsernameSection.AutoSize = true;
|
||||
this.lblUsernameSection.Font = new System.Drawing.Font("Segoe UI", 11F, System.Drawing.FontStyle.Bold);
|
||||
this.lblUsernameSection.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(93)))), ((int)(((byte)(203)))), ((int)(((byte)(173)))));
|
||||
this.lblUsernameSection.Location = new System.Drawing.Point(20, 15);
|
||||
this.lblUsernameSection.Name = "lblUsernameSection";
|
||||
this.lblUsernameSection.Size = new System.Drawing.Size(80, 20);
|
||||
this.lblUsernameSection.TabIndex = 0;
|
||||
this.lblUsernameSection.Text = "Username";
|
||||
//
|
||||
// label2
|
||||
// lblMediaSection
|
||||
//
|
||||
this.label2.AutoSize = true;
|
||||
this.label2.BackColor = System.Drawing.Color.Transparent;
|
||||
this.label2.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
|
||||
this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.label2.ForeColor = System.Drawing.Color.LightSteelBlue;
|
||||
this.label2.Location = new System.Drawing.Point(83, 303);
|
||||
this.label2.Name = "label2";
|
||||
this.label2.Size = new System.Drawing.Size(143, 16);
|
||||
this.label2.TabIndex = 6;
|
||||
this.label2.Text = "Reboot Quest to Reset";
|
||||
this.label2.TextAlign = System.Drawing.ContentAlignment.TopCenter;
|
||||
this.lblMediaSection.AutoSize = true;
|
||||
this.lblMediaSection.Font = new System.Drawing.Font("Segoe UI", 11F, System.Drawing.FontStyle.Bold);
|
||||
this.lblMediaSection.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(93)))), ((int)(((byte)(203)))), ((int)(((byte)(173)))));
|
||||
this.lblMediaSection.Location = new System.Drawing.Point(20, 97);
|
||||
this.lblMediaSection.Name = "lblMediaSection";
|
||||
this.lblMediaSection.Size = new System.Drawing.Size(114, 20);
|
||||
this.lblMediaSection.TabIndex = 3;
|
||||
this.lblMediaSection.Text = "Media Transfer";
|
||||
//
|
||||
// label11
|
||||
// lblPerformanceSection
|
||||
//
|
||||
this.label11.AutoSize = true;
|
||||
this.label11.BackColor = System.Drawing.Color.Transparent;
|
||||
this.label11.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
|
||||
this.label11.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.label11.ForeColor = System.Drawing.Color.LightCyan;
|
||||
this.label11.Location = new System.Drawing.Point(18, 94);
|
||||
this.label11.Name = "label11";
|
||||
this.label11.Size = new System.Drawing.Size(272, 20);
|
||||
this.label11.TabIndex = 6;
|
||||
this.label11.Text = "Transfer screenshots to Desktop";
|
||||
this.label11.TextAlign = System.Drawing.ContentAlignment.TopCenter;
|
||||
//
|
||||
// label12
|
||||
//
|
||||
this.label12.AutoSize = true;
|
||||
this.label12.BackColor = System.Drawing.Color.Transparent;
|
||||
this.label12.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
|
||||
this.label12.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.label12.ForeColor = System.Drawing.Color.LightCyan;
|
||||
this.label12.Location = new System.Drawing.Point(92, 9);
|
||||
this.label12.Name = "label12";
|
||||
this.label12.Size = new System.Drawing.Size(124, 20);
|
||||
this.label12.TabIndex = 6;
|
||||
this.label12.Text = "Set Username";
|
||||
this.label12.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
||||
//
|
||||
// DeleteShots
|
||||
//
|
||||
this.DeleteShots.AutoSize = true;
|
||||
this.DeleteShots.BackColor = System.Drawing.Color.Transparent;
|
||||
this.DeleteShots.CheckAlign = System.Drawing.ContentAlignment.MiddleRight;
|
||||
this.DeleteShots.Cursor = System.Windows.Forms.Cursors.Default;
|
||||
this.DeleteShots.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
|
||||
this.DeleteShots.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F);
|
||||
this.DeleteShots.ForeColor = System.Drawing.Color.LightSkyBlue;
|
||||
this.DeleteShots.Location = new System.Drawing.Point(33, 232);
|
||||
this.DeleteShots.Name = "DeleteShots";
|
||||
this.DeleteShots.Size = new System.Drawing.Size(242, 21);
|
||||
this.DeleteShots.TabIndex = 7;
|
||||
this.DeleteShots.Text = "Delete files on Quest after transfer";
|
||||
this.DeleteShots.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
||||
this.DeleteShots.UseVisualStyleBackColor = false;
|
||||
this.DeleteShots.CheckedChanged += new System.EventHandler(this.DeleteShots_CheckedChanged);
|
||||
//
|
||||
// splitter1
|
||||
//
|
||||
this.splitter1.Location = new System.Drawing.Point(0, 0);
|
||||
this.splitter1.Name = "splitter1";
|
||||
this.splitter1.Size = new System.Drawing.Size(3, 486);
|
||||
this.splitter1.TabIndex = 14;
|
||||
this.splitter1.TabStop = false;
|
||||
//
|
||||
// label10
|
||||
//
|
||||
this.label10.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(20)))), ((int)(((byte)(20)))));
|
||||
this.label10.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
|
||||
this.label10.Location = new System.Drawing.Point(-4, 261);
|
||||
this.label10.Name = "label10";
|
||||
this.label10.Size = new System.Drawing.Size(394, 3);
|
||||
this.label10.TabIndex = 15;
|
||||
//
|
||||
// label14
|
||||
//
|
||||
this.label14.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(20)))), ((int)(((byte)(20)))));
|
||||
this.label14.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
|
||||
this.label14.Location = new System.Drawing.Point(-4, 80);
|
||||
this.label14.Name = "label14";
|
||||
this.label14.Size = new System.Drawing.Size(394, 3);
|
||||
this.label14.TabIndex = 17;
|
||||
//
|
||||
// label16
|
||||
//
|
||||
this.label16.AutoSize = true;
|
||||
this.label16.BackColor = System.Drawing.Color.Transparent;
|
||||
this.label16.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
|
||||
this.label16.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.label16.ForeColor = System.Drawing.Color.LightSteelBlue;
|
||||
this.label16.Location = new System.Drawing.Point(34, 153);
|
||||
this.label16.Name = "label16";
|
||||
this.label16.Size = new System.Drawing.Size(240, 16);
|
||||
this.label16.TabIndex = 6;
|
||||
this.label16.Text = "Exports to: Desktop\\Quest Screenshots";
|
||||
this.label16.TextAlign = System.Drawing.ContentAlignment.TopCenter;
|
||||
//
|
||||
// CPUComboBox
|
||||
//
|
||||
this.CPUComboBox.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(25)))), ((int)(((byte)(25)))));
|
||||
this.CPUComboBox.DataBindings.Add(new System.Windows.Forms.Binding("Font", global::AndroidSideloader.Properties.Settings.Default, "FontStyle", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
|
||||
this.CPUComboBox.DataBindings.Add(new System.Windows.Forms.Binding("ForeColor", global::AndroidSideloader.Properties.Settings.Default, "FontColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
|
||||
this.CPUComboBox.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
|
||||
this.CPUComboBox.Font = global::AndroidSideloader.Properties.Settings.Default.FontStyle;
|
||||
this.CPUComboBox.ForeColor = global::AndroidSideloader.Properties.Settings.Default.FontColor;
|
||||
this.CPUComboBox.FormattingEnabled = true;
|
||||
this.CPUComboBox.Items.AddRange(new object[] {
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4"});
|
||||
this.CPUComboBox.Location = new System.Drawing.Point(38, 383);
|
||||
this.CPUComboBox.Name = "CPUComboBox";
|
||||
this.CPUComboBox.Size = new System.Drawing.Size(232, 26);
|
||||
this.CPUComboBox.TabIndex = 2;
|
||||
this.CPUComboBox.Text = "Select CPU level (0 for default)";
|
||||
//
|
||||
// GPUComboBox
|
||||
//
|
||||
this.GPUComboBox.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(25)))), ((int)(((byte)(25)))));
|
||||
this.GPUComboBox.DataBindings.Add(new System.Windows.Forms.Binding("Font", global::AndroidSideloader.Properties.Settings.Default, "FontStyle", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
|
||||
this.GPUComboBox.DataBindings.Add(new System.Windows.Forms.Binding("ForeColor", global::AndroidSideloader.Properties.Settings.Default, "FontColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
|
||||
this.GPUComboBox.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
|
||||
this.GPUComboBox.Font = global::AndroidSideloader.Properties.Settings.Default.FontStyle;
|
||||
this.GPUComboBox.ForeColor = global::AndroidSideloader.Properties.Settings.Default.FontColor;
|
||||
this.GPUComboBox.FormattingEnabled = true;
|
||||
this.GPUComboBox.Items.AddRange(new object[] {
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4"});
|
||||
this.GPUComboBox.Location = new System.Drawing.Point(38, 354);
|
||||
this.GPUComboBox.Name = "GPUComboBox";
|
||||
this.GPUComboBox.Size = new System.Drawing.Size(232, 26);
|
||||
this.GPUComboBox.TabIndex = 1;
|
||||
this.GPUComboBox.Text = "Select GPU level (0 for default)";
|
||||
//
|
||||
// ResolutionLabel
|
||||
//
|
||||
this.ResolutionLabel.AutoSize = true;
|
||||
this.ResolutionLabel.BackColor = System.Drawing.Color.Transparent;
|
||||
this.ResolutionLabel.DataBindings.Add(new System.Windows.Forms.Binding("ForeColor", global::AndroidSideloader.Properties.Settings.Default, "FontColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
|
||||
this.ResolutionLabel.DataBindings.Add(new System.Windows.Forms.Binding("Font", global::AndroidSideloader.Properties.Settings.Default, "FontStyle", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
|
||||
this.ResolutionLabel.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
|
||||
this.ResolutionLabel.Font = global::AndroidSideloader.Properties.Settings.Default.FontStyle;
|
||||
this.ResolutionLabel.ForeColor = global::AndroidSideloader.Properties.Settings.Default.FontColor;
|
||||
this.ResolutionLabel.Location = new System.Drawing.Point(38, 413);
|
||||
this.ResolutionLabel.Name = "ResolutionLabel";
|
||||
this.ResolutionLabel.Size = new System.Drawing.Size(153, 18);
|
||||
this.ResolutionLabel.TabIndex = 3;
|
||||
this.ResolutionLabel.Text = "Resolution (0=default)";
|
||||
this.lblPerformanceSection.AutoSize = true;
|
||||
this.lblPerformanceSection.Font = new System.Drawing.Font("Segoe UI", 11F, System.Drawing.FontStyle.Bold);
|
||||
this.lblPerformanceSection.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(93)))), ((int)(((byte)(203)))), ((int)(((byte)(173)))));
|
||||
this.lblPerformanceSection.Location = new System.Drawing.Point(20, 232);
|
||||
this.lblPerformanceSection.Name = "lblPerformanceSection";
|
||||
this.lblPerformanceSection.Size = new System.Drawing.Size(147, 20);
|
||||
this.lblPerformanceSection.TabIndex = 9;
|
||||
this.lblPerformanceSection.Text = "Temporary Settings";
|
||||
//
|
||||
// GlobalUsername
|
||||
//
|
||||
this.GlobalUsername.BackColor = global::AndroidSideloader.Properties.Settings.Default.TextBoxColor;
|
||||
this.GlobalUsername.DataBindings.Add(new System.Windows.Forms.Binding("BackColor", global::AndroidSideloader.Properties.Settings.Default, "TextBoxColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
|
||||
this.GlobalUsername.DataBindings.Add(new System.Windows.Forms.Binding("Font", global::AndroidSideloader.Properties.Settings.Default, "FontStyle", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
|
||||
this.GlobalUsername.DataBindings.Add(new System.Windows.Forms.Binding("ForeColor", global::AndroidSideloader.Properties.Settings.Default, "FontColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
|
||||
this.GlobalUsername.Font = global::AndroidSideloader.Properties.Settings.Default.FontStyle;
|
||||
this.GlobalUsername.ForeColor = global::AndroidSideloader.Properties.Settings.Default.FontColor;
|
||||
this.GlobalUsername.Location = new System.Drawing.Point(33, 39);
|
||||
this.GlobalUsername.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(44)))), ((int)(((byte)(52)))));
|
||||
this.GlobalUsername.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||||
this.GlobalUsername.Font = new System.Drawing.Font("Segoe UI", 9.5F);
|
||||
this.GlobalUsername.ForeColor = System.Drawing.Color.White;
|
||||
this.GlobalUsername.Location = new System.Drawing.Point(24, 45);
|
||||
this.GlobalUsername.Name = "GlobalUsername";
|
||||
this.GlobalUsername.Size = new System.Drawing.Size(142, 24);
|
||||
this.GlobalUsername.TabIndex = 12;
|
||||
this.GlobalUsername.Size = new System.Drawing.Size(200, 24);
|
||||
this.GlobalUsername.TabIndex = 1;
|
||||
this.GlobalUsername.TextChanged += new System.EventHandler(this.GlobalUsername_TextChanged);
|
||||
//
|
||||
// TextureResTextBox
|
||||
//
|
||||
this.TextureResTextBox.BackColor = global::AndroidSideloader.Properties.Settings.Default.TextBoxColor;
|
||||
this.TextureResTextBox.DataBindings.Add(new System.Windows.Forms.Binding("BackColor", global::AndroidSideloader.Properties.Settings.Default, "TextBoxColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
|
||||
this.TextureResTextBox.DataBindings.Add(new System.Windows.Forms.Binding("Font", global::AndroidSideloader.Properties.Settings.Default, "FontStyle", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
|
||||
this.TextureResTextBox.DataBindings.Add(new System.Windows.Forms.Binding("ForeColor", global::AndroidSideloader.Properties.Settings.Default, "FontColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
|
||||
this.TextureResTextBox.Font = global::AndroidSideloader.Properties.Settings.Default.FontStyle;
|
||||
this.TextureResTextBox.ForeColor = global::AndroidSideloader.Properties.Settings.Default.FontColor;
|
||||
this.TextureResTextBox.Location = new System.Drawing.Point(38, 436);
|
||||
this.TextureResTextBox.Name = "TextureResTextBox";
|
||||
this.TextureResTextBox.Size = new System.Drawing.Size(111, 24);
|
||||
this.TextureResTextBox.TabIndex = 3;
|
||||
this.TextureResTextBox.Text = "0";
|
||||
//
|
||||
// RefreshRateComboBox
|
||||
//
|
||||
this.RefreshRateComboBox.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(25)))), ((int)(((byte)(25)))));
|
||||
this.RefreshRateComboBox.DataBindings.Add(new System.Windows.Forms.Binding("Font", global::AndroidSideloader.Properties.Settings.Default, "FontStyle", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
|
||||
this.RefreshRateComboBox.DataBindings.Add(new System.Windows.Forms.Binding("ForeColor", global::AndroidSideloader.Properties.Settings.Default, "FontColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
|
||||
this.RefreshRateComboBox.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
|
||||
this.RefreshRateComboBox.Font = global::AndroidSideloader.Properties.Settings.Default.FontStyle;
|
||||
this.RefreshRateComboBox.ForeColor = global::AndroidSideloader.Properties.Settings.Default.FontColor;
|
||||
this.RefreshRateComboBox.FormattingEnabled = true;
|
||||
this.RefreshRateComboBox.Items.AddRange(new object[] {
|
||||
"72",
|
||||
"90",
|
||||
"120"});
|
||||
this.RefreshRateComboBox.Location = new System.Drawing.Point(38, 325);
|
||||
this.RefreshRateComboBox.Name = "RefreshRateComboBox";
|
||||
this.RefreshRateComboBox.Size = new System.Drawing.Size(232, 26);
|
||||
this.RefreshRateComboBox.TabIndex = 0;
|
||||
this.RefreshRateComboBox.Text = "Select refresh rate";
|
||||
//
|
||||
// btnApplyTempSettings
|
||||
//
|
||||
this.btnApplyTempSettings.Active1 = System.Drawing.Color.FromArgb(((int)(((byte)(45)))), ((int)(((byte)(45)))), ((int)(((byte)(45)))));
|
||||
this.btnApplyTempSettings.Active2 = System.Drawing.Color.FromArgb(((int)(((byte)(45)))), ((int)(((byte)(45)))), ((int)(((byte)(45)))));
|
||||
this.btnApplyTempSettings.BackColor = System.Drawing.Color.Transparent;
|
||||
this.btnApplyTempSettings.DialogResult = System.Windows.Forms.DialogResult.OK;
|
||||
this.btnApplyTempSettings.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F);
|
||||
this.btnApplyTempSettings.ForeColor = System.Drawing.Color.White;
|
||||
this.btnApplyTempSettings.Inactive1 = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(25)))), ((int)(((byte)(25)))));
|
||||
this.btnApplyTempSettings.Inactive2 = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(25)))), ((int)(((byte)(25)))));
|
||||
this.btnApplyTempSettings.Location = new System.Drawing.Point(185, 434);
|
||||
this.btnApplyTempSettings.Name = "btnApplyTempSettings";
|
||||
this.btnApplyTempSettings.Radius = 5;
|
||||
this.btnApplyTempSettings.Size = new System.Drawing.Size(85, 25);
|
||||
this.btnApplyTempSettings.Stroke = true;
|
||||
this.btnApplyTempSettings.StrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(74)))), ((int)(((byte)(74)))), ((int)(((byte)(74)))));
|
||||
this.btnApplyTempSettings.TabIndex = 19;
|
||||
this.btnApplyTempSettings.Text = "APPLY";
|
||||
this.btnApplyTempSettings.Transparency = false;
|
||||
this.btnApplyTempSettings.Click += new System.EventHandler(this.btnApplyTempSettings_Click);
|
||||
//
|
||||
// questVids
|
||||
//
|
||||
this.questVids.Active1 = System.Drawing.Color.FromArgb(((int)(((byte)(45)))), ((int)(((byte)(45)))), ((int)(((byte)(45)))));
|
||||
this.questVids.Active2 = System.Drawing.Color.FromArgb(((int)(((byte)(45)))), ((int)(((byte)(45)))), ((int)(((byte)(45)))));
|
||||
this.questVids.BackColor = System.Drawing.Color.Transparent;
|
||||
this.questVids.DialogResult = System.Windows.Forms.DialogResult.OK;
|
||||
this.questVids.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F);
|
||||
this.questVids.ForeColor = System.Drawing.Color.White;
|
||||
this.questVids.Inactive1 = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(25)))), ((int)(((byte)(25)))));
|
||||
this.questVids.Inactive2 = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(25)))), ((int)(((byte)(25)))));
|
||||
this.questVids.Location = new System.Drawing.Point(72, 178);
|
||||
this.questVids.Name = "questVids";
|
||||
this.questVids.Radius = 5;
|
||||
this.questVids.Size = new System.Drawing.Size(165, 25);
|
||||
this.questVids.Stroke = true;
|
||||
this.questVids.StrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(74)))), ((int)(((byte)(74)))), ((int)(((byte)(74)))));
|
||||
this.questVids.TabIndex = 21;
|
||||
this.questVids.Text = "RECORDINGS";
|
||||
this.questVids.Transparency = false;
|
||||
this.questVids.Click += new System.EventHandler(this.questVids_Click);
|
||||
//
|
||||
// questPics
|
||||
//
|
||||
this.questPics.Active1 = System.Drawing.Color.FromArgb(((int)(((byte)(45)))), ((int)(((byte)(45)))), ((int)(((byte)(45)))));
|
||||
this.questPics.Active2 = System.Drawing.Color.FromArgb(((int)(((byte)(45)))), ((int)(((byte)(45)))), ((int)(((byte)(45)))));
|
||||
this.questPics.BackColor = System.Drawing.Color.Transparent;
|
||||
this.questPics.DialogResult = System.Windows.Forms.DialogResult.OK;
|
||||
this.questPics.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F);
|
||||
this.questPics.ForeColor = System.Drawing.Color.White;
|
||||
this.questPics.Inactive1 = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(25)))), ((int)(((byte)(25)))));
|
||||
this.questPics.Inactive2 = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(25)))), ((int)(((byte)(25)))));
|
||||
this.questPics.Location = new System.Drawing.Point(72, 124);
|
||||
this.questPics.Name = "questPics";
|
||||
this.questPics.Radius = 5;
|
||||
this.questPics.Size = new System.Drawing.Size(165, 25);
|
||||
this.questPics.Stroke = true;
|
||||
this.questPics.StrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(74)))), ((int)(((byte)(74)))), ((int)(((byte)(74)))));
|
||||
this.questPics.TabIndex = 22;
|
||||
this.questPics.Text = "SCREENSHOTS";
|
||||
this.questPics.Transparency = false;
|
||||
this.questPics.Click += new System.EventHandler(this.questPics_Click);
|
||||
//
|
||||
// btnApplyUsername
|
||||
//
|
||||
this.btnApplyUsername.Active1 = System.Drawing.Color.FromArgb(((int)(((byte)(45)))), ((int)(((byte)(45)))), ((int)(((byte)(45)))));
|
||||
this.btnApplyUsername.Active2 = System.Drawing.Color.FromArgb(((int)(((byte)(45)))), ((int)(((byte)(45)))), ((int)(((byte)(45)))));
|
||||
this.btnApplyUsername.Active1 = System.Drawing.Color.FromArgb(((int)(((byte)(113)))), ((int)(((byte)(223)))), ((int)(((byte)(193)))));
|
||||
this.btnApplyUsername.Active2 = System.Drawing.Color.FromArgb(((int)(((byte)(113)))), ((int)(((byte)(223)))), ((int)(((byte)(193)))));
|
||||
this.btnApplyUsername.BackColor = System.Drawing.Color.Transparent;
|
||||
this.btnApplyUsername.Cursor = System.Windows.Forms.Cursors.Hand;
|
||||
this.btnApplyUsername.DialogResult = System.Windows.Forms.DialogResult.OK;
|
||||
this.btnApplyUsername.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F);
|
||||
this.btnApplyUsername.ForeColor = System.Drawing.Color.White;
|
||||
this.btnApplyUsername.Inactive1 = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(25)))), ((int)(((byte)(25)))));
|
||||
this.btnApplyUsername.Inactive2 = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(25)))), ((int)(((byte)(25)))));
|
||||
this.btnApplyUsername.Location = new System.Drawing.Point(194, 38);
|
||||
this.btnApplyUsername.Disabled1 = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(35)))), ((int)(((byte)(45)))));
|
||||
this.btnApplyUsername.Disabled2 = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(28)))), ((int)(((byte)(35)))));
|
||||
this.btnApplyUsername.DisabledStrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(50)))), ((int)(((byte)(55)))), ((int)(((byte)(65)))));
|
||||
this.btnApplyUsername.Enabled = false;
|
||||
this.btnApplyUsername.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold);
|
||||
this.btnApplyUsername.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(80)))), ((int)(((byte)(80)))), ((int)(((byte)(80)))));
|
||||
this.btnApplyUsername.Inactive1 = System.Drawing.Color.FromArgb(((int)(((byte)(93)))), ((int)(((byte)(203)))), ((int)(((byte)(173)))));
|
||||
this.btnApplyUsername.Inactive2 = System.Drawing.Color.FromArgb(((int)(((byte)(93)))), ((int)(((byte)(203)))), ((int)(((byte)(173)))));
|
||||
this.btnApplyUsername.Location = new System.Drawing.Point(234, 45);
|
||||
this.btnApplyUsername.Name = "btnApplyUsername";
|
||||
this.btnApplyUsername.Radius = 5;
|
||||
this.btnApplyUsername.Size = new System.Drawing.Size(81, 25);
|
||||
this.btnApplyUsername.Stroke = true;
|
||||
this.btnApplyUsername.StrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(74)))), ((int)(((byte)(74)))), ((int)(((byte)(74)))));
|
||||
this.btnApplyUsername.TabIndex = 27;
|
||||
this.btnApplyUsername.Size = new System.Drawing.Size(80, 25);
|
||||
this.btnApplyUsername.Stroke = false;
|
||||
this.btnApplyUsername.StrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(93)))), ((int)(((byte)(203)))), ((int)(((byte)(173)))));
|
||||
this.btnApplyUsername.TabIndex = 1;
|
||||
this.btnApplyUsername.Text = "APPLY";
|
||||
this.btnApplyUsername.Transparency = false;
|
||||
this.btnApplyUsername.Click += new System.EventHandler(this.btnApplyUsername_Click);
|
||||
//
|
||||
// label3
|
||||
// questPics
|
||||
//
|
||||
this.label3.AutoSize = true;
|
||||
this.label3.BackColor = System.Drawing.Color.Transparent;
|
||||
this.label3.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
|
||||
this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.label3.ForeColor = System.Drawing.Color.LightSteelBlue;
|
||||
this.label3.Location = new System.Drawing.Point(37, 206);
|
||||
this.label3.Name = "label3";
|
||||
this.label3.Size = new System.Drawing.Size(235, 16);
|
||||
this.label3.TabIndex = 28;
|
||||
this.label3.Text = "Exports to: Desktop\\Quest Recordings";
|
||||
this.label3.TextAlign = System.Drawing.ContentAlignment.TopCenter;
|
||||
this.questPics.Active1 = System.Drawing.Color.FromArgb(((int)(((byte)(50)))), ((int)(((byte)(55)))), ((int)(((byte)(65)))));
|
||||
this.questPics.Active2 = System.Drawing.Color.FromArgb(((int)(((byte)(50)))), ((int)(((byte)(55)))), ((int)(((byte)(65)))));
|
||||
this.questPics.BackColor = System.Drawing.Color.Transparent;
|
||||
this.questPics.Cursor = System.Windows.Forms.Cursors.Hand;
|
||||
this.questPics.DialogResult = System.Windows.Forms.DialogResult.OK;
|
||||
this.questPics.Disabled1 = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(35)))), ((int)(((byte)(45)))));
|
||||
this.questPics.Disabled2 = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(28)))), ((int)(((byte)(35)))));
|
||||
this.questPics.DisabledStrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(50)))), ((int)(((byte)(55)))), ((int)(((byte)(65)))));
|
||||
this.questPics.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||
this.questPics.ForeColor = System.Drawing.Color.White;
|
||||
this.questPics.Inactive1 = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(44)))), ((int)(((byte)(52)))));
|
||||
this.questPics.Inactive2 = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(44)))), ((int)(((byte)(52)))));
|
||||
this.questPics.Location = new System.Drawing.Point(24, 127);
|
||||
this.questPics.Name = "questPics";
|
||||
this.questPics.Radius = 5;
|
||||
this.questPics.Size = new System.Drawing.Size(140, 28);
|
||||
this.questPics.Stroke = true;
|
||||
this.questPics.StrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(60)))), ((int)(((byte)(65)))), ((int)(((byte)(75)))));
|
||||
this.questPics.TabIndex = 2;
|
||||
this.questPics.Text = "Screenshots";
|
||||
this.questPics.Transparency = false;
|
||||
this.questPics.Click += new System.EventHandler(this.questPics_Click);
|
||||
//
|
||||
// questVids
|
||||
//
|
||||
this.questVids.Active1 = System.Drawing.Color.FromArgb(((int)(((byte)(50)))), ((int)(((byte)(55)))), ((int)(((byte)(65)))));
|
||||
this.questVids.Active2 = System.Drawing.Color.FromArgb(((int)(((byte)(50)))), ((int)(((byte)(55)))), ((int)(((byte)(65)))));
|
||||
this.questVids.BackColor = System.Drawing.Color.Transparent;
|
||||
this.questVids.Cursor = System.Windows.Forms.Cursors.Hand;
|
||||
this.questVids.DialogResult = System.Windows.Forms.DialogResult.OK;
|
||||
this.questVids.Disabled1 = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(35)))), ((int)(((byte)(45)))));
|
||||
this.questVids.Disabled2 = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(28)))), ((int)(((byte)(35)))));
|
||||
this.questVids.DisabledStrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(50)))), ((int)(((byte)(55)))), ((int)(((byte)(65)))));
|
||||
this.questVids.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||
this.questVids.ForeColor = System.Drawing.Color.White;
|
||||
this.questVids.Inactive1 = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(44)))), ((int)(((byte)(52)))));
|
||||
this.questVids.Inactive2 = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(44)))), ((int)(((byte)(52)))));
|
||||
this.questVids.Location = new System.Drawing.Point(174, 127);
|
||||
this.questVids.Name = "questVids";
|
||||
this.questVids.Radius = 5;
|
||||
this.questVids.Size = new System.Drawing.Size(140, 28);
|
||||
this.questVids.Stroke = true;
|
||||
this.questVids.StrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(60)))), ((int)(((byte)(65)))), ((int)(((byte)(75)))));
|
||||
this.questVids.TabIndex = 3;
|
||||
this.questVids.Text = "Recordings";
|
||||
this.questVids.Transparency = false;
|
||||
this.questVids.Click += new System.EventHandler(this.questVids_Click);
|
||||
//
|
||||
// lblScreenshotsPath
|
||||
//
|
||||
this.lblScreenshotsPath.AutoSize = true;
|
||||
this.lblScreenshotsPath.Font = new System.Drawing.Font("Segoe UI", 8F);
|
||||
this.lblScreenshotsPath.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(120)))), ((int)(((byte)(120)))), ((int)(((byte)(120)))));
|
||||
this.lblScreenshotsPath.Location = new System.Drawing.Point(24, 158);
|
||||
this.lblScreenshotsPath.Name = "lblScreenshotsPath";
|
||||
this.lblScreenshotsPath.Size = new System.Drawing.Size(161, 13);
|
||||
this.lblScreenshotsPath.TabIndex = 4;
|
||||
this.lblScreenshotsPath.Text = "→ Desktop\\Quest Screenshots";
|
||||
//
|
||||
// lblRecordingsPath
|
||||
//
|
||||
this.lblRecordingsPath.AutoSize = true;
|
||||
this.lblRecordingsPath.Font = new System.Drawing.Font("Segoe UI", 8F);
|
||||
this.lblRecordingsPath.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(120)))), ((int)(((byte)(120)))), ((int)(((byte)(120)))));
|
||||
this.lblRecordingsPath.Location = new System.Drawing.Point(174, 158);
|
||||
this.lblRecordingsPath.Name = "lblRecordingsPath";
|
||||
this.lblRecordingsPath.Size = new System.Drawing.Size(157, 13);
|
||||
this.lblRecordingsPath.TabIndex = 5;
|
||||
this.lblRecordingsPath.Text = "→ Desktop\\Quest Recordings";
|
||||
//
|
||||
// toggleDeleteAfterTransfer
|
||||
//
|
||||
this.toggleDeleteAfterTransfer.BackColor = System.Drawing.Color.Transparent;
|
||||
this.toggleDeleteAfterTransfer.Cursor = System.Windows.Forms.Cursors.Hand;
|
||||
this.toggleDeleteAfterTransfer.Location = new System.Drawing.Point(27, 189);
|
||||
this.toggleDeleteAfterTransfer.Name = "toggleDeleteAfterTransfer";
|
||||
this.toggleDeleteAfterTransfer.OffColor = System.Drawing.Color.FromArgb(((int)(((byte)(60)))), ((int)(((byte)(65)))), ((int)(((byte)(75)))));
|
||||
this.toggleDeleteAfterTransfer.OnColor = System.Drawing.Color.FromArgb(((int)(((byte)(93)))), ((int)(((byte)(203)))), ((int)(((byte)(173)))));
|
||||
this.toggleDeleteAfterTransfer.Size = new System.Drawing.Size(36, 18);
|
||||
this.toggleDeleteAfterTransfer.TabIndex = 6;
|
||||
this.toggleDeleteAfterTransfer.ThumbColor = System.Drawing.Color.White;
|
||||
this.toggleDeleteAfterTransfer.CheckedChanged += new System.EventHandler(this.toggleDeleteAfterTransfer_CheckedChanged);
|
||||
//
|
||||
// lblDeleteAfterTransfer
|
||||
//
|
||||
this.lblDeleteAfterTransfer.AutoSize = true;
|
||||
this.lblDeleteAfterTransfer.Font = new System.Drawing.Font("Segoe UI", 9.5F);
|
||||
this.lblDeleteAfterTransfer.ForeColor = System.Drawing.Color.White;
|
||||
this.lblDeleteAfterTransfer.Location = new System.Drawing.Point(72, 188);
|
||||
this.lblDeleteAfterTransfer.Name = "lblDeleteAfterTransfer";
|
||||
this.lblDeleteAfterTransfer.Size = new System.Drawing.Size(195, 17);
|
||||
this.lblDeleteAfterTransfer.TabIndex = 7;
|
||||
this.lblDeleteAfterTransfer.Text = "Delete from Quest after transfer";
|
||||
//
|
||||
// lblPerformanceNote
|
||||
//
|
||||
this.lblPerformanceNote.AutoSize = true;
|
||||
this.lblPerformanceNote.Font = new System.Drawing.Font("Segoe UI", 8F);
|
||||
this.lblPerformanceNote.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(120)))), ((int)(((byte)(120)))), ((int)(((byte)(120)))));
|
||||
this.lblPerformanceNote.Location = new System.Drawing.Point(21, 254);
|
||||
this.lblPerformanceNote.Name = "lblPerformanceNote";
|
||||
this.lblPerformanceNote.Size = new System.Drawing.Size(120, 13);
|
||||
this.lblPerformanceNote.TabIndex = 10;
|
||||
this.lblPerformanceNote.Text = "Reboot Quest to reset";
|
||||
//
|
||||
// lblRefreshRate
|
||||
//
|
||||
this.lblRefreshRate.AutoSize = true;
|
||||
this.lblRefreshRate.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||
this.lblRefreshRate.ForeColor = System.Drawing.Color.White;
|
||||
this.lblRefreshRate.Location = new System.Drawing.Point(24, 280);
|
||||
this.lblRefreshRate.Name = "lblRefreshRate";
|
||||
this.lblRefreshRate.Size = new System.Drawing.Size(72, 15);
|
||||
this.lblRefreshRate.TabIndex = 11;
|
||||
this.lblRefreshRate.Text = "Refresh Rate";
|
||||
//
|
||||
// RefreshRateComboBox
|
||||
//
|
||||
this.RefreshRateComboBox.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(44)))), ((int)(((byte)(52)))));
|
||||
this.RefreshRateComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||
this.RefreshRateComboBox.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
|
||||
this.RefreshRateComboBox.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||
this.RefreshRateComboBox.ForeColor = System.Drawing.Color.White;
|
||||
this.RefreshRateComboBox.FormattingEnabled = true;
|
||||
this.RefreshRateComboBox.Items.AddRange(new object[] {
|
||||
"72 Hz",
|
||||
"90 Hz",
|
||||
"120 Hz"});
|
||||
this.RefreshRateComboBox.Location = new System.Drawing.Point(24, 298);
|
||||
this.RefreshRateComboBox.Name = "RefreshRateComboBox";
|
||||
this.RefreshRateComboBox.Size = new System.Drawing.Size(130, 23);
|
||||
this.RefreshRateComboBox.TabIndex = 12;
|
||||
//
|
||||
// lblGpuLevel
|
||||
//
|
||||
this.lblGpuLevel.AutoSize = true;
|
||||
this.lblGpuLevel.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||
this.lblGpuLevel.ForeColor = System.Drawing.Color.White;
|
||||
this.lblGpuLevel.Location = new System.Drawing.Point(170, 280);
|
||||
this.lblGpuLevel.Name = "lblGpuLevel";
|
||||
this.lblGpuLevel.Size = new System.Drawing.Size(60, 15);
|
||||
this.lblGpuLevel.TabIndex = 13;
|
||||
this.lblGpuLevel.Text = "GPU Level";
|
||||
//
|
||||
// GPUComboBox
|
||||
//
|
||||
this.GPUComboBox.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(44)))), ((int)(((byte)(52)))));
|
||||
this.GPUComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||
this.GPUComboBox.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
|
||||
this.GPUComboBox.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||
this.GPUComboBox.ForeColor = System.Drawing.Color.White;
|
||||
this.GPUComboBox.FormattingEnabled = true;
|
||||
this.GPUComboBox.Items.AddRange(new object[] {
|
||||
"0 (Default)",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4"});
|
||||
this.GPUComboBox.Location = new System.Drawing.Point(170, 298);
|
||||
this.GPUComboBox.Name = "GPUComboBox";
|
||||
this.GPUComboBox.Size = new System.Drawing.Size(130, 23);
|
||||
this.GPUComboBox.TabIndex = 14;
|
||||
//
|
||||
// lblCpuLevel
|
||||
//
|
||||
this.lblCpuLevel.AutoSize = true;
|
||||
this.lblCpuLevel.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||
this.lblCpuLevel.ForeColor = System.Drawing.Color.White;
|
||||
this.lblCpuLevel.Location = new System.Drawing.Point(24, 330);
|
||||
this.lblCpuLevel.Name = "lblCpuLevel";
|
||||
this.lblCpuLevel.Size = new System.Drawing.Size(60, 15);
|
||||
this.lblCpuLevel.TabIndex = 15;
|
||||
this.lblCpuLevel.Text = "CPU Level";
|
||||
//
|
||||
// CPUComboBox
|
||||
//
|
||||
this.CPUComboBox.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(44)))), ((int)(((byte)(52)))));
|
||||
this.CPUComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||
this.CPUComboBox.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
|
||||
this.CPUComboBox.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||
this.CPUComboBox.ForeColor = System.Drawing.Color.White;
|
||||
this.CPUComboBox.FormattingEnabled = true;
|
||||
this.CPUComboBox.Items.AddRange(new object[] {
|
||||
"0 (Default)",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4"});
|
||||
this.CPUComboBox.Location = new System.Drawing.Point(24, 348);
|
||||
this.CPUComboBox.Name = "CPUComboBox";
|
||||
this.CPUComboBox.Size = new System.Drawing.Size(130, 23);
|
||||
this.CPUComboBox.TabIndex = 16;
|
||||
//
|
||||
// lblResolution
|
||||
//
|
||||
this.lblResolution.AutoSize = true;
|
||||
this.lblResolution.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||
this.lblResolution.ForeColor = System.Drawing.Color.White;
|
||||
this.lblResolution.Location = new System.Drawing.Point(170, 330);
|
||||
this.lblResolution.Name = "lblResolution";
|
||||
this.lblResolution.Size = new System.Drawing.Size(63, 15);
|
||||
this.lblResolution.TabIndex = 17;
|
||||
this.lblResolution.Text = "Resolution";
|
||||
//
|
||||
// TextureResTextBox
|
||||
//
|
||||
this.TextureResTextBox.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(44)))), ((int)(((byte)(52)))));
|
||||
this.TextureResTextBox.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||||
this.TextureResTextBox.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||
this.TextureResTextBox.ForeColor = System.Drawing.Color.White;
|
||||
this.TextureResTextBox.Location = new System.Drawing.Point(170, 348);
|
||||
this.TextureResTextBox.Name = "TextureResTextBox";
|
||||
this.TextureResTextBox.Size = new System.Drawing.Size(130, 23);
|
||||
this.TextureResTextBox.TabIndex = 18;
|
||||
this.TextureResTextBox.Text = "0";
|
||||
//
|
||||
// btnApplyTempSettings
|
||||
//
|
||||
this.btnApplyTempSettings.Active1 = System.Drawing.Color.FromArgb(((int)(((byte)(113)))), ((int)(((byte)(223)))), ((int)(((byte)(193)))));
|
||||
this.btnApplyTempSettings.Active2 = System.Drawing.Color.FromArgb(((int)(((byte)(113)))), ((int)(((byte)(223)))), ((int)(((byte)(193)))));
|
||||
this.btnApplyTempSettings.BackColor = System.Drawing.Color.Transparent;
|
||||
this.btnApplyTempSettings.Cursor = System.Windows.Forms.Cursors.Hand;
|
||||
this.btnApplyTempSettings.DialogResult = System.Windows.Forms.DialogResult.OK;
|
||||
this.btnApplyTempSettings.Disabled1 = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(35)))), ((int)(((byte)(45)))));
|
||||
this.btnApplyTempSettings.Disabled2 = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(28)))), ((int)(((byte)(35)))));
|
||||
this.btnApplyTempSettings.DisabledStrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(50)))), ((int)(((byte)(55)))), ((int)(((byte)(65)))));
|
||||
this.btnApplyTempSettings.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold);
|
||||
this.btnApplyTempSettings.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(24)))), ((int)(((byte)(29)))));
|
||||
this.btnApplyTempSettings.Inactive1 = System.Drawing.Color.FromArgb(((int)(((byte)(93)))), ((int)(((byte)(203)))), ((int)(((byte)(173)))));
|
||||
this.btnApplyTempSettings.Inactive2 = System.Drawing.Color.FromArgb(((int)(((byte)(93)))), ((int)(((byte)(203)))), ((int)(((byte)(173)))));
|
||||
this.btnApplyTempSettings.Location = new System.Drawing.Point(24, 385);
|
||||
this.btnApplyTempSettings.Name = "btnApplyTempSettings";
|
||||
this.btnApplyTempSettings.Radius = 5;
|
||||
this.btnApplyTempSettings.Size = new System.Drawing.Size(130, 30);
|
||||
this.btnApplyTempSettings.Stroke = false;
|
||||
this.btnApplyTempSettings.StrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(93)))), ((int)(((byte)(203)))), ((int)(((byte)(173)))));
|
||||
this.btnApplyTempSettings.TabIndex = 10;
|
||||
this.btnApplyTempSettings.Text = "APPLY SETTINGS";
|
||||
this.btnApplyTempSettings.Transparency = false;
|
||||
this.btnApplyTempSettings.Click += new System.EventHandler(this.btnApplyTempSettings_Click);
|
||||
//
|
||||
// separator1
|
||||
//
|
||||
this.separator1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(50)))), ((int)(((byte)(55)))), ((int)(((byte)(65)))));
|
||||
this.separator1.Location = new System.Drawing.Point(20, 85);
|
||||
this.separator1.Name = "separator1";
|
||||
this.separator1.Size = new System.Drawing.Size(295, 1);
|
||||
this.separator1.TabIndex = 2;
|
||||
//
|
||||
// separator2
|
||||
//
|
||||
this.separator2.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(50)))), ((int)(((byte)(55)))), ((int)(((byte)(65)))));
|
||||
this.separator2.Location = new System.Drawing.Point(20, 220);
|
||||
this.separator2.Name = "separator2";
|
||||
this.separator2.Size = new System.Drawing.Size(295, 1);
|
||||
this.separator2.TabIndex = 8;
|
||||
//
|
||||
// btnClose
|
||||
//
|
||||
this.btnClose.Active1 = System.Drawing.Color.FromArgb(((int)(((byte)(60)))), ((int)(((byte)(65)))), ((int)(((byte)(75)))));
|
||||
this.btnClose.Active2 = System.Drawing.Color.FromArgb(((int)(((byte)(60)))), ((int)(((byte)(65)))), ((int)(((byte)(75)))));
|
||||
this.btnClose.BackColor = System.Drawing.Color.Transparent;
|
||||
this.btnClose.Cursor = System.Windows.Forms.Cursors.Hand;
|
||||
this.btnClose.DialogResult = System.Windows.Forms.DialogResult.OK;
|
||||
this.btnClose.Disabled1 = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(35)))), ((int)(((byte)(45)))));
|
||||
this.btnClose.Disabled2 = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(28)))), ((int)(((byte)(35)))));
|
||||
this.btnClose.DisabledStrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(50)))), ((int)(((byte)(55)))), ((int)(((byte)(65)))));
|
||||
this.btnClose.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||
this.btnClose.ForeColor = System.Drawing.Color.White;
|
||||
this.btnClose.Inactive1 = System.Drawing.Color.FromArgb(((int)(((byte)(50)))), ((int)(((byte)(55)))), ((int)(((byte)(65)))));
|
||||
this.btnClose.Inactive2 = System.Drawing.Color.FromArgb(((int)(((byte)(50)))), ((int)(((byte)(55)))), ((int)(((byte)(65)))));
|
||||
this.btnClose.Location = new System.Drawing.Point(170, 385);
|
||||
this.btnClose.Name = "btnClose";
|
||||
this.btnClose.Radius = 5;
|
||||
this.btnClose.Size = new System.Drawing.Size(130, 30);
|
||||
this.btnClose.Stroke = true;
|
||||
this.btnClose.StrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(74)))), ((int)(((byte)(74)))), ((int)(((byte)(74)))));
|
||||
this.btnClose.TabIndex = 11;
|
||||
this.btnClose.Text = "Close";
|
||||
this.btnClose.Transparency = false;
|
||||
this.btnClose.Click += new System.EventHandler(this.btnClose_Click);
|
||||
//
|
||||
// QuestForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(45)))), ((int)(((byte)(45)))), ((int)(((byte)(45)))));
|
||||
this.BackgroundImage = global::AndroidSideloader.Properties.Resources.pattern_cubes;
|
||||
this.ClientSize = new System.Drawing.Size(309, 486);
|
||||
this.Controls.Add(this.label3);
|
||||
this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(24)))), ((int)(((byte)(29)))));
|
||||
this.CancelButton = this.btnClose;
|
||||
this.ClientSize = new System.Drawing.Size(340, 435);
|
||||
this.Controls.Add(this.lblUsernameSection);
|
||||
this.Controls.Add(this.GlobalUsername);
|
||||
this.Controls.Add(this.btnApplyUsername);
|
||||
this.Controls.Add(this.separator1);
|
||||
this.Controls.Add(this.lblMediaSection);
|
||||
this.Controls.Add(this.questPics);
|
||||
this.Controls.Add(this.questVids);
|
||||
this.Controls.Add(this.btnApplyTempSettings);
|
||||
this.Controls.Add(this.label14);
|
||||
this.Controls.Add(this.label10);
|
||||
this.Controls.Add(this.splitter1);
|
||||
this.Controls.Add(this.DeleteShots);
|
||||
this.Controls.Add(this.label16);
|
||||
this.Controls.Add(this.label2);
|
||||
this.Controls.Add(this.label11);
|
||||
this.Controls.Add(this.label12);
|
||||
this.Controls.Add(this.label1);
|
||||
this.Controls.Add(this.CPUComboBox);
|
||||
this.Controls.Add(this.GPUComboBox);
|
||||
this.Controls.Add(this.ResolutionLabel);
|
||||
this.Controls.Add(this.GlobalUsername);
|
||||
this.Controls.Add(this.TextureResTextBox);
|
||||
this.Controls.Add(this.lblScreenshotsPath);
|
||||
this.Controls.Add(this.lblRecordingsPath);
|
||||
this.Controls.Add(this.toggleDeleteAfterTransfer);
|
||||
this.Controls.Add(this.lblDeleteAfterTransfer);
|
||||
this.Controls.Add(this.separator2);
|
||||
this.Controls.Add(this.lblPerformanceSection);
|
||||
this.Controls.Add(this.lblPerformanceNote);
|
||||
this.Controls.Add(this.lblRefreshRate);
|
||||
this.Controls.Add(this.RefreshRateComboBox);
|
||||
this.Controls.Add(this.lblGpuLevel);
|
||||
this.Controls.Add(this.GPUComboBox);
|
||||
this.Controls.Add(this.lblCpuLevel);
|
||||
this.Controls.Add(this.CPUComboBox);
|
||||
this.Controls.Add(this.lblResolution);
|
||||
this.Controls.Add(this.TextureResTextBox);
|
||||
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.MaximumSize = new System.Drawing.Size(325, 525);
|
||||
this.MinimumSize = new System.Drawing.Size(325, 525);
|
||||
this.MaximizeBox = false;
|
||||
this.MinimizeBox = false;
|
||||
this.Name = "QuestForm";
|
||||
this.ShowIcon = false;
|
||||
this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||
this.Text = "Quest settings";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||
this.Text = "Quest Settings";
|
||||
this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.QuestForm_FormClosed);
|
||||
this.Load += new System.EventHandler(this.QuestForm_Load);
|
||||
this.ResumeLayout(false);
|
||||
@@ -415,26 +457,40 @@ namespace AndroidSideloader
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.ComboBox RefreshRateComboBox;
|
||||
private System.Windows.Forms.TextBox TextureResTextBox;
|
||||
private System.Windows.Forms.Label ResolutionLabel;
|
||||
private System.Windows.Forms.ComboBox GPUComboBox;
|
||||
private System.Windows.Forms.ComboBox CPUComboBox;
|
||||
private Label label1;
|
||||
private Label label2;
|
||||
private Label label11;
|
||||
private Label label12;
|
||||
private CheckBox DeleteShots;
|
||||
private Splitter splitter1;
|
||||
private Label label10;
|
||||
private Label label14;
|
||||
private Label label16;
|
||||
private TextBox GlobalUsername;
|
||||
private RoundButton btnApplyTempSettings;
|
||||
private RoundButton questVids;
|
||||
private RoundButton questPics;
|
||||
private RoundButton btnApplyUsername;
|
||||
private Label label3;
|
||||
}
|
||||
// Section Labels
|
||||
private System.Windows.Forms.Label lblUsernameSection;
|
||||
private System.Windows.Forms.Label lblMediaSection;
|
||||
private System.Windows.Forms.Label lblPerformanceSection;
|
||||
|
||||
}
|
||||
// Username controls
|
||||
private System.Windows.Forms.TextBox GlobalUsername;
|
||||
private RoundButton btnApplyUsername;
|
||||
|
||||
// Media controls
|
||||
private RoundButton questPics;
|
||||
private RoundButton questVids;
|
||||
private System.Windows.Forms.Label lblScreenshotsPath;
|
||||
private System.Windows.Forms.Label lblRecordingsPath;
|
||||
private ToggleSwitch toggleDeleteAfterTransfer;
|
||||
private System.Windows.Forms.Label lblDeleteAfterTransfer;
|
||||
|
||||
// Performance controls
|
||||
private System.Windows.Forms.Label lblPerformanceNote;
|
||||
private System.Windows.Forms.Label lblRefreshRate;
|
||||
private System.Windows.Forms.ComboBox RefreshRateComboBox;
|
||||
private System.Windows.Forms.Label lblGpuLevel;
|
||||
private System.Windows.Forms.ComboBox GPUComboBox;
|
||||
private System.Windows.Forms.Label lblCpuLevel;
|
||||
private System.Windows.Forms.ComboBox CPUComboBox;
|
||||
private System.Windows.Forms.Label lblResolution;
|
||||
private System.Windows.Forms.TextBox TextureResTextBox;
|
||||
private RoundButton btnApplyTempSettings;
|
||||
|
||||
// Separators
|
||||
private System.Windows.Forms.Panel separator1;
|
||||
private System.Windows.Forms.Panel separator2;
|
||||
|
||||
// Close button
|
||||
private RoundButton btnClose;
|
||||
}
|
||||
}
|
||||
118
QuestForm.cs
@@ -11,164 +11,170 @@ namespace AndroidSideloader
|
||||
public static int length = 0;
|
||||
public static string[] result;
|
||||
public bool settingsexist = false;
|
||||
public bool delsh = false;
|
||||
private bool delsh = false;
|
||||
|
||||
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)
|
||||
{
|
||||
bool ChangesMade = false;
|
||||
|
||||
//Quest 2 settings, might remove them in the future since some of them are broken
|
||||
if (RefreshRateComboBox.SelectedIndex != -1)
|
||||
{
|
||||
_ = ADB.RunAdbCommandToString($"shell setprop debug.oculus.refreshRate {RefreshRateComboBox.SelectedItem}");
|
||||
string refreshRate = RefreshRateComboBox.SelectedItem.ToString().Replace(" Hz", "");
|
||||
_ = ADB.RunAdbCommandToString($"shell setprop debug.oculus.refreshRate {refreshRate}");
|
||||
_ = ADB.RunAdbCommandToString($"shell settings put global 90hz_global {RefreshRateComboBox.SelectedIndex}");
|
||||
_ = ADB.RunAdbCommandToString($"shell settings put global 90hzglobal {RefreshRateComboBox.SelectedIndex}");
|
||||
ChangesMade = true;
|
||||
}
|
||||
|
||||
if (TextureResTextBox.Text.Length > 0)
|
||||
if (TextureResTextBox.Text.Length > 0 && TextureResTextBox.Text != "0")
|
||||
{
|
||||
_ = int.TryParse(TextureResTextBox.Text, out _);
|
||||
_ = ADB.RunAdbCommandToString($"shell settings put global texture_size_Global {TextureResTextBox.Text}");
|
||||
_ = ADB.RunAdbCommandToString($"shell setprop debug.oculus.textureWidth {TextureResTextBox.Text}");
|
||||
_ = ADB.RunAdbCommandToString($"shell setprop debug.oculus.textureHeight {TextureResTextBox.Text}");
|
||||
ChangesMade = true;
|
||||
if (int.TryParse(TextureResTextBox.Text, out _))
|
||||
{
|
||||
_ = ADB.RunAdbCommandToString($"shell settings put global texture_size_Global {TextureResTextBox.Text}");
|
||||
_ = ADB.RunAdbCommandToString($"shell setprop debug.oculus.textureWidth {TextureResTextBox.Text}");
|
||||
_ = ADB.RunAdbCommandToString($"shell setprop debug.oculus.textureHeight {TextureResTextBox.Text}");
|
||||
ChangesMade = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (CPUComboBox.SelectedIndex != -1)
|
||||
{
|
||||
_ = ADB.RunAdbCommandToString($"shell setprop debug.oculus.cpuLevel {CPUComboBox.SelectedItem}");
|
||||
_ = ADB.RunAdbCommandToString($"shell setprop debug.oculus.cpuLevel {CPUComboBox.SelectedIndex}");
|
||||
ChangesMade = true;
|
||||
}
|
||||
|
||||
if (GPUComboBox.SelectedIndex != -1)
|
||||
{
|
||||
_ = ADB.RunAdbCommandToString($"shell setprop debug.oculus.gpuLevel {GPUComboBox.SelectedItem}");
|
||||
_ = ADB.RunAdbCommandToString($"shell setprop debug.oculus.gpuLevel {GPUComboBox.SelectedIndex}");
|
||||
ChangesMade = true;
|
||||
}
|
||||
|
||||
if (ChangesMade)
|
||||
{
|
||||
_ = MessageBox.Show("Settings applied!");
|
||||
_ = MessageBox.Show("Settings applied!", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void setLength(int value)
|
||||
{
|
||||
result = new string[value];
|
||||
}
|
||||
|
||||
|
||||
private void DeleteShots_CheckedChanged(object sender, EventArgs e)
|
||||
private void toggleDeleteAfterTransfer_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
delsh = DeleteShots.Checked;
|
||||
delsh = toggleDeleteAfterTransfer.Checked;
|
||||
}
|
||||
|
||||
private static readonly Random random = new Random();
|
||||
private static readonly object syncLock = new object();
|
||||
|
||||
public static int RandomNumber(int min, int max)
|
||||
{
|
||||
lock (syncLock)
|
||||
{ // synchronize
|
||||
{
|
||||
return random.Next(min, max);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private void QuestForm_FormClosed(object sender, FormClosedEventArgs e)
|
||||
{
|
||||
if (DeleteShots.Checked)
|
||||
{
|
||||
settings.Delsh = true;
|
||||
settings.Save();
|
||||
}
|
||||
if (!DeleteShots.Checked)
|
||||
{
|
||||
settings.Delsh = false;
|
||||
settings.Save();
|
||||
}
|
||||
settings.Delsh = toggleDeleteAfterTransfer.Checked;
|
||||
settings.Save();
|
||||
}
|
||||
|
||||
private void QuestForm_Load(object sender, EventArgs e)
|
||||
{
|
||||
DeleteShots.Checked = settings.Delsh;
|
||||
CenterToParent();
|
||||
toggleDeleteAfterTransfer.SetCheckedSilent(settings.Delsh);
|
||||
delsh = settings.Delsh;
|
||||
GlobalUsername.Text = settings.GlobalUsername;
|
||||
}
|
||||
|
||||
private void DeleteButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
_ = MessageBox.Show("Ok, Deleted your custom settings file.\nIf you would like to re-enable return here and apply settings again");
|
||||
File.Delete($"{settings.MainDir}\\Config.Json");
|
||||
}
|
||||
private void questPics_Click(object sender, EventArgs e)
|
||||
{
|
||||
string path = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
|
||||
|
||||
if (!Directory.Exists($"{path}\\Quest ScreenShots"))
|
||||
if (!Directory.Exists($"{path}\\Quest Screenshots"))
|
||||
{
|
||||
_ = Directory.CreateDirectory($"{path}\\Quest ScreenShots");
|
||||
_ = Directory.CreateDirectory($"{path}\\Quest Screenshots");
|
||||
}
|
||||
|
||||
_ = MessageBox.Show("Please wait until you get the message that the transfer has finished.");
|
||||
_ = MessageBox.Show("Please wait until you get the message that the transfer has finished.",
|
||||
"Transfer Starting", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
|
||||
Program.form.changeTitle("Pulling files...");
|
||||
_ = ADB.RunAdbCommandToString($"pull \"/sdcard/Oculus/Screenshots\" \"{path}\\Quest ScreenShots\"");
|
||||
_ = ADB.RunAdbCommandToString($"pull \"/sdcard/Oculus/Screenshots\" \"{path}\\Quest Screenshots\"");
|
||||
|
||||
if (delsh)
|
||||
{
|
||||
DialogResult dialogResult = MessageBox.Show("You have chosen to delete files from headset after transferring, so be sure to move them from your desktop to somewhere safe!", "Warning!", MessageBoxButtons.OKCancel);
|
||||
DialogResult dialogResult = MessageBox.Show(
|
||||
"You have chosen to delete files from headset after transferring.\n\nMake sure to move them from your desktop to somewhere safe!",
|
||||
"Warning", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning);
|
||||
|
||||
if (dialogResult == DialogResult.OK)
|
||||
{
|
||||
_ = ADB.RunAdbCommandToString("shell rm -r /sdcard/Oculus/Screenshots");
|
||||
_ = ADB.RunAdbCommandToString("shell mkdir /sdcard/Oculus/Screenshots");
|
||||
}
|
||||
}
|
||||
_ = MessageBox.Show("Transfer finished! ScreenShots can be found in a folder named Quest Screenshots on your desktop!");
|
||||
|
||||
_ = MessageBox.Show("Transfer finished!\n\nScreenshots can be found in:\nDesktop\\Quest Screenshots",
|
||||
"Complete", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
Program.form.changeTitle("Done!");
|
||||
}
|
||||
|
||||
private void questVids_Click(object sender, EventArgs e)
|
||||
{
|
||||
string path = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
|
||||
|
||||
if (!Directory.Exists($"{path}\\Quest Recordings"))
|
||||
{
|
||||
_ = Directory.CreateDirectory($"{path}\\Quest Recordings");
|
||||
}
|
||||
|
||||
_ = MessageBox.Show("Please wait until you get the message that the transfer has finished.");
|
||||
_ = MessageBox.Show("Please wait until you get the message that the transfer has finished.",
|
||||
"Transfer Starting", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
|
||||
Program.form.changeTitle("Pulling files...");
|
||||
_ = ADB.RunAdbCommandToString($"pull \"/sdcard/Oculus/Videoshots\" \"{path}\\Quest Recordings\"");
|
||||
|
||||
if (delsh)
|
||||
{
|
||||
DialogResult dialogResult = MessageBox.Show("You have chosen to delete files from headset after transferring, so be sure to move them from your desktop to somewhere safe!", "Warning!", MessageBoxButtons.OKCancel);
|
||||
DialogResult dialogResult = MessageBox.Show(
|
||||
"You have chosen to delete files from headset after transferring.\n\nMake sure to move them from your desktop to somewhere safe!",
|
||||
"Warning", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning);
|
||||
|
||||
if (dialogResult == DialogResult.OK)
|
||||
{
|
||||
_ = ADB.RunAdbCommandToString("shell rm -r /sdcard/Oculus/Videoshots");
|
||||
_ = ADB.RunAdbCommandToString("shell mkdir /sdcard/Oculus/Videoshots");
|
||||
}
|
||||
}
|
||||
_ = MessageBox.Show("Transfer finished! Recordings can be found in a folder named Quest Recordings on your desktop!");
|
||||
|
||||
_ = MessageBox.Show("Transfer finished!\n\nRecordings can be found in:\nDesktop\\Quest Recordings",
|
||||
"Complete", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
Program.form.changeTitle("Done!");
|
||||
}
|
||||
|
||||
private void btnApplyUsername_Click(object sender, EventArgs e)
|
||||
{
|
||||
_ = ADB.RunAdbCommandToString($"shell settings put global username {GlobalUsername.Text}");
|
||||
_ = MessageBox.Show($"Username set as {GlobalUsername.Text}", "Success");
|
||||
_ = MessageBox.Show($"Username set to: {GlobalUsername.Text}", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
}
|
||||
|
||||
private void Form_KeyDown(object sender, KeyEventArgs e)
|
||||
private void btnClose_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (e.KeyCode == Keys.Escape)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
protected override bool ProcessDialogKey(Keys keyData)
|
||||
{
|
||||
if (Form.ModifierKeys == Keys.None && keyData == Keys.Escape)
|
||||
@@ -182,8 +188,12 @@ namespace AndroidSideloader
|
||||
private void GlobalUsername_TextChanged(object sender, EventArgs e)
|
||||
{
|
||||
btnApplyUsername.Enabled = GlobalUsername.TextLength > 0;
|
||||
btnApplyUsername.ForeColor = System.Drawing.Color.FromArgb(
|
||||
((int)(((byte)(btnApplyUsername.Enabled ? 30 : 80)))),
|
||||
((int)(((byte)(btnApplyUsername.Enabled ? 24 : 80)))),
|
||||
((int)(((byte)(btnApplyUsername.Enabled ? 29 : 80)))));
|
||||
|
||||
settings.GlobalUsername = GlobalUsername.Text;
|
||||
settings.Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
6849
QuestForm.resx
42
RCLONE.cs
@@ -105,6 +105,9 @@ namespace AndroidSideloader
|
||||
rclone.StartInfo.RedirectStandardOutput = true;
|
||||
rclone.StartInfo.WorkingDirectory = Path.Combine(Environment.CurrentDirectory, "rclone");
|
||||
rclone.StartInfo.CreateNoWindow = true;
|
||||
|
||||
setRcloneProxy();
|
||||
|
||||
// Display RCLONE Window if the binary is being run in Debug Mode.
|
||||
if (MainForm.debugMode)
|
||||
{
|
||||
@@ -204,6 +207,9 @@ namespace AndroidSideloader
|
||||
rclone.StartInfo.RedirectStandardOutput = true;
|
||||
rclone.StartInfo.WorkingDirectory = Path.Combine(Environment.CurrentDirectory, "rclone");
|
||||
rclone.StartInfo.CreateNoWindow = true;
|
||||
|
||||
setRcloneProxy();
|
||||
|
||||
// Display RCLONE Window if the binary is being run in Debug Mode.
|
||||
if (MainForm.debugMode)
|
||||
{
|
||||
@@ -281,6 +287,9 @@ namespace AndroidSideloader
|
||||
rclone.StartInfo.RedirectStandardOutput = true;
|
||||
rclone.StartInfo.WorkingDirectory = Path.Combine(Environment.CurrentDirectory, "rclone");
|
||||
rclone.StartInfo.CreateNoWindow = true;
|
||||
|
||||
setRcloneProxy();
|
||||
|
||||
// Display RCLONE Window if the binary is being run in Debug Mode.
|
||||
if (MainForm.debugMode)
|
||||
{
|
||||
@@ -345,5 +354,38 @@ namespace AndroidSideloader
|
||||
|
||||
return prcoutput;
|
||||
}
|
||||
|
||||
private static void setRcloneProxy()
|
||||
{
|
||||
// Use the user's proxy settings if set, otherwise fallback to DNS fallback proxy if active
|
||||
string proxyUrl = DnsHelper.ProxyUrl;
|
||||
|
||||
if (settings.useProxy)
|
||||
{
|
||||
// Use user's configured proxy
|
||||
var url = $"http://{settings.ProxyAddress}:{settings.ProxyPort}";
|
||||
rclone.StartInfo.EnvironmentVariables["HTTP_PROXY"] = url;
|
||||
rclone.StartInfo.EnvironmentVariables["HTTPS_PROXY"] = url;
|
||||
rclone.StartInfo.EnvironmentVariables["http_proxy"] = url;
|
||||
rclone.StartInfo.EnvironmentVariables["https_proxy"] = url;
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(proxyUrl))
|
||||
{
|
||||
// Use our DNS-resolving proxy
|
||||
rclone.StartInfo.EnvironmentVariables["HTTP_PROXY"] = proxyUrl;
|
||||
rclone.StartInfo.EnvironmentVariables["HTTPS_PROXY"] = proxyUrl;
|
||||
rclone.StartInfo.EnvironmentVariables["http_proxy"] = proxyUrl;
|
||||
rclone.StartInfo.EnvironmentVariables["https_proxy"] = proxyUrl;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No proxy
|
||||
rclone.StartInfo.EnvironmentVariables.Remove("HTTP_PROXY");
|
||||
rclone.StartInfo.EnvironmentVariables.Remove("HTTPS_PROXY");
|
||||
rclone.StartInfo.EnvironmentVariables.Remove("http_proxy");
|
||||
rclone.StartInfo.EnvironmentVariables.Remove("https_proxy");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 398 B |
|
Before Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 476 B |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 17 KiB |
@@ -36,6 +36,39 @@ namespace AndroidSideloader
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
private Color disabled1, disabled2;
|
||||
private Color disabledStrokeColor;
|
||||
|
||||
public Color Disabled1
|
||||
{
|
||||
get => disabled1;
|
||||
set
|
||||
{
|
||||
disabled1 = value;
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
public Color Disabled2
|
||||
{
|
||||
get => disabled2;
|
||||
set
|
||||
{
|
||||
disabled2 = value;
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
public Color DisabledStrokeColor
|
||||
{
|
||||
get => disabledStrokeColor;
|
||||
set
|
||||
{
|
||||
disabledStrokeColor = value;
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#region RoundButton
|
||||
public RoundButton()
|
||||
@@ -48,7 +81,9 @@ namespace AndroidSideloader
|
||||
inactive2 = Color.FromArgb(33, 167, 188);
|
||||
active1 = Color.FromArgb(64, 168, 183);
|
||||
active2 = Color.FromArgb(36, 164, 183);
|
||||
|
||||
disabled1 = Color.FromArgb(32, 35, 45);
|
||||
disabled2 = Color.FromArgb(25, 28, 35);
|
||||
disabledStrokeColor = Color.FromArgb(50, 55, 65);
|
||||
|
||||
radius = 10;
|
||||
roundedRect = new RoundedRectangleF(Width, Height, radius);
|
||||
@@ -74,7 +109,7 @@ namespace AndroidSideloader
|
||||
#endregion
|
||||
|
||||
#region Drawing
|
||||
e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
|
||||
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
|
||||
roundedRect = new RoundedRectangleF(Width, Height, radius);
|
||||
e.Graphics.FillRectangle(Brushes.Transparent, ClientRectangle);
|
||||
|
||||
@@ -123,12 +158,18 @@ namespace AndroidSideloader
|
||||
}
|
||||
else
|
||||
{
|
||||
Color linear1 = Color.FromArgb(190, 190, 190);
|
||||
Color linear2 = Color.FromArgb(210, 210, 210);
|
||||
using (LinearGradientBrush inactiveGB = new LinearGradientBrush(rect, linear1, linear2, 90f))
|
||||
using (LinearGradientBrush disabledGB = new LinearGradientBrush(rect, disabled1, disabled2, 90f))
|
||||
{
|
||||
e.Graphics.FillPath(inactiveGB, roundedRect.Path);
|
||||
e.Graphics.DrawPath(new Pen(inactiveGB), roundedRect.Path);
|
||||
e.Graphics.FillPath(disabledGB, roundedRect.Path);
|
||||
}
|
||||
|
||||
if (stroke)
|
||||
{
|
||||
using (Pen pen = new Pen(disabledStrokeColor, 1))
|
||||
using (GraphicsPath path = new RoundedRectangleF(Width - (radius > 0 ? 0 : 1), Height - (radius > 0 ? 0 : 1), radius).Path)
|
||||
{
|
||||
e.Graphics.DrawPath(pen, path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
1235
SettingsForm.Designer.cs
generated
307
SettingsForm.cs
@@ -1,8 +1,10 @@
|
||||
using AndroidSideloader.Utilities;
|
||||
using AndroidSideloader.Properties;
|
||||
using AndroidSideloader.Utilities;
|
||||
using JR.Utils.GUI.Forms;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Forms;
|
||||
|
||||
@@ -11,9 +13,13 @@ namespace AndroidSideloader
|
||||
public partial class SettingsForm : Form
|
||||
{
|
||||
private static readonly SettingsManager _settings = SettingsManager.Instance;
|
||||
|
||||
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)
|
||||
@@ -25,37 +31,85 @@ namespace AndroidSideloader
|
||||
|
||||
private void initSettings()
|
||||
{
|
||||
checkForUpdatesCheckBox.Checked = _settings.CheckForUpdates;
|
||||
enableMessageBoxesCheckBox.Checked = _settings.EnableMessageBoxes;
|
||||
deleteAfterInstallCheckBox.Checked = _settings.DeleteAllAfterInstall;
|
||||
updateConfigCheckBox.Checked = _settings.AutoUpdateConfig;
|
||||
userJsonOnGameInstall.Checked = _settings.UserJsonOnGameInstall;
|
||||
nodevicemodeBox.Checked = _settings.NodeviceMode;
|
||||
bmbfBox.Checked = _settings.BMBFChecked;
|
||||
AutoReinstBox.Checked = _settings.AutoReinstall;
|
||||
trailersOn.Checked = _settings.TrailersOn;
|
||||
chkSingleThread.Checked = _settings.SingleThreadMode;
|
||||
virtualFilesystemCompatibilityCheckbox.Checked = _settings.VirtualFilesystemCompatibility;
|
||||
hideAdultContentCheckBox.Checked = _settings.HideAdultContent;
|
||||
// Use SetCheckedSilent to avoid triggering events during initialization
|
||||
toggleCheckForUpdates.SetCheckedSilent(_settings.CheckForUpdates);
|
||||
toggleMessageBoxes.SetCheckedSilent(_settings.EnableMessageBoxes);
|
||||
toggleDeleteAfterInstall.SetCheckedSilent(_settings.DeleteAllAfterInstall);
|
||||
toggleUpdateConfig.SetCheckedSilent(_settings.AutoUpdateConfig);
|
||||
toggleUserJson.SetCheckedSilent(_settings.UserJsonOnGameInstall);
|
||||
toggleNoDeviceMode.SetCheckedSilent(_settings.NodeviceMode);
|
||||
toggleBMBF.SetCheckedSilent(_settings.BMBFChecked);
|
||||
toggleAutoReinstall.SetCheckedSilent(_settings.AutoReinstall);
|
||||
toggleSingleThread.SetCheckedSilent(_settings.SingleThreadMode);
|
||||
toggleVirtualFilesystem.SetCheckedSilent(_settings.VirtualFilesystemCompatibility);
|
||||
toggleUseDownloadedFiles.SetCheckedSilent(_settings.UseDownloadedFiles);
|
||||
toggleTrailers.SetCheckedSilent(_settings.TrailersEnabled);
|
||||
bandwidthLimitTextBox.Text = _settings.BandwidthLimit.ToString();
|
||||
if (nodevicemodeBox.Checked)
|
||||
|
||||
// Handle no device mode disabling delete after install
|
||||
if (toggleNoDeviceMode.Checked)
|
||||
{
|
||||
deleteAfterInstallCheckBox.Checked = false;
|
||||
deleteAfterInstallCheckBox.Enabled = false;
|
||||
toggleDeleteAfterInstall.SetCheckedSilent(false);
|
||||
toggleDeleteAfterInstall.Enabled = false;
|
||||
lblDeleteAfterInstall.ForeColor = System.Drawing.Color.FromArgb(100, 100, 100);
|
||||
}
|
||||
chkUseDownloadedFiles.Checked = _settings.UseDownloadedFiles;
|
||||
|
||||
toggleProxy.Checked = _settings.useProxy;
|
||||
proxyAddressTextBox.Text = _settings.ProxyAddress;
|
||||
proxyPortTextBox.Text = _settings.ProxyPort;
|
||||
}
|
||||
|
||||
private void initToolTips()
|
||||
{
|
||||
ToolTip checkForUpdatesToolTip = new ToolTip();
|
||||
checkForUpdatesToolTip.SetToolTip(checkForUpdatesCheckBox, "If this is checked, the software will check for available updates");
|
||||
ToolTip enableMessageBoxesToolTip = new ToolTip();
|
||||
enableMessageBoxesToolTip.SetToolTip(enableMessageBoxesCheckBox, "If this is checked, the software will display message boxes after every completed task");
|
||||
ToolTip deleteAfterInstallToolTip = new ToolTip();
|
||||
deleteAfterInstallToolTip.SetToolTip(deleteAfterInstallCheckBox, "If this is checked, the software will delete all game files after downloading and installing a game from a remote server");
|
||||
ToolTip chkUseDownloadedFilesTooltip = new ToolTip();
|
||||
chkUseDownloadedFilesTooltip.SetToolTip(chkUseDownloadedFiles, "If this is checked, Rookie will always install Downloaded files without Re-Downloading or Asking to Re-Download");
|
||||
ToolTip toolTip = new ToolTip();
|
||||
toolTip.SetToolTip(toggleCheckForUpdates, "Check for available application updates on startup");
|
||||
toolTip.SetToolTip(lblCheckForUpdates, "Check for available application updates on startup");
|
||||
toolTip.SetToolTip(toggleMessageBoxes, "Show message boxes after every completed task");
|
||||
toolTip.SetToolTip(lblMessageBoxes, "Show message boxes after every completed task");
|
||||
toolTip.SetToolTip(toggleDeleteAfterInstall, "Delete game files after downloading and installing");
|
||||
toolTip.SetToolTip(lblDeleteAfterInstall, "Delete game files after downloading and installing");
|
||||
toolTip.SetToolTip(toggleUseDownloadedFiles, "Always install downloaded files without prompting to re-download");
|
||||
toolTip.SetToolTip(lblUseDownloadedFiles, "Always install downloaded files without prompting to re-download");
|
||||
toolTip.SetToolTip(toggleTrailers, "Show game trailers when selecting a game");
|
||||
toolTip.SetToolTip(lblTrailers, "Show game trailers when selecting a game");
|
||||
}
|
||||
|
||||
private void SaveAllSettings()
|
||||
{
|
||||
string input = bandwidthLimitTextBox.Text;
|
||||
Regex regex = new Regex(@"^\d+(\.\d+)?$");
|
||||
|
||||
if (regex.IsMatch(input) && float.TryParse(input, out float bandwidthLimit))
|
||||
{
|
||||
_settings.BandwidthLimit = bandwidthLimit;
|
||||
}
|
||||
|
||||
_settings.CheckForUpdates = toggleCheckForUpdates.Checked;
|
||||
_settings.EnableMessageBoxes = toggleMessageBoxes.Checked;
|
||||
_settings.DeleteAllAfterInstall = toggleDeleteAfterInstall.Checked;
|
||||
_settings.AutoUpdateConfig = toggleUpdateConfig.Checked;
|
||||
_settings.UserJsonOnGameInstall = toggleUserJson.Checked;
|
||||
_settings.NodeviceMode = toggleNoDeviceMode.Checked;
|
||||
_settings.BMBFChecked = toggleBMBF.Checked;
|
||||
_settings.AutoReinstall = toggleAutoReinstall.Checked;
|
||||
_settings.SingleThreadMode = toggleSingleThread.Checked;
|
||||
_settings.VirtualFilesystemCompatibility = toggleVirtualFilesystem.Checked;
|
||||
_settings.UseDownloadedFiles = toggleUseDownloadedFiles.Checked;
|
||||
_settings.TrailersEnabled = toggleTrailers.Checked;
|
||||
_settings.useProxy = toggleProxy.Checked;
|
||||
|
||||
if (Program.form != null)
|
||||
{
|
||||
Program.form.SetTrailerVisibility(toggleTrailers.Checked);
|
||||
Program.form.UpdateSideloadingUI();
|
||||
}
|
||||
|
||||
if (_settings.AutoUpdateConfig)
|
||||
{
|
||||
_settings.CreatePubMirrorFile = true;
|
||||
}
|
||||
|
||||
_settings.Save();
|
||||
}
|
||||
|
||||
public void btnUploadDebug_click(object sender, EventArgs e)
|
||||
@@ -88,77 +142,100 @@ namespace AndroidSideloader
|
||||
|
||||
private void applyButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
string input = bandwidthLimitTextBox.Text;
|
||||
// Parse bandwidth value
|
||||
var bandwidthInput = bandwidthLimitTextBox.Text;
|
||||
Regex regex = new Regex(@"^\d+(\.\d+)?$");
|
||||
|
||||
if (regex.IsMatch(input) && float.TryParse(input, out float bandwidthLimit))
|
||||
if (regex.IsMatch(bandwidthInput) && float.TryParse(bandwidthInput, out float bandwidthLimit))
|
||||
{
|
||||
_settings.BandwidthLimit = bandwidthLimit;
|
||||
_settings.Save();
|
||||
this.Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show("Please enter a valid number for the bandwidth limit.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse proxy values if proxy is enabled
|
||||
if (toggleProxy.Checked)
|
||||
{
|
||||
// Parse proxy address
|
||||
var proxyAddressInput = proxyAddressTextBox.Text;
|
||||
|
||||
if (proxyAddressInput.StartsWith("http://"))
|
||||
{
|
||||
proxyAddressInput = proxyAddressInput.Substring("http://".Length);
|
||||
}
|
||||
else if (proxyAddressInput.StartsWith("https://"))
|
||||
{
|
||||
proxyAddressInput = proxyAddressInput.Substring("https://".Length);
|
||||
}
|
||||
|
||||
if (proxyAddressInput.Equals("localhost", StringComparison.OrdinalIgnoreCase) ||
|
||||
IPAddress.TryParse(proxyAddressInput, out _))
|
||||
{
|
||||
_settings.ProxyAddress = proxyAddressInput;
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show("Please enter a valid address for the proxy.");
|
||||
}
|
||||
|
||||
// Parse proxy port
|
||||
var proxyPortInput = proxyPortTextBox.Text;
|
||||
|
||||
if (ushort.TryParse(proxyPortInput, out _))
|
||||
{
|
||||
_settings.ProxyPort = proxyPortInput;
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show("Please enter a valid port for the proxy.");
|
||||
}
|
||||
}
|
||||
|
||||
SaveAllSettings();
|
||||
this.Close();
|
||||
}
|
||||
|
||||
private void checkForUpdatesCheckBox_CheckedChanged(object sender, EventArgs e)
|
||||
private void toggleCheckForUpdates_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
_settings.CheckForUpdates = checkForUpdatesCheckBox.Checked;
|
||||
_settings.Save();
|
||||
// Settings saved on form close
|
||||
}
|
||||
|
||||
private void chkUseDownloadedFiles_CheckedChanged(object sender, EventArgs e)
|
||||
private void toggleUseDownloadedFiles_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
_settings.UseDownloadedFiles = chkUseDownloadedFiles.Checked;
|
||||
_settings.Save();
|
||||
// Settings saved on form close
|
||||
}
|
||||
|
||||
private void enableMessageBoxesCheckBox_CheckedChanged(object sender, EventArgs e)
|
||||
private void toggleMessageBoxes_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
_settings.EnableMessageBoxes = enableMessageBoxesCheckBox.Checked;
|
||||
_settings.Save();
|
||||
// Settings saved on form close
|
||||
}
|
||||
|
||||
private void toggleTrailers_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
// Settings saved on form close
|
||||
}
|
||||
|
||||
private void resetSettingsButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
// Reset the specific properties
|
||||
_settings.CustomDownloadDir = false;
|
||||
_settings.CustomBackupDir = false;
|
||||
|
||||
// Set backup folder and download directory
|
||||
MainForm.backupFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "Rookie Backups");
|
||||
_settings.DownloadDir = Environment.CurrentDirectory;
|
||||
_settings.CreatePubMirrorFile = true;
|
||||
|
||||
// Optionally, call initSettings if it needs to initialize anything based on these settings
|
||||
initSettings();
|
||||
|
||||
// Save the updated settings
|
||||
_settings.Save();
|
||||
this.Close();
|
||||
}
|
||||
|
||||
private void deleteAfterInstallCheckBox_CheckedChanged(object sender, EventArgs e)
|
||||
private void toggleDeleteAfterInstall_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
_settings.DeleteAllAfterInstall = deleteAfterInstallCheckBox.Checked;
|
||||
_settings.Save();
|
||||
// Settings saved on form close
|
||||
}
|
||||
|
||||
private void updateConfigCheckBox_CheckedChanged(object sender, EventArgs e)
|
||||
private void toggleUpdateConfig_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
_settings.AutoUpdateConfig = updateConfigCheckBox.Checked;
|
||||
if (_settings.AutoUpdateConfig)
|
||||
{
|
||||
_settings.CreatePubMirrorFile = true;
|
||||
}
|
||||
_settings.Save();
|
||||
// Settings saved on form close
|
||||
}
|
||||
|
||||
private void userJsonOnGameInstall_CheckedChanged(object sender, EventArgs e)
|
||||
private void toggleUserJson_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
_settings.UserJsonOnGameInstall = userJsonOnGameInstall.Checked;
|
||||
_settings.Save();
|
||||
// Settings saved on form close
|
||||
}
|
||||
|
||||
private void SettingsForm_KeyPress(object sender, KeyPressEventArgs e)
|
||||
@@ -192,58 +269,54 @@ namespace AndroidSideloader
|
||||
return base.ProcessDialogKey(keyData);
|
||||
}
|
||||
|
||||
private void nodevicemodeBox_CheckedChanged(object sender, EventArgs e)
|
||||
private void toggleNoDeviceMode_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
_settings.NodeviceMode = nodevicemodeBox.Checked;
|
||||
if (!nodevicemodeBox.Checked)
|
||||
// Update UI state only - settings saved on form close
|
||||
if (!toggleNoDeviceMode.Checked)
|
||||
{
|
||||
deleteAfterInstallCheckBox.Checked = true;
|
||||
_settings.DeleteAllAfterInstall = true;
|
||||
deleteAfterInstallCheckBox.Enabled = true;
|
||||
toggleDeleteAfterInstall.Checked = true;
|
||||
toggleDeleteAfterInstall.Enabled = true;
|
||||
lblDeleteAfterInstall.ForeColor = System.Drawing.Color.White;
|
||||
}
|
||||
else
|
||||
{
|
||||
deleteAfterInstallCheckBox.Checked = false;
|
||||
_settings.DeleteAllAfterInstall = false;
|
||||
deleteAfterInstallCheckBox.Enabled = false;
|
||||
toggleDeleteAfterInstall.SetCheckedSilent(false);
|
||||
toggleDeleteAfterInstall.Enabled = false;
|
||||
lblDeleteAfterInstall.ForeColor = System.Drawing.Color.FromArgb(100, 100, 100);
|
||||
}
|
||||
_settings.Save();
|
||||
}
|
||||
|
||||
private void bmbfBox_CheckedChanged(object sender, EventArgs e)
|
||||
private void toggleBMBF_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
_settings.BMBFChecked = bmbfBox.Checked;
|
||||
_settings.Save();
|
||||
// Settings saved on form close
|
||||
}
|
||||
|
||||
private void AutoReinstBox_CheckedChanged(object sender, EventArgs e)
|
||||
private void toggleAutoReinstall_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
_settings.AutoReinstall = AutoReinstBox.Checked;
|
||||
_settings.Save();
|
||||
// Settings saved on form close
|
||||
}
|
||||
|
||||
private void AutoReinstBox_Click(object sender, EventArgs e)
|
||||
private void toggleAutoReinstall_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (AutoReinstBox.Checked)
|
||||
if (toggleAutoReinstall.Checked)
|
||||
{
|
||||
DialogResult dialogResult = FlexibleMessageBox.Show(this, "WARNING: This box enables automatic reinstall when installs fail,\ndue to some games not allowing " +
|
||||
"access to their save data (less than 5%) this\noption can lead to losing your progress." +
|
||||
" However with this option\nchecked when installs fail you won't have to agree to a prompt to perform\nthe reinstall. " +
|
||||
"(ideal when installing from a queue).\n\nNOTE: If your usb/wireless adb connection is extremely slow this option can\ncause larger" +
|
||||
"apk file installations to fail. Enable anyway?", "WARNING", MessageBoxButtons.OKCancel);
|
||||
DialogResult dialogResult = FlexibleMessageBox.Show(this,
|
||||
"WARNING: This enables automatic reinstall when installs fail.\n\n" +
|
||||
"Some games (less than 5%) don't allow access to their save data, " +
|
||||
"which can lead to losing your progress.\n\n" +
|
||||
"However, with this option enabled, you won't have to confirm reinstalls manually " +
|
||||
"(ideal for queue installations).\n\n" +
|
||||
"NOTE: If your USB/wireless ADB connection is slow, this may cause " +
|
||||
"larger APK installations to fail.\n\nEnable anyway?",
|
||||
"WARNING", MessageBoxButtons.OKCancel);
|
||||
|
||||
if (dialogResult == DialogResult.Cancel)
|
||||
{
|
||||
AutoReinstBox.Checked = false;
|
||||
toggleAutoReinstall.SetCheckedSilent(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void trailersOn_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
_settings.TrailersOn = trailersOn.Checked;
|
||||
_settings.Save();
|
||||
}
|
||||
|
||||
private void btnOpenDebug_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (File.Exists($"{Environment.CurrentDirectory}\\debuglog.txt"))
|
||||
@@ -254,35 +327,44 @@ namespace AndroidSideloader
|
||||
|
||||
private void setDownloadDirectory_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (downloadDirectorySetter.ShowDialog() == DialogResult.OK)
|
||||
var dialog = new FolderSelectDialog
|
||||
{
|
||||
Title = "Select Download Folder",
|
||||
InitialDirectory = _settings.CustomDownloadDir && Directory.Exists(_settings.DownloadDir)
|
||||
? _settings.DownloadDir
|
||||
: Environment.CurrentDirectory
|
||||
};
|
||||
|
||||
if (dialog.Show(this.Handle))
|
||||
{
|
||||
_settings.CustomDownloadDir = true;
|
||||
_settings.DownloadDir = downloadDirectorySetter.SelectedPath;
|
||||
_settings.Save();
|
||||
_settings.DownloadDir = dialog.FileName;
|
||||
}
|
||||
}
|
||||
|
||||
private void setBackupDirectory_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (backupDirectorySetter.ShowDialog() == DialogResult.OK)
|
||||
var dialog = new FolderSelectDialog
|
||||
{
|
||||
Title = "Select Backup Folder",
|
||||
InitialDirectory = _settings.GetEffectiveBackupDir()
|
||||
};
|
||||
|
||||
if (dialog.Show(this.Handle))
|
||||
{
|
||||
_settings.CustomBackupDir = true;
|
||||
_settings.BackupDir = backupDirectorySetter.SelectedPath;
|
||||
MainForm.backupFolder = _settings.BackupDir;
|
||||
_settings.Save();
|
||||
_settings.BackupDir = dialog.FileName;
|
||||
}
|
||||
}
|
||||
|
||||
private void chkSingleThread_CheckedChanged(object sender, EventArgs e)
|
||||
private void toggleSingleThread_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
_settings.SingleThreadMode = chkSingleThread.Checked;
|
||||
_settings.Save();
|
||||
// Settings saved on form close
|
||||
}
|
||||
|
||||
private void virtualFilesystemCompatibilityCheckbox_CheckedChanged(object sender, EventArgs e)
|
||||
private void toggleVirtualFilesystem_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
_settings.VirtualFilesystemCompatibility = virtualFilesystemCompatibilityCheckbox.Checked;
|
||||
_settings.Save();
|
||||
// Settings saved on form close
|
||||
}
|
||||
|
||||
private void openDownloadDirectory_Click(object sender, EventArgs e)
|
||||
@@ -293,9 +375,7 @@ namespace AndroidSideloader
|
||||
|
||||
private void openBackupDirectory_Click(object sender, EventArgs e)
|
||||
{
|
||||
string pathToOpen = _settings.CustomBackupDir
|
||||
? Path.Combine(_settings.BackupDir, "Rookie Backups")
|
||||
: Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "Rookie Backups");
|
||||
string pathToOpen = _settings.GetEffectiveBackupDir();
|
||||
MainForm.OpenDirectory(pathToOpen);
|
||||
}
|
||||
|
||||
@@ -312,10 +392,9 @@ namespace AndroidSideloader
|
||||
}
|
||||
}
|
||||
|
||||
private void hideAdultContentCheckBox_CheckedChanged(object sender, EventArgs e)
|
||||
private void toggleProxy_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
_settings.HideAdultContent = hideAdultContentCheckBox.Checked;
|
||||
_settings.Save();
|
||||
// Settings saved on form close
|
||||
}
|
||||
}
|
||||
}
|
||||
8737
SettingsForm.resx
@@ -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
|
||||
{
|
||||
@@ -17,33 +18,6 @@ namespace AndroidSideloader
|
||||
public static string TempFolder = Path.Combine(Environment.CurrentDirectory, "temp");
|
||||
public static string CrashLogPath = "crashlog.txt";
|
||||
|
||||
public static void killWebView2()
|
||||
{
|
||||
var parentProcessId = Process.GetCurrentProcess().Id;
|
||||
var processes = Process.GetProcessesByName("msedgewebview2");
|
||||
|
||||
foreach (var process in processes)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (ManagementObject obj = new ManagementObject($"win32_process.handle='{process.Id}'"))
|
||||
{
|
||||
obj.Get();
|
||||
var ppid = Convert.ToInt32(obj["ParentProcessId"]);
|
||||
|
||||
if (ppid == parentProcessId)
|
||||
{
|
||||
process.Kill();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_ = Logger.Log($"Exception occured while attempting to shut down WebView2 with exception message: {ex.Message}", LogLevel.ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//push user.json to device (required for some devices like oculus quest)
|
||||
public static void PushUserJsons()
|
||||
{
|
||||
@@ -111,13 +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
|
||||
{
|
||||
@@ -125,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); }
|
||||
@@ -167,14 +149,7 @@ namespace AndroidSideloader
|
||||
|
||||
public static void BackupGame(string packagename)
|
||||
{
|
||||
if (!settings.CustomBackupDir)
|
||||
{
|
||||
MainForm.backupFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), $"Rookie Backups");
|
||||
}
|
||||
else
|
||||
{
|
||||
MainForm.backupFolder = Path.Combine((settings.BackupDir), $"Rookie Backups");
|
||||
}
|
||||
MainForm.backupFolder = settings.GetEffectiveBackupDir();
|
||||
if (!Directory.Exists(MainForm.backupFolder))
|
||||
{
|
||||
_ = Directory.CreateDirectory(MainForm.backupFolder);
|
||||
@@ -235,7 +210,7 @@ namespace AndroidSideloader
|
||||
|
||||
if (Directory.Exists($"{settings.MainDir}\\{packageName}"))
|
||||
{
|
||||
Directory.Delete($"{settings.MainDir}\\{packageName}", true);
|
||||
FileSystemUtilities.TryDeleteDirectory($"{settings.MainDir}\\{packageName}");
|
||||
}
|
||||
|
||||
_ = Directory.CreateDirectory($"{settings.MainDir}\\{packageName}");
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using JR.Utils.GUI.Forms;
|
||||
using AndroidSideloader.Utilities;
|
||||
using JR.Utils.GUI.Forms;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
@@ -30,7 +31,7 @@ namespace AndroidSideloader
|
||||
string resultString;
|
||||
|
||||
// Try fetching raw JSON data from the provided link
|
||||
HttpWebRequest getUrl = (HttpWebRequest)WebRequest.Create(configUrl);
|
||||
HttpWebRequest getUrl = DnsHelper.CreateWebRequest(configUrl);
|
||||
using (StreamReader responseReader = new StreamReader(getUrl.GetResponse().GetResponseStream()))
|
||||
{
|
||||
resultString = responseReader.ReadToEnd();
|
||||
@@ -44,7 +45,7 @@ namespace AndroidSideloader
|
||||
_ = Logger.Log($"Failed to update public config from main: {mainException.Message}, trying fallback.", LogLevel.ERROR);
|
||||
try
|
||||
{
|
||||
HttpWebRequest getUrl = (HttpWebRequest)WebRequest.Create(fallbackUrl);
|
||||
HttpWebRequest getUrl = DnsHelper.CreateWebRequest(fallbackUrl);
|
||||
using (StreamReader responseReader = new StreamReader(getUrl.GetResponse().GetResponseStream()))
|
||||
{
|
||||
string resultString = responseReader.ReadToEnd();
|
||||
@@ -60,11 +61,12 @@ namespace AndroidSideloader
|
||||
}
|
||||
}
|
||||
|
||||
// Download required dependencies.
|
||||
// Download required dependencies
|
||||
public static void downloadFiles()
|
||||
{
|
||||
MainForm.SplashScreen.UpdateBackgroundImage(AndroidSideloader.Properties.Resources.splashimage_deps);
|
||||
|
||||
// Initialize DNS helper early to detect and configure fallback if needed
|
||||
DnsHelper.Initialize();
|
||||
|
||||
WebClient client = new WebClient();
|
||||
ServicePointManager.Expect100Continue = true;
|
||||
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
|
||||
@@ -75,7 +77,7 @@ namespace AndroidSideloader
|
||||
{
|
||||
currentAccessedWebsite = "github";
|
||||
_ = Logger.Log($"Missing 'Sideloader Launcher.exe'. Attempting to download from {currentAccessedWebsite}");
|
||||
client.DownloadFile("https://github.com/VRPirates/rookie/raw/master/Sideloader%20Launcher.exe", "Sideloader Launcher.exe");
|
||||
DownloadFileWithDnsFallback(client, "https://github.com/VRPirates/rookie/raw/master/Sideloader%20Launcher.exe", "Sideloader Launcher.exe");
|
||||
_ = Logger.Log($"'Sideloader Launcher.exe' download successful");
|
||||
}
|
||||
|
||||
@@ -83,7 +85,7 @@ namespace AndroidSideloader
|
||||
{
|
||||
currentAccessedWebsite = "github";
|
||||
_ = Logger.Log($"Missing 'Rookie Offline.cmd'. Attempting to download from {currentAccessedWebsite}");
|
||||
client.DownloadFile("https://github.com/VRPirates/rookie/raw/master/Rookie%20Offline.cmd", "Rookie Offline.cmd");
|
||||
DownloadFileWithDnsFallback(client, "https://github.com/VRPirates/rookie/raw/master/Rookie%20Offline.cmd", "Rookie Offline.cmd");
|
||||
_ = Logger.Log($"'Rookie Offline.cmd' download successful");
|
||||
}
|
||||
|
||||
@@ -91,7 +93,7 @@ namespace AndroidSideloader
|
||||
{
|
||||
currentAccessedWebsite = "github";
|
||||
_ = Logger.Log($"Missing 'CleanupInstall.cmd'. Attempting to download from {currentAccessedWebsite}");
|
||||
client.DownloadFile("https://github.com/VRPirates/rookie/raw/master/CleanupInstall.cmd", "CleanupInstall.cmd");
|
||||
DownloadFileWithDnsFallback(client, "https://github.com/VRPirates/rookie/raw/master/CleanupInstall.cmd", "CleanupInstall.cmd");
|
||||
_ = Logger.Log($"'CleanupInstall.cmd' download successful");
|
||||
}
|
||||
|
||||
@@ -99,53 +101,128 @@ namespace AndroidSideloader
|
||||
{
|
||||
currentAccessedWebsite = "github";
|
||||
_ = Logger.Log($"Missing 'AddDefenderExceptions.ps1'. Attempting to download from {currentAccessedWebsite}");
|
||||
client.DownloadFile("https://github.com/VRPirates/rookie/raw/master/AddDefenderExceptions.ps1", "AddDefenderExceptions.ps1");
|
||||
DownloadFileWithDnsFallback(client, "https://github.com/VRPirates/rookie/raw/master/AddDefenderExceptions.ps1", "AddDefenderExceptions.ps1");
|
||||
_ = Logger.Log($"'AddDefenderExceptions.ps1' download successful");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_ = FlexibleMessageBox.Show($"You are unable to access raw.githubusercontent.com with the Exception:\n{ex.Message}\n\nSome files may be missing (Offline/Cleanup Script, Launcher)");
|
||||
_ = FlexibleMessageBox.Show(Program.form, $"You are unable to access raw.githubusercontent.com with the Exception:\n{ex.Message}\n\nSome files may be missing (Offline/Cleanup Script, Launcher)");
|
||||
}
|
||||
|
||||
string adbPath = Path.Combine(Environment.CurrentDirectory, "platform-tools", "adb.exe");
|
||||
string platformToolsDir = Path.Combine(Environment.CurrentDirectory, "platform-tools");
|
||||
|
||||
try
|
||||
{
|
||||
if (!File.Exists($"{Path.GetPathRoot(Environment.SystemDirectory)}RSL\\platform-tools\\adb.exe")) //if adb is not updated, download and auto extract
|
||||
if (!File.Exists(adbPath)) //if adb is not updated, download and auto extract
|
||||
{
|
||||
if (!Directory.Exists($"{Path.GetPathRoot(Environment.SystemDirectory)}RSL\\platform-tools"))
|
||||
if (!Directory.Exists(platformToolsDir))
|
||||
{
|
||||
_ = Directory.CreateDirectory($"{Path.GetPathRoot(Environment.SystemDirectory)}RSL\\platform-tools");
|
||||
_ = Directory.CreateDirectory(platformToolsDir);
|
||||
}
|
||||
|
||||
currentAccessedWebsite = "github";
|
||||
_ = Logger.Log($"Missing adb within {Path.GetPathRoot(Environment.SystemDirectory)}RSL\\platform-tools. Attempting to download from {currentAccessedWebsite}");
|
||||
client.DownloadFile("https://github.com/VRPirates/rookie/raw/master/dependencies.7z", "dependencies.7z");
|
||||
Utilities.Zip.ExtractFile(Path.Combine(Environment.CurrentDirectory, "dependencies.7z"), $"{Path.GetPathRoot(Environment.SystemDirectory)}RSL\\platform-tools");
|
||||
_ = Logger.Log($"Missing adb within {platformToolsDir}. Attempting to download from {currentAccessedWebsite}");
|
||||
DownloadFileWithDnsFallback(client, "https://github.com/VRPirates/rookie/raw/master/dependencies.7z", "dependencies.7z");
|
||||
Utilities.Zip.ExtractFile(Path.Combine(Environment.CurrentDirectory, "dependencies.7z"), platformToolsDir);
|
||||
File.Delete("dependencies.7z");
|
||||
_ = Logger.Log($"adb download successful");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_ = FlexibleMessageBox.Show($"You are unable to access raw.githubusercontent.com page with the Exception:\n{ex.Message}\n\nSome files may be missing (ADB)");
|
||||
_ = FlexibleMessageBox.Show("ADB was unable to be downloaded\nRookie will now close.");
|
||||
_ = FlexibleMessageBox.Show(Program.form, $"You are unable to access raw.githubusercontent.com page with the Exception:\n{ex.Message}\n\nSome files may be missing (ADB)");
|
||||
_ = FlexibleMessageBox.Show(Program.form, "ADB was unable to be downloaded\nRookie will now close.");
|
||||
Application.Exit();
|
||||
}
|
||||
|
||||
string wantedRcloneVersion = "1.68.2";
|
||||
string wantedRcloneVersion = "1.72.1";
|
||||
bool rcloneSuccess = false;
|
||||
|
||||
rcloneSuccess = downloadRclone(wantedRcloneVersion, false);
|
||||
if (!rcloneSuccess) {
|
||||
if (!rcloneSuccess)
|
||||
{
|
||||
rcloneSuccess = downloadRclone(wantedRcloneVersion, true);
|
||||
}
|
||||
if (!rcloneSuccess) {
|
||||
if (!rcloneSuccess)
|
||||
{
|
||||
_ = Logger.Log($"Unable to download rclone", LogLevel.ERROR);
|
||||
_ = FlexibleMessageBox.Show("Rclone was unable to be downloaded\nRookie will now close, please use Offline Mode for manual sideloading if needed");
|
||||
_ = FlexibleMessageBox.Show(Program.form, "Rclone was unable to be downloaded\nRookie will now close, please use Offline Mode for manual sideloading if needed");
|
||||
Application.Exit();
|
||||
}
|
||||
|
||||
// Download WebView2 runtime if needed
|
||||
downloadWebView2Runtime();
|
||||
}
|
||||
|
||||
// Downloads a file using the DNS fallback proxy if active
|
||||
public static void DownloadFileWithDnsFallback(WebClient client, string url, string localPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Use DNS fallback proxy if active
|
||||
if (DnsHelper.UseFallbackDns && !string.IsNullOrEmpty(DnsHelper.ProxyUrl))
|
||||
{
|
||||
client.Proxy = new WebProxy(DnsHelper.ProxyUrl);
|
||||
}
|
||||
|
||||
client.DownloadFile(url, localPath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_ = Logger.Log($"Download failed for {url}: {ex.Message}", LogLevel.ERROR);
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Reset proxy to avoid affecting other operations
|
||||
client.Proxy = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Overload that creates its own WebClient for convenience
|
||||
public static void DownloadFileWithDnsFallback(string url, string localPath)
|
||||
{
|
||||
using (var client = new WebClient())
|
||||
{
|
||||
ServicePointManager.Expect100Continue = true;
|
||||
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
|
||||
DownloadFileWithDnsFallback(client, url, localPath);
|
||||
}
|
||||
}
|
||||
|
||||
// Downloads WebView2 runtime if not present
|
||||
private static void downloadWebView2Runtime()
|
||||
{
|
||||
string runtimesPath = Path.Combine(Environment.CurrentDirectory, "runtimes");
|
||||
string webView2LoaderArm64 = Path.Combine(runtimesPath, "win-arm64", "native", "WebView2Loader.dll");
|
||||
string webView2LoaderX86 = Path.Combine(runtimesPath, "win-x86", "native", "WebView2Loader.dll");
|
||||
string webView2LoaderX64 = Path.Combine(runtimesPath, "win-x64", "native", "WebView2Loader.dll");
|
||||
|
||||
bool runtimeExists = File.Exists(webView2LoaderX86) || File.Exists(webView2LoaderX64) || File.Exists(webView2LoaderArm64);
|
||||
|
||||
if (!runtimeExists)
|
||||
{
|
||||
try
|
||||
{
|
||||
_ = Logger.Log("Missing WebView2 runtime. Attempting to download...");
|
||||
string archivePath = Path.Combine(Environment.CurrentDirectory, "runtimes.7z");
|
||||
|
||||
DownloadFileWithDnsFallback("https://vrpirates.wiki/downloads/runtimes.7z", archivePath);
|
||||
|
||||
_ = Logger.Log("Extracting WebView2 runtime...");
|
||||
Utilities.Zip.ExtractFile(archivePath, Environment.CurrentDirectory);
|
||||
File.Delete(archivePath);
|
||||
_ = Logger.Log("WebView2 runtime download successful");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_ = Logger.Log($"Failed to download WebView2 runtime: {ex.Message}", LogLevel.ERROR);
|
||||
// Don't show message box here - let CreateEnvironment handle the UI feedback
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool downloadRclone(string wantedRcloneVersion, bool useFallback = false)
|
||||
{
|
||||
@@ -174,12 +251,15 @@ namespace AndroidSideloader
|
||||
_ = Logger.Log($"RCLONE Version does not match ({currentRcloneVersion})! Downloading required version ({wantedRcloneVersion})");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
updateRclone = true;
|
||||
_ = Logger.Log($"RCLONE exe does not exist, attempting to download");
|
||||
}
|
||||
|
||||
if (!Directory.Exists(dirRclone)) {
|
||||
if (!Directory.Exists(dirRclone))
|
||||
{
|
||||
updateRclone = true;
|
||||
_ = Logger.Log($"Missing RCLONE Folder, attempting to download");
|
||||
|
||||
@@ -200,18 +280,17 @@ namespace AndroidSideloader
|
||||
hasConfig = true;
|
||||
}
|
||||
|
||||
MainForm.SplashScreen.UpdateBackgroundImage(AndroidSideloader.Properties.Resources.splashimage_rclone);
|
||||
|
||||
string architecture = Environment.Is64BitOperatingSystem ? "amd64" : "386";
|
||||
string url = $"https://downloads.rclone.org/v{wantedRcloneVersion}/rclone-v{wantedRcloneVersion}-windows-{architecture}.zip";
|
||||
if (useFallback == true) {
|
||||
if (useFallback == true)
|
||||
{
|
||||
_ = Logger.Log($"Using git fallback for rclone download");
|
||||
url = $"https://raw.githubusercontent.com/VRPirates/rookie/master/dep/rclone-v{wantedRcloneVersion}-windows-{architecture}.zip";
|
||||
}
|
||||
_ = Logger.Log($"Downloading rclone from {url}");
|
||||
|
||||
_ = Logger.Log("Begin download rclone");
|
||||
client.DownloadFile(url, "rclone.zip");
|
||||
DownloadFileWithDnsFallback(client, url, "rclone.zip");
|
||||
_ = Logger.Log("Complete download rclone");
|
||||
|
||||
_ = Logger.Log($"Extract {Environment.CurrentDirectory}\\rclone.zip");
|
||||
@@ -230,7 +309,7 @@ namespace AndroidSideloader
|
||||
}
|
||||
File.Move(file, destFile);
|
||||
}
|
||||
Directory.Delete(dirExtractedRclone, true);
|
||||
FileSystemUtilities.TryDeleteDirectory(dirExtractedRclone);
|
||||
|
||||
// Restore vrp.download.config if it was backed up
|
||||
if (hasConfig && File.Exists(tempConfigPath))
|
||||
@@ -251,4 +330,4 @@ namespace AndroidSideloader
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +1,19 @@
|
||||
using AndroidSideloader.Utilities;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace AndroidSideloader
|
||||
{
|
||||
internal class rcloneFolder
|
||||
{
|
||||
public string Path { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Size { get; set; }
|
||||
public string ModTime { get; set; }
|
||||
}
|
||||
|
||||
internal class SideloaderRCLONE
|
||||
{
|
||||
public static List<string> RemotesList = new List<string>();
|
||||
|
||||
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;
|
||||
@@ -32,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
|
||||
@@ -77,46 +67,91 @@ namespace AndroidSideloader
|
||||
{
|
||||
try
|
||||
{
|
||||
_ = Logger.Log($"Extracting Metadata");
|
||||
Zip.ExtractFile(Path.Combine(Environment.CurrentDirectory, "meta.7z"), Path.Combine(Environment.CurrentDirectory, "meta"),
|
||||
MainForm.PublicConfigFile.Password);
|
||||
var sw = Stopwatch.StartNew();
|
||||
|
||||
_ = Logger.Log($"Updating Metadata");
|
||||
string currentDir = Environment.CurrentDirectory;
|
||||
string metaRoot = Path.Combine(currentDir, "meta");
|
||||
string metaArchive = Path.Combine(currentDir, "meta.7z");
|
||||
string metaDotMeta = Path.Combine(metaRoot, ".meta");
|
||||
|
||||
if (Directory.Exists(Nouns))
|
||||
// Check if archive exists and is newer than existing metadata
|
||||
if (!File.Exists(metaArchive))
|
||||
{
|
||||
Directory.Delete(Nouns, true);
|
||||
Logger.Log("meta.7z not found, skipping extraction", LogLevel.WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Directory.Exists(ThumbnailsFolder))
|
||||
// Skip extraction if metadata is already up-to-date (based on file timestamps)
|
||||
string gameListPath = Path.Combine(metaRoot, "VRP-GameList.txt");
|
||||
if (File.Exists(gameListPath))
|
||||
{
|
||||
Directory.Delete(ThumbnailsFolder, true);
|
||||
}
|
||||
var archiveTime = File.GetLastWriteTimeUtc(metaArchive);
|
||||
var gameListTime = File.GetLastWriteTimeUtc(gameListPath);
|
||||
|
||||
if (Directory.Exists(NotesFolder))
|
||||
{
|
||||
Directory.Delete(NotesFolder, true);
|
||||
}
|
||||
|
||||
Directory.Move(Path.Combine(Environment.CurrentDirectory, "meta", ".meta", "nouns"), Nouns);
|
||||
Directory.Move(Path.Combine(Environment.CurrentDirectory, "meta", ".meta", "thumbnails"), ThumbnailsFolder);
|
||||
Directory.Move(Path.Combine(Environment.CurrentDirectory, "meta", ".meta", "notes"), NotesFolder);
|
||||
|
||||
_ = Logger.Log($"Initializing Games List");
|
||||
string gameList = File.ReadAllText(Path.Combine(Environment.CurrentDirectory, "meta", "VRP-GameList.txt"));
|
||||
|
||||
string[] splitList = gameList.Split('\n');
|
||||
splitList = splitList.Skip(1).ToArray();
|
||||
foreach (string game in splitList)
|
||||
{
|
||||
if (game.Length > 1)
|
||||
// If game list is newer than archive, skip extraction
|
||||
if (gameListTime > archiveTime && games.Count > 0)
|
||||
{
|
||||
string[] splitGame = game.Split(';');
|
||||
games.Add(splitGame);
|
||||
Logger.Log($"Metadata already up-to-date, skipping extraction");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Directory.Delete(Path.Combine(Environment.CurrentDirectory, "meta"), true);
|
||||
_ = Logger.Log($"Extracting Metadata");
|
||||
Zip.ExtractFile(metaArchive, metaRoot, MainForm.PublicConfigFile.Password);
|
||||
Logger.Log($"Extraction completed in {sw.ElapsedMilliseconds}ms");
|
||||
sw.Restart();
|
||||
|
||||
_ = Logger.Log($"Updating Metadata");
|
||||
|
||||
// Use Parallel.Invoke for independent directory operations
|
||||
System.Threading.Tasks.Parallel.Invoke(
|
||||
() => SafeDeleteDirectory(Nouns),
|
||||
() => SafeDeleteDirectory(ThumbnailsFolder),
|
||||
() => SafeDeleteDirectory(NotesFolder)
|
||||
);
|
||||
Logger.Log($"Directory cleanup in {sw.ElapsedMilliseconds}ms");
|
||||
sw.Restart();
|
||||
|
||||
// Move directories
|
||||
MoveIfExists(Path.Combine(metaDotMeta, "nouns"), Nouns);
|
||||
MoveIfExists(Path.Combine(metaDotMeta, "thumbnails"), ThumbnailsFolder);
|
||||
MoveIfExists(Path.Combine(metaDotMeta, "notes"), NotesFolder);
|
||||
Logger.Log($"Directory moves in {sw.ElapsedMilliseconds}ms");
|
||||
sw.Restart();
|
||||
|
||||
_ = Logger.Log($"Initializing Games List");
|
||||
|
||||
gameListPath = Path.Combine(metaRoot, "VRP-GameList.txt");
|
||||
if (File.Exists(gameListPath))
|
||||
{
|
||||
// Read all lines at once - faster for files that fit in memory
|
||||
var lines = File.ReadAllLines(gameListPath);
|
||||
var newGames = new List<string[]>(lines.Length);
|
||||
|
||||
for (int i = 1; i < lines.Length; i++) // Skip header
|
||||
{
|
||||
var line = lines[i];
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
continue;
|
||||
|
||||
var splitGame = line.Split(';');
|
||||
if (splitGame.Length > 1)
|
||||
{
|
||||
newGames.Add(splitGame);
|
||||
}
|
||||
}
|
||||
|
||||
// Atomic swap
|
||||
games.Clear();
|
||||
games.AddRange(newGames);
|
||||
Logger.Log($"Parsed {games.Count} games in {sw.ElapsedMilliseconds}ms");
|
||||
}
|
||||
else
|
||||
{
|
||||
_ = Logger.Log("VRP-GameList.txt not found in extracted metadata.", LogLevel.WARNING);
|
||||
}
|
||||
|
||||
SafeDeleteDirectory(metaRoot);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -125,125 +160,49 @@ namespace AndroidSideloader
|
||||
}
|
||||
}
|
||||
|
||||
public static void RefreshRemotes()
|
||||
{
|
||||
_ = Logger.Log($"Refresh / List Remotes");
|
||||
RemotesList.Clear();
|
||||
string[] remotes = RCLONE.runRcloneCommand_DownloadConfig("listremotes").Output.Split('\n');
|
||||
|
||||
_ = Logger.Log("Loaded following remotes: ");
|
||||
foreach (string r in remotes)
|
||||
{
|
||||
if (r.Length > 1)
|
||||
{
|
||||
string remote = r.Remove(r.Length - 1);
|
||||
if (remote.Contains("mirror"))
|
||||
{
|
||||
_ = Logger.Log(remote);
|
||||
RemotesList.Add(remote);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void initGames(string remote)
|
||||
{
|
||||
_ = Logger.Log($"Initializing Games List");
|
||||
|
||||
gameProperties.Clear();
|
||||
games.Clear();
|
||||
|
||||
// Fetch once, then process as lines
|
||||
string tempGameList = RCLONE.runRcloneCommand_DownloadConfig($"cat \"{remote}:{RcloneGamesFolder}/VRP-GameList.txt\"").Output;
|
||||
if (MainForm.debugMode)
|
||||
{
|
||||
File.WriteAllText("VRP-GamesList.txt", tempGameList);
|
||||
}
|
||||
if (!tempGameList.Equals(""))
|
||||
{
|
||||
string[] gameListSplited = tempGameList.Split(new[] { '\n' });
|
||||
gameListSplited = gameListSplited.Skip(1).ToArray();
|
||||
foreach (string game in gameListSplited)
|
||||
// Avoid redundant disk I/O: write only if non-empty
|
||||
if (!string.IsNullOrEmpty(tempGameList))
|
||||
{
|
||||
if (game.Length > 1)
|
||||
File.WriteAllText("VRP-GamesList.txt", tempGameList);
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(tempGameList))
|
||||
{
|
||||
bool isFirstLine = true;
|
||||
foreach (var line in SplitLines(tempGameList))
|
||||
{
|
||||
if (isFirstLine)
|
||||
{
|
||||
isFirstLine = false; // skip header
|
||||
continue;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var splitGame = line.Split(new[] { ';' }, StringSplitOptions.None);
|
||||
if (splitGame.Length > 1)
|
||||
{
|
||||
string[] splitGame = game.Split(';');
|
||||
games.Add(splitGame);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void updateDownloadConfig()
|
||||
{
|
||||
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls
|
||||
| SecurityProtocolType.Tls11
|
||||
| SecurityProtocolType.Tls12
|
||||
| SecurityProtocolType.Ssl3;
|
||||
_ = Logger.Log($"Attempting to Update Download Config");
|
||||
|
||||
string downloadConfigFilename = "vrp.download.config";
|
||||
|
||||
try
|
||||
{
|
||||
string configUrl = $"https://vrpirates.wiki/downloads/{downloadConfigFilename}";
|
||||
|
||||
HttpWebRequest getUrl = (HttpWebRequest)WebRequest.Create(configUrl);
|
||||
using (StreamReader responseReader = new StreamReader(getUrl.GetResponse().GetResponseStream()))
|
||||
{
|
||||
string resultString = responseReader.ReadToEnd();
|
||||
|
||||
_ = Logger.Log($"Retrieved updated config from: {configUrl}");
|
||||
|
||||
if (File.Exists(Path.Combine(Environment.CurrentDirectory, "rclone", $"{downloadConfigFilename}_new")))
|
||||
{
|
||||
File.Delete(Path.Combine(Environment.CurrentDirectory, "rclone", $"{downloadConfigFilename}_new"));
|
||||
}
|
||||
|
||||
File.Create(Path.Combine(Environment.CurrentDirectory, "rclone", $"{downloadConfigFilename}_new")).Close();
|
||||
File.WriteAllText(Path.Combine(Environment.CurrentDirectory, "rclone", $"{downloadConfigFilename}_new"), resultString);
|
||||
|
||||
if (!File.Exists(Path.Combine(Environment.CurrentDirectory, "rclone", "hash.txt")))
|
||||
{
|
||||
File.Create(Path.Combine(Environment.CurrentDirectory, "rclone", "hash.txt")).Close();
|
||||
}
|
||||
|
||||
string newConfig = CalculateMD5(Path.Combine(Environment.CurrentDirectory, "rclone", $"{downloadConfigFilename}_new"));
|
||||
string oldConfig = File.ReadAllText(Path.Combine(Environment.CurrentDirectory, "rclone", "hash.txt"));
|
||||
|
||||
if (!File.Exists(Path.Combine(Environment.CurrentDirectory, "rclone", $"{downloadConfigFilename}")))
|
||||
{
|
||||
oldConfig = "Config Doesnt Exist!";
|
||||
}
|
||||
|
||||
_ = Logger.Log($"Online Config Hash: {newConfig}; Local Config Hash: {oldConfig}");
|
||||
|
||||
if (newConfig != oldConfig)
|
||||
{
|
||||
_ = Logger.Log($"Updated Config Hash is different than the current Config. Updating Configuration File.");
|
||||
|
||||
if (File.Exists(Path.Combine(Environment.CurrentDirectory, "rclone", $"{downloadConfigFilename}")))
|
||||
{
|
||||
File.Delete(Path.Combine(Environment.CurrentDirectory, "rclone", $"{downloadConfigFilename}"));
|
||||
}
|
||||
|
||||
File.Move(Path.Combine(Environment.CurrentDirectory, "rclone", $"{downloadConfigFilename}_new"), Path.Combine(Environment.CurrentDirectory, "rclone", $"{downloadConfigFilename}"));
|
||||
|
||||
File.WriteAllText(Path.Combine(Environment.CurrentDirectory, "rclone", "hash.txt"), string.Empty);
|
||||
File.WriteAllText(Path.Combine(Environment.CurrentDirectory, "rclone", "hash.txt"), newConfig);
|
||||
}
|
||||
else
|
||||
{
|
||||
_ = Logger.Log($"Updated Config Hash matches last download. Not updating.");
|
||||
|
||||
if (File.Exists(Path.Combine(Environment.CurrentDirectory, "rclone", $"{downloadConfigFilename}_new")))
|
||||
{
|
||||
File.Delete(Path.Combine(Environment.CurrentDirectory, "rclone", $"{downloadConfigFilename}g_new"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
public static void updateUploadConfig()
|
||||
{
|
||||
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls
|
||||
@@ -255,14 +214,19 @@ namespace AndroidSideloader
|
||||
{
|
||||
string configUrl = "https://vrpirates.wiki/downloads/vrp.upload.config";
|
||||
|
||||
HttpWebRequest getUrl = (HttpWebRequest)WebRequest.Create(configUrl);
|
||||
using (StreamReader responseReader = new StreamReader(getUrl.GetResponse().GetResponseStream()))
|
||||
// Use DnsHelper for fallback DNS support
|
||||
var getUrl = DnsHelper.CreateWebRequest(configUrl);
|
||||
using (var response = getUrl.GetResponse())
|
||||
using (var stream = response.GetResponseStream())
|
||||
using (var responseReader = new StreamReader(stream))
|
||||
{
|
||||
string resultString = responseReader.ReadToEnd();
|
||||
|
||||
_ = Logger.Log($"Retrieved updated config from: {configUrl}");
|
||||
|
||||
File.WriteAllText(Path.Combine(Environment.CurrentDirectory, "rclone", "vrp.upload.config"), resultString);
|
||||
// Avoid multiple combines; write once
|
||||
string uploadConfigPath = Path.Combine(Environment.CurrentDirectory, "rclone", "vrp.upload.config");
|
||||
File.WriteAllText(uploadConfigPath, resultString);
|
||||
|
||||
_ = Logger.Log("Upload config updated successfully.");
|
||||
}
|
||||
@@ -273,14 +237,94 @@ namespace AndroidSideloader
|
||||
}
|
||||
}
|
||||
|
||||
private static string CalculateMD5(string filename)
|
||||
// Fast directory delete using Windows cmd - faster than .NET's Directory.Delete
|
||||
// for large directories with many files (e.g., thumbnails folder with 1000+ images)
|
||||
private static void SafeDeleteDirectory(string path)
|
||||
{
|
||||
using (MD5 md5 = MD5.Create())
|
||||
// Avoid exceptions when directory is missing
|
||||
if (!Directory.Exists(path))
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
using (FileStream stream = File.OpenRead(filename))
|
||||
// Use Windows rd command which is ~10x faster than .NET's recursive delete
|
||||
var psi = new ProcessStartInfo
|
||||
{
|
||||
byte[] hash = md5.ComputeHash(stream);
|
||||
return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
|
||||
FileName = "cmd.exe",
|
||||
Arguments = $"/c rd /s /q \"{path}\"",
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true
|
||||
};
|
||||
|
||||
using (var process = Process.Start(psi))
|
||||
{
|
||||
// Wait with timeout to prevent hanging
|
||||
if (!process.WaitForExit(30000)) // 30 second timeout
|
||||
{
|
||||
try { process.Kill(); } catch { }
|
||||
Logger.Log($"Directory delete timed out for: {path}", LogLevel.WARNING);
|
||||
// Fallback to .NET delete
|
||||
FallbackDelete(path);
|
||||
}
|
||||
else if (process.ExitCode != 0 && Directory.Exists(path))
|
||||
{
|
||||
// Command failed, try fallback
|
||||
FallbackDelete(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Log($"Fast delete failed for {path}: {ex.Message}", LogLevel.WARNING);
|
||||
// Fallback to standard .NET delete
|
||||
FallbackDelete(path);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback delete method using standard .NET
|
||||
private static void FallbackDelete(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(path))
|
||||
{
|
||||
FileSystemUtilities.TryDeleteDirectory(path);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Log($"Fallback delete also failed for {path}: {ex.Message}", LogLevel.ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
// Move directory only if source exists
|
||||
private static void MoveIfExists(string sourceDir, string destDir)
|
||||
{
|
||||
if (Directory.Exists(sourceDir))
|
||||
{
|
||||
// Ensure destination does not exist to prevent IOException
|
||||
// Use fast delete method
|
||||
SafeDeleteDirectory(destDir);
|
||||
Directory.Move(sourceDir, destDir);
|
||||
}
|
||||
else
|
||||
{
|
||||
_ = Logger.Log($"Source directory not found: {sourceDir}", LogLevel.WARNING);
|
||||
}
|
||||
}
|
||||
|
||||
// Efficient, cross-platform line splitting for string buffers
|
||||
private static IEnumerable<string> SplitLines(string s)
|
||||
{
|
||||
// Handle both \r\n and \n without allocating intermediate arrays
|
||||
using (var reader = new StringReader(s))
|
||||
{
|
||||
string line;
|
||||
while ((line = reader.ReadLine()) != null)
|
||||
{
|
||||
yield return line;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
59
Splash.Designer.cs
generated
@@ -1,59 +0,0 @@
|
||||
namespace AndroidSideloader
|
||||
{
|
||||
partial class Splash
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Splash));
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// Splash
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("$this.BackgroundImage")));
|
||||
this.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch;
|
||||
this.CausesValidation = false;
|
||||
this.ClientSize = new System.Drawing.Size(427, 250);
|
||||
this.ControlBox = false;
|
||||
this.DoubleBuffered = true;
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
|
||||
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
|
||||
this.Margin = new System.Windows.Forms.Padding(2);
|
||||
this.Name = "Splash";
|
||||
this.ShowIcon = false;
|
||||
this.ShowInTaskbar = false;
|
||||
this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||
this.Text = "Splash";
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
19
Splash.cs
@@ -1,19 +0,0 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace AndroidSideloader
|
||||
{
|
||||
public partial class Splash : Form
|
||||
{
|
||||
public Splash()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public void UpdateBackgroundImage(Image newImage)
|
||||
{
|
||||
this.BackgroundImage = newImage;
|
||||
}
|
||||
}
|
||||
}
|
||||
7261
Splash.resx
264
ToggleSwitch.cs
Normal file
@@ -0,0 +1,264 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace AndroidSideloader
|
||||
{
|
||||
/// <summary>
|
||||
/// An iOS-style toggle switch control with smooth animation.
|
||||
/// </summary>
|
||||
public class ToggleSwitch : Control
|
||||
{
|
||||
private bool _checked;
|
||||
private bool _isHovered;
|
||||
private float _animationProgress; // 0 = off, 1 = on
|
||||
private Timer _animationTimer;
|
||||
private const int AnimationDuration = 80; // ms
|
||||
private const int AnimationInterval = 8; // ~120fps
|
||||
private float _animationStep;
|
||||
|
||||
// Colors
|
||||
private Color _onColor = Color.FromArgb(93, 203, 173);
|
||||
private Color _offColor = Color.FromArgb(60, 65, 75);
|
||||
private Color _thumbColor = Color.White;
|
||||
private Color _onHoverColor = Color.FromArgb(110, 215, 190);
|
||||
private Color _offHoverColor = Color.FromArgb(75, 80, 90);
|
||||
|
||||
public event EventHandler CheckedChanged;
|
||||
|
||||
public ToggleSwitch()
|
||||
{
|
||||
SetStyle(ControlStyles.AllPaintingInWmPaint |
|
||||
ControlStyles.UserPaint |
|
||||
ControlStyles.OptimizedDoubleBuffer |
|
||||
ControlStyles.ResizeRedraw |
|
||||
ControlStyles.SupportsTransparentBackColor |
|
||||
ControlStyles.StandardClick |
|
||||
ControlStyles.StandardDoubleClick, true);
|
||||
|
||||
// Disable double-click so rapid clicks are treated as separate clicks
|
||||
SetStyle(ControlStyles.StandardDoubleClick, false);
|
||||
|
||||
Size = new Size(44, 24);
|
||||
Cursor = Cursors.Hand;
|
||||
BackColor = Color.Transparent;
|
||||
|
||||
_animationTimer = new Timer { Interval = AnimationInterval };
|
||||
_animationTimer.Tick += AnimationTimer_Tick;
|
||||
_animationStep = (float)AnimationInterval / AnimationDuration;
|
||||
}
|
||||
|
||||
[Category("Appearance")]
|
||||
[Description("Gets or sets whether the toggle is in the 'on' state.")]
|
||||
[DefaultValue(false)]
|
||||
public bool Checked
|
||||
{
|
||||
get => _checked;
|
||||
set
|
||||
{
|
||||
if (_checked != value)
|
||||
{
|
||||
_checked = value;
|
||||
StartAnimation();
|
||||
OnCheckedChanged(EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the checked state without triggering animation or events.
|
||||
/// Using this for initial state setup.
|
||||
/// </summary>
|
||||
public void SetCheckedSilent(bool value)
|
||||
{
|
||||
_checked = value;
|
||||
_animationProgress = value ? 1f : 0f;
|
||||
_animationTimer.Stop();
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
[Category("Appearance")]
|
||||
[Description("The color of the toggle when it is on.")]
|
||||
public Color OnColor
|
||||
{
|
||||
get => _onColor;
|
||||
set { _onColor = value; Invalidate(); }
|
||||
}
|
||||
|
||||
[Category("Appearance")]
|
||||
[Description("The color of the toggle when it is off.")]
|
||||
public Color OffColor
|
||||
{
|
||||
get => _offColor;
|
||||
set { _offColor = value; Invalidate(); }
|
||||
}
|
||||
|
||||
[Category("Appearance")]
|
||||
[Description("The color of the thumb (circle).")]
|
||||
public Color ThumbColor
|
||||
{
|
||||
get => _thumbColor;
|
||||
set { _thumbColor = value; Invalidate(); }
|
||||
}
|
||||
|
||||
protected virtual void OnCheckedChanged(EventArgs e)
|
||||
{
|
||||
CheckedChanged?.Invoke(this, e);
|
||||
}
|
||||
|
||||
private void StartAnimation()
|
||||
{
|
||||
if (!_animationTimer.Enabled)
|
||||
{
|
||||
_animationTimer.Start();
|
||||
}
|
||||
}
|
||||
|
||||
private void AnimationTimer_Tick(object sender, EventArgs e)
|
||||
{
|
||||
float target = _checked ? 1f : 0f;
|
||||
|
||||
if (_animationProgress < target)
|
||||
{
|
||||
_animationProgress += _animationStep;
|
||||
if (_animationProgress >= target)
|
||||
{
|
||||
_animationProgress = target;
|
||||
_animationTimer.Stop();
|
||||
}
|
||||
}
|
||||
else if (_animationProgress > target)
|
||||
{
|
||||
_animationProgress -= _animationStep;
|
||||
if (_animationProgress <= target)
|
||||
{
|
||||
_animationProgress = target;
|
||||
_animationTimer.Stop();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_animationTimer.Stop();
|
||||
}
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
protected override void OnPaint(PaintEventArgs e)
|
||||
{
|
||||
base.OnPaint(e);
|
||||
|
||||
Graphics g = e.Graphics;
|
||||
g.SmoothingMode = SmoothingMode.AntiAlias;
|
||||
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
|
||||
|
||||
int width = Width;
|
||||
int height = Height;
|
||||
int padding = 2;
|
||||
int thumbDiameter = height - (padding * 2);
|
||||
int trackRadius = height / 2;
|
||||
|
||||
Color trackColor;
|
||||
if (_isHovered)
|
||||
{
|
||||
trackColor = InterpolateColor(_offHoverColor, _onHoverColor, _animationProgress);
|
||||
}
|
||||
else
|
||||
{
|
||||
trackColor = InterpolateColor(_offColor, _onColor, _animationProgress);
|
||||
}
|
||||
|
||||
Rectangle trackRect = new Rectangle(0, 0, width, height);
|
||||
using (GraphicsPath trackPath = CreateRoundedRectPath(trackRect, trackRadius))
|
||||
using (SolidBrush trackBrush = new SolidBrush(trackColor))
|
||||
{
|
||||
g.FillPath(trackBrush, trackPath);
|
||||
}
|
||||
|
||||
int thumbMinX = padding;
|
||||
int thumbMaxX = width - thumbDiameter - padding;
|
||||
float easedProgress = EaseOutQuad(_animationProgress);
|
||||
int thumbX = (int)(thumbMinX + (thumbMaxX - thumbMinX) * easedProgress);
|
||||
int thumbY = padding;
|
||||
|
||||
Rectangle shadowRect = new Rectangle(thumbX + 1, thumbY + 1, thumbDiameter, thumbDiameter);
|
||||
using (SolidBrush shadowBrush = new SolidBrush(Color.FromArgb(40, 0, 0, 0)))
|
||||
{
|
||||
g.FillEllipse(shadowBrush, shadowRect);
|
||||
}
|
||||
|
||||
Rectangle thumbRect = new Rectangle(thumbX, thumbY, thumbDiameter, thumbDiameter);
|
||||
using (SolidBrush thumbBrush = new SolidBrush(_thumbColor))
|
||||
{
|
||||
g.FillEllipse(thumbBrush, thumbRect);
|
||||
}
|
||||
}
|
||||
|
||||
private float EaseOutQuad(float t)
|
||||
{
|
||||
return t * (2 - t);
|
||||
}
|
||||
|
||||
private Color InterpolateColor(Color from, Color to, float progress)
|
||||
{
|
||||
int r = (int)(from.R + (to.R - from.R) * progress);
|
||||
int g = (int)(from.G + (to.G - from.G) * progress);
|
||||
int b = (int)(from.B + (to.B - from.B) * progress);
|
||||
int a = (int)(from.A + (to.A - from.A) * progress);
|
||||
return Color.FromArgb(a, r, g, b);
|
||||
}
|
||||
|
||||
private GraphicsPath CreateRoundedRectPath(Rectangle rect, int radius)
|
||||
{
|
||||
GraphicsPath path = new GraphicsPath();
|
||||
int diameter = radius * 2;
|
||||
|
||||
path.AddArc(rect.X, rect.Y, diameter, diameter, 180, 90);
|
||||
path.AddArc(rect.Right - diameter, rect.Y, diameter, diameter, 270, 90);
|
||||
path.AddArc(rect.Right - diameter, rect.Bottom - diameter, diameter, diameter, 0, 90);
|
||||
path.AddArc(rect.X, rect.Bottom - diameter, diameter, diameter, 90, 90);
|
||||
path.CloseFigure();
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
protected override void OnMouseEnter(EventArgs e)
|
||||
{
|
||||
base.OnMouseEnter(e);
|
||||
_isHovered = true;
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
protected override void OnMouseLeave(EventArgs e)
|
||||
{
|
||||
base.OnMouseLeave(e);
|
||||
_isHovered = false;
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
protected override void OnMouseDown(MouseEventArgs e)
|
||||
{
|
||||
base.OnMouseDown(e);
|
||||
|
||||
if (e.Button == MouseButtons.Left)
|
||||
{
|
||||
// Toggle immediately on mouse down for responsive feel
|
||||
_checked = !_checked;
|
||||
StartAnimation();
|
||||
OnCheckedChanged(EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_animationTimer?.Stop();
|
||||
_animationTimer?.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
132
UpdateForm.Designer.cs
generated
@@ -1,5 +1,4 @@
|
||||
|
||||
namespace AndroidSideloader
|
||||
namespace AndroidSideloader
|
||||
{
|
||||
partial class UpdateForm
|
||||
{
|
||||
@@ -29,7 +28,6 @@ namespace AndroidSideloader
|
||||
/// </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();
|
||||
@@ -40,125 +38,133 @@ namespace AndroidSideloader
|
||||
this.panel1.SuspendLayout();
|
||||
this.panel3.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
//
|
||||
// panel1
|
||||
//
|
||||
this.panel1.BackColor = global::AndroidSideloader.Properties.Settings.Default.BackColor;
|
||||
this.panel1.BackgroundImage = global::AndroidSideloader.Properties.Resources.pattern_cubes;
|
||||
//
|
||||
this.panel1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(24)))), ((int)(((byte)(29)))));
|
||||
this.panel1.Controls.Add(this.YesUpdate);
|
||||
this.panel1.Controls.Add(this.panel3);
|
||||
this.panel1.Controls.Add(this.UpdateVerLabel);
|
||||
this.panel1.Controls.Add(this.CurVerLabel);
|
||||
this.panel1.Controls.Add(this.SkipUpdate);
|
||||
this.panel1.DataBindings.Add(new System.Windows.Forms.Binding("BackColor", global::AndroidSideloader.Properties.Settings.Default, "BackColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
|
||||
this.panel1.Location = new System.Drawing.Point(-6, -6);
|
||||
this.panel1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.panel1.Location = new System.Drawing.Point(0, 0);
|
||||
this.panel1.Name = "panel1";
|
||||
this.panel1.Size = new System.Drawing.Size(474, 305);
|
||||
this.panel1.Padding = new System.Windows.Forms.Padding(20, 50, 20, 20);
|
||||
this.panel1.Size = new System.Drawing.Size(480, 320);
|
||||
this.panel1.TabIndex = 5;
|
||||
this.panel1.MouseDown += new System.Windows.Forms.MouseEventHandler(this.UpdateForm_MouseDown);
|
||||
this.panel1.MouseMove += new System.Windows.Forms.MouseEventHandler(this.UpdateForm_MouseMove);
|
||||
this.panel1.MouseUp += new System.Windows.Forms.MouseEventHandler(this.UpdateForm_MouseUp);
|
||||
//
|
||||
//
|
||||
// YesUpdate
|
||||
//
|
||||
this.YesUpdate.Active1 = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(40)))), ((int)(((byte)(40)))));
|
||||
this.YesUpdate.Active2 = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(40)))), ((int)(((byte)(40)))));
|
||||
//
|
||||
this.YesUpdate.Active1 = System.Drawing.Color.FromArgb(((int)(((byte)(113)))), ((int)(((byte)(223)))), ((int)(((byte)(193)))));
|
||||
this.YesUpdate.Active2 = System.Drawing.Color.FromArgb(((int)(((byte)(93)))), ((int)(((byte)(203)))), ((int)(((byte)(173)))));
|
||||
this.YesUpdate.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.YesUpdate.BackColor = System.Drawing.Color.Transparent;
|
||||
this.YesUpdate.DialogResult = System.Windows.Forms.DialogResult.OK;
|
||||
this.YesUpdate.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F);
|
||||
this.YesUpdate.ForeColor = System.Drawing.Color.White;
|
||||
this.YesUpdate.Inactive1 = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(25)))), ((int)(((byte)(25)))));
|
||||
this.YesUpdate.Inactive2 = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(25)))), ((int)(((byte)(25)))));
|
||||
this.YesUpdate.Location = new System.Drawing.Point(339, 245);
|
||||
this.YesUpdate.Disabled1 = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(35)))), ((int)(((byte)(45)))));
|
||||
this.YesUpdate.Disabled2 = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(28)))), ((int)(((byte)(35)))));
|
||||
this.YesUpdate.DisabledStrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(50)))), ((int)(((byte)(55)))), ((int)(((byte)(65)))));
|
||||
this.YesUpdate.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Bold);
|
||||
this.YesUpdate.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(20)))), ((int)(((byte)(20)))));
|
||||
this.YesUpdate.Inactive1 = System.Drawing.Color.FromArgb(((int)(((byte)(93)))), ((int)(((byte)(203)))), ((int)(((byte)(173)))));
|
||||
this.YesUpdate.Inactive2 = System.Drawing.Color.FromArgb(((int)(((byte)(73)))), ((int)(((byte)(183)))), ((int)(((byte)(153)))));
|
||||
this.YesUpdate.Location = new System.Drawing.Point(340, 259);
|
||||
this.YesUpdate.Name = "YesUpdate";
|
||||
this.YesUpdate.Radius = 5;
|
||||
this.YesUpdate.Size = new System.Drawing.Size(111, 31);
|
||||
this.YesUpdate.Stroke = true;
|
||||
this.YesUpdate.Radius = 6;
|
||||
this.YesUpdate.Size = new System.Drawing.Size(120, 36);
|
||||
this.YesUpdate.Stroke = false;
|
||||
this.YesUpdate.StrokeColor = System.Drawing.Color.FromArgb(((int)(((byte)(74)))), ((int)(((byte)(74)))), ((int)(((byte)(74)))));
|
||||
this.YesUpdate.TabIndex = 2;
|
||||
this.YesUpdate.Text = "Update Now";
|
||||
this.YesUpdate.Transparency = false;
|
||||
this.YesUpdate.Click += new System.EventHandler(this.YesUpdate_Click);
|
||||
//
|
||||
//
|
||||
// panel3
|
||||
//
|
||||
this.panel3.BackColor = global::AndroidSideloader.Properties.Settings.Default.SubButtonColor;
|
||||
//
|
||||
this.panel3.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.panel3.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(28)))), ((int)(((byte)(32)))), ((int)(((byte)(38)))));
|
||||
this.panel3.Controls.Add(this.UpdateTextBox);
|
||||
this.panel3.DataBindings.Add(new System.Windows.Forms.Binding("BackColor", global::AndroidSideloader.Properties.Settings.Default, "SubButtonColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
|
||||
this.panel3.Location = new System.Drawing.Point(21, 19);
|
||||
this.panel3.Location = new System.Drawing.Point(20, 50);
|
||||
this.panel3.Name = "panel3";
|
||||
this.panel3.Size = new System.Drawing.Size(432, 218);
|
||||
this.panel3.Padding = new System.Windows.Forms.Padding(12, 10, 12, 10);
|
||||
this.panel3.Size = new System.Drawing.Size(440, 200);
|
||||
this.panel3.TabIndex = 0;
|
||||
//
|
||||
//
|
||||
// UpdateTextBox
|
||||
//
|
||||
this.UpdateTextBox.BackColor = global::AndroidSideloader.Properties.Settings.Default.ComboBoxColor;
|
||||
//
|
||||
this.UpdateTextBox.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(28)))), ((int)(((byte)(32)))), ((int)(((byte)(38)))));
|
||||
this.UpdateTextBox.BorderStyle = System.Windows.Forms.BorderStyle.None;
|
||||
this.UpdateTextBox.DataBindings.Add(new System.Windows.Forms.Binding("BackColor", global::AndroidSideloader.Properties.Settings.Default, "ComboBoxColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
|
||||
this.UpdateTextBox.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.25F);
|
||||
this.UpdateTextBox.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.UpdateTextBox.Font = new System.Drawing.Font("Segoe UI", 9.5F);
|
||||
this.UpdateTextBox.ForeColor = System.Drawing.Color.White;
|
||||
this.UpdateTextBox.Location = new System.Drawing.Point(12, 8);
|
||||
this.UpdateTextBox.Location = new System.Drawing.Point(12, 10);
|
||||
this.UpdateTextBox.Margin = new System.Windows.Forms.Padding(6);
|
||||
this.UpdateTextBox.Name = "UpdateTextBox";
|
||||
this.UpdateTextBox.ReadOnly = true;
|
||||
this.UpdateTextBox.ScrollBars = System.Windows.Forms.RichTextBoxScrollBars.Vertical;
|
||||
this.UpdateTextBox.Size = new System.Drawing.Size(408, 200);
|
||||
this.UpdateTextBox.Size = new System.Drawing.Size(416, 180);
|
||||
this.UpdateTextBox.TabIndex = 1;
|
||||
this.UpdateTextBox.Text = "";
|
||||
this.UpdateTextBox.TextChanged += new System.EventHandler(this.UpdateTextBox_TextChanged);
|
||||
this.UpdateTextBox.MouseDown += new System.Windows.Forms.MouseEventHandler(this.UpdateForm_MouseDown);
|
||||
this.UpdateTextBox.MouseMove += new System.Windows.Forms.MouseEventHandler(this.UpdateForm_MouseMove);
|
||||
this.UpdateTextBox.MouseUp += new System.Windows.Forms.MouseEventHandler(this.UpdateForm_MouseUp);
|
||||
//
|
||||
//
|
||||
// UpdateVerLabel
|
||||
//
|
||||
//
|
||||
this.UpdateVerLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
this.UpdateVerLabel.AutoSize = true;
|
||||
this.UpdateVerLabel.BackColor = System.Drawing.Color.Transparent;
|
||||
this.UpdateVerLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F);
|
||||
this.UpdateVerLabel.ForeColor = System.Drawing.SystemColors.Control;
|
||||
this.UpdateVerLabel.Location = new System.Drawing.Point(21, 261);
|
||||
this.UpdateVerLabel.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold);
|
||||
this.UpdateVerLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(93)))), ((int)(((byte)(203)))), ((int)(((byte)(173)))));
|
||||
this.UpdateVerLabel.Location = new System.Drawing.Point(20, 285);
|
||||
this.UpdateVerLabel.Name = "UpdateVerLabel";
|
||||
this.UpdateVerLabel.Size = new System.Drawing.Size(94, 15);
|
||||
this.UpdateVerLabel.Size = new System.Drawing.Size(95, 15);
|
||||
this.UpdateVerLabel.TabIndex = 3;
|
||||
this.UpdateVerLabel.Text = "Update Version:";
|
||||
//
|
||||
//
|
||||
// CurVerLabel
|
||||
//
|
||||
//
|
||||
this.CurVerLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
this.CurVerLabel.AutoSize = true;
|
||||
this.CurVerLabel.BackColor = System.Drawing.Color.Transparent;
|
||||
this.CurVerLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F);
|
||||
this.CurVerLabel.ForeColor = System.Drawing.SystemColors.Control;
|
||||
this.CurVerLabel.Location = new System.Drawing.Point(21, 245);
|
||||
this.CurVerLabel.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||
this.CurVerLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(160)))), ((int)(((byte)(165)))), ((int)(((byte)(175)))));
|
||||
this.CurVerLabel.Location = new System.Drawing.Point(20, 266);
|
||||
this.CurVerLabel.Name = "CurVerLabel";
|
||||
this.CurVerLabel.Size = new System.Drawing.Size(94, 15);
|
||||
this.CurVerLabel.Size = new System.Drawing.Size(91, 15);
|
||||
this.CurVerLabel.TabIndex = 2;
|
||||
this.CurVerLabel.Text = "Current Version:";
|
||||
//
|
||||
//
|
||||
// SkipUpdate
|
||||
//
|
||||
//
|
||||
this.SkipUpdate.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.SkipUpdate.AutoSize = true;
|
||||
this.SkipUpdate.BackColor = System.Drawing.Color.Transparent;
|
||||
this.SkipUpdate.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
|
||||
this.SkipUpdate.Font = new System.Drawing.Font("Microsoft Sans Serif", 8F);
|
||||
this.SkipUpdate.ForeColor = System.Drawing.Color.Silver;
|
||||
this.SkipUpdate.Location = new System.Drawing.Point(374, 279);
|
||||
this.SkipUpdate.Cursor = System.Windows.Forms.Cursors.Hand;
|
||||
this.SkipUpdate.Font = new System.Drawing.Font("Segoe UI", 8.5F);
|
||||
this.SkipUpdate.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(160)))), ((int)(((byte)(165)))), ((int)(((byte)(175)))));
|
||||
this.SkipUpdate.Location = new System.Drawing.Point(380, 297);
|
||||
this.SkipUpdate.Name = "SkipUpdate";
|
||||
this.SkipUpdate.Size = new System.Drawing.Size(76, 13);
|
||||
this.SkipUpdate.Size = new System.Drawing.Size(73, 15);
|
||||
this.SkipUpdate.TabIndex = 4;
|
||||
this.SkipUpdate.Text = "𝖲𝖪𝖨𝖯 𝖥𝖮𝖱 𝖭𝖮𝖶";
|
||||
this.SkipUpdate.Text = "Skip for now";
|
||||
this.SkipUpdate.Click += new System.EventHandler(this.SkipUpdate_Click);
|
||||
//
|
||||
//
|
||||
// UpdateForm
|
||||
//
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.AutoValidate = System.Windows.Forms.AutoValidate.EnablePreventFocusChange;
|
||||
this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(29)))), ((int)(((byte)(29)))), ((int)(((byte)(29)))));
|
||||
this.ClientSize = new System.Drawing.Size(462, 291);
|
||||
this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(24)))), ((int)(((byte)(29)))));
|
||||
this.ClientSize = new System.Drawing.Size(480, 320);
|
||||
this.ControlBox = false;
|
||||
this.Controls.Add(this.panel1);
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Fixed3D;
|
||||
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
|
||||
this.Name = "UpdateForm";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||
this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.UpdateForm_MouseDown);
|
||||
@@ -181,4 +187,4 @@ namespace AndroidSideloader
|
||||
private System.Windows.Forms.Panel panel1;
|
||||
private RoundButton YesUpdate;
|
||||
}
|
||||
}
|
||||
}
|
||||
201
UpdateForm.cs
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace AndroidSideloader
|
||||
@@ -9,15 +10,207 @@ namespace AndroidSideloader
|
||||
private bool mouseDown;
|
||||
private Point lastLocation;
|
||||
|
||||
// Modern theme colors
|
||||
private static readonly Color BackgroundColor = Color.FromArgb(20, 24, 29);
|
||||
private static readonly Color PanelColor = Color.FromArgb(28, 32, 38);
|
||||
private static readonly Color TextColor = Color.White;
|
||||
private static readonly Color SecondaryTextColor = Color.FromArgb(160, 165, 175);
|
||||
private static readonly Color BorderColor = Color.FromArgb(60, 65, 75);
|
||||
|
||||
public UpdateForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
// Use same icon as the executable
|
||||
this.Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath);
|
||||
|
||||
ApplyModernTheme();
|
||||
CenterToScreen();
|
||||
CurVerLabel.Text += " " + Updater.LocalVersion;
|
||||
UpdateVerLabel.Text += " " + Updater.currentVersion;
|
||||
CurVerLabel.Text = $"Current Version: {Updater.LocalVersion}";
|
||||
UpdateVerLabel.Text = $"Update Version: {Updater.currentVersion}";
|
||||
UpdateTextBox.Text = Updater.changelog;
|
||||
}
|
||||
|
||||
private void ApplyModernTheme()
|
||||
{
|
||||
// Form settings
|
||||
this.FormBorderStyle = FormBorderStyle.None;
|
||||
this.BackColor = BackgroundColor;
|
||||
this.DoubleBuffered = true;
|
||||
|
||||
// Enable double buffering on panels for smooth rounded corners
|
||||
EnableDoubleBuffering(panel1);
|
||||
EnableDoubleBuffering(panel3);
|
||||
|
||||
// Add custom paint handler for rounded panel1 (main container)
|
||||
panel1.Paint += Panel1_Paint;
|
||||
panel1.BackColor = Color.Transparent;
|
||||
|
||||
// Add custom paint handler for rounded panel3 (changelog container)
|
||||
panel3.Paint += Panel3_Paint;
|
||||
panel3.BackColor = Color.Transparent;
|
||||
|
||||
// Update textbox to have matching background
|
||||
UpdateTextBox.BackColor = PanelColor;
|
||||
|
||||
// Add title label
|
||||
var titleLabel = new Label
|
||||
{
|
||||
Text = "Update Available",
|
||||
Font = new Font("Segoe UI", 12F, FontStyle.Bold),
|
||||
ForeColor = TextColor,
|
||||
BackColor = Color.Transparent,
|
||||
AutoSize = true,
|
||||
Location = new Point(20, 15)
|
||||
};
|
||||
panel1.Controls.Add(titleLabel);
|
||||
titleLabel.BringToFront();
|
||||
|
||||
// Add close button
|
||||
var closeButton = new Label
|
||||
{
|
||||
Text = "✕",
|
||||
Font = new Font("Segoe UI", 10F),
|
||||
ForeColor = SecondaryTextColor,
|
||||
BackColor = Color.Transparent,
|
||||
AutoSize = true,
|
||||
Cursor = Cursors.Hand,
|
||||
Location = new Point(this.ClientSize.Width - 30, 10),
|
||||
Anchor = AnchorStyles.Top | AnchorStyles.Right
|
||||
};
|
||||
closeButton.Click += (s, e) => Close();
|
||||
closeButton.MouseEnter += (s, e) => closeButton.ForeColor = Color.FromArgb(220, 80, 80);
|
||||
closeButton.MouseLeave += (s, e) => closeButton.ForeColor = SecondaryTextColor;
|
||||
panel1.Controls.Add(closeButton);
|
||||
closeButton.BringToFront();
|
||||
|
||||
// Apply custom painting for form rounded corners and border
|
||||
this.Paint += UpdateForm_Paint;
|
||||
}
|
||||
|
||||
private void EnableDoubleBuffering(Panel panel)
|
||||
{
|
||||
typeof(Panel).InvokeMember("DoubleBuffered",
|
||||
System.Reflection.BindingFlags.SetProperty | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic,
|
||||
null, panel, new object[] { true });
|
||||
}
|
||||
|
||||
private void Panel1_Paint(object sender, PaintEventArgs e)
|
||||
{
|
||||
var panel = sender as Panel;
|
||||
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
|
||||
e.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
|
||||
|
||||
int radius = 12;
|
||||
var rect = new Rectangle(0, 0, panel.Width - 1, panel.Height - 1);
|
||||
|
||||
using (var path = CreateRoundedRectPath(rect, radius))
|
||||
{
|
||||
// Fill background
|
||||
using (var brush = new SolidBrush(BackgroundColor))
|
||||
{
|
||||
e.Graphics.FillPath(brush, path);
|
||||
}
|
||||
|
||||
// Draw border
|
||||
using (var pen = new Pen(BorderColor, 1f))
|
||||
{
|
||||
e.Graphics.DrawPath(pen, path);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply rounded region to clip children
|
||||
using (var regionPath = CreateRoundedRectPath(new Rectangle(0, 0, panel.Width, panel.Height), radius))
|
||||
{
|
||||
panel.Region = new Region(regionPath);
|
||||
}
|
||||
}
|
||||
|
||||
private void Panel3_Paint(object sender, PaintEventArgs e)
|
||||
{
|
||||
var panel = sender as Panel;
|
||||
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
|
||||
e.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
|
||||
|
||||
int radius = 10;
|
||||
var rect = new Rectangle(0, 0, panel.Width - 1, panel.Height - 1);
|
||||
|
||||
using (var path = CreateRoundedRectPath(rect, radius))
|
||||
{
|
||||
// Fill background
|
||||
using (var brush = new SolidBrush(PanelColor))
|
||||
{
|
||||
e.Graphics.FillPath(brush, path);
|
||||
}
|
||||
|
||||
// Draw border
|
||||
using (var pen = new Pen(BorderColor, 1f))
|
||||
{
|
||||
e.Graphics.DrawPath(pen, path);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply rounded region to clip children
|
||||
using (var regionPath = CreateRoundedRectPath(new Rectangle(0, 0, panel.Width, panel.Height), radius))
|
||||
{
|
||||
panel.Region = new Region(regionPath);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateForm_Paint(object sender, PaintEventArgs e)
|
||||
{
|
||||
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
|
||||
|
||||
int w = this.ClientSize.Width;
|
||||
int h = this.ClientSize.Height;
|
||||
int radius = 12;
|
||||
|
||||
// Draw border
|
||||
using (var borderPen = new Pen(BorderColor, 1f))
|
||||
using (var path = CreateRoundedRectPath(new Rectangle(0, 0, w - 1, h - 1), radius))
|
||||
{
|
||||
e.Graphics.DrawPath(borderPen, path);
|
||||
}
|
||||
|
||||
// Apply rounded region
|
||||
using (var regionPath = CreateRoundedRectPath(new Rectangle(0, 0, w, h), radius))
|
||||
{
|
||||
this.Region = new Region(regionPath);
|
||||
}
|
||||
}
|
||||
|
||||
private GraphicsPath CreateRoundedRectPath(Rectangle rect, int radius)
|
||||
{
|
||||
GraphicsPath path = new GraphicsPath();
|
||||
|
||||
if (radius <= 0)
|
||||
{
|
||||
path.AddRectangle(rect);
|
||||
return path;
|
||||
}
|
||||
|
||||
int diameter = radius * 2;
|
||||
diameter = Math.Min(diameter, Math.Min(rect.Width, rect.Height));
|
||||
radius = diameter / 2;
|
||||
|
||||
Rectangle arcRect = new Rectangle(rect.Location, new Size(diameter, diameter));
|
||||
|
||||
// Top left arc
|
||||
path.AddArc(arcRect, 180, 90);
|
||||
// Top right arc
|
||||
arcRect.X = rect.Right - diameter;
|
||||
path.AddArc(arcRect, 270, 90);
|
||||
// Bottom right arc
|
||||
arcRect.Y = rect.Bottom - diameter;
|
||||
path.AddArc(arcRect, 0, 90);
|
||||
// Bottom left arc
|
||||
arcRect.X = rect.Left;
|
||||
path.AddArc(arcRect, 90, 90);
|
||||
|
||||
path.CloseFigure();
|
||||
return path;
|
||||
}
|
||||
|
||||
private void YesUpdate_Click(object sender, EventArgs e)
|
||||
{
|
||||
Updater.doUpdate();
|
||||
@@ -31,7 +224,6 @@ namespace AndroidSideloader
|
||||
|
||||
private void UpdateTextBox_TextChanged(object sender, EventArgs e)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private void UpdateForm_MouseDown(object sender, MouseEventArgs e)
|
||||
@@ -46,7 +238,6 @@ namespace AndroidSideloader
|
||||
{
|
||||
Location = new Point(
|
||||
Location.X - lastLocation.X + e.X, Location.Y - lastLocation.Y + e.Y);
|
||||
|
||||
Update();
|
||||
}
|
||||
}
|
||||
@@ -56,4 +247,4 @@ namespace AndroidSideloader
|
||||
mouseDown = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
6849
UpdateForm.resx
39
Updater.cs
@@ -11,9 +11,9 @@ namespace AndroidSideloader
|
||||
public static string AppName { get; set; }
|
||||
public static string Repository { get; set; }
|
||||
private static readonly string RawGitHubUrl = "https://raw.githubusercontent.com/VRPirates/rookie";
|
||||
private static readonly string GitHubUrl = "https://github.com/VRPirates/rookie";
|
||||
public static readonly string GitHubUrl = "https://github.com/VRPirates/rookie";
|
||||
|
||||
public static readonly string LocalVersion = "2.34.0";
|
||||
public static readonly string LocalVersion = "3.0.1";
|
||||
public static string currentVersion = string.Empty;
|
||||
public static string changelog = string.Empty;
|
||||
|
||||
@@ -34,10 +34,41 @@ namespace AndroidSideloader
|
||||
}
|
||||
}
|
||||
|
||||
return LocalVersion.Trim() != currentVersion;
|
||||
// Compare versions - only return true if server version is greater than local version
|
||||
return CompareVersions(currentVersion, LocalVersion.Trim()) > 0;
|
||||
}
|
||||
|
||||
// Call this to ask the user if they want to update
|
||||
// Compares two semantic version strings (e.g., "2.35")
|
||||
// returns: 1 if version1 > version2, -1 if version1 < version2, 0 if equal
|
||||
private static int CompareVersions(string version1, string version2)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Parse versions into parts
|
||||
string[] parts1 = version1.Split('.');
|
||||
string[] parts2 = version2.Split('.');
|
||||
|
||||
// Compare each part
|
||||
int maxLength = Math.Max(parts1.Length, parts2.Length);
|
||||
for (int i = 0; i < maxLength; i++)
|
||||
{
|
||||
int v1 = i < parts1.Length && int.TryParse(parts1[i], out int p1) ? p1 : 0;
|
||||
int v2 = i < parts2.Length && int.TryParse(parts2[i], out int p2) ? p2 : 0;
|
||||
|
||||
if (v1 > v2) return 1;
|
||||
if (v1 < v2) return -1;
|
||||
}
|
||||
|
||||
return 0; // Versions are equal
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Fallback to string comparison if parsing fails
|
||||
return string.Compare(version1, version2, StringComparison.Ordinal);
|
||||
}
|
||||
}
|
||||
|
||||
// Ask the user if they want to update
|
||||
public static async Task Update()
|
||||
{
|
||||
if (await IsUpdateAvailableAsync())
|
||||
|
||||
24
UsernameForm.Designer.cs
generated
@@ -28,30 +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.DataBindings.Add(new System.Windows.Forms.Binding("BackColor", global::AndroidSideloader.Properties.Settings.Default, "TextBoxColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
|
||||
this.textBox1.DataBindings.Add(new System.Windows.Forms.Binding("Font", global::AndroidSideloader.Properties.Settings.Default, "FontStyle", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
|
||||
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)))));
|
||||
@@ -66,23 +66,19 @@
|
||||
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;
|
||||
this.BackgroundImage = global::AndroidSideloader.Properties.Resources.pattern_cubes;
|
||||
this.ClientSize = new System.Drawing.Size(443, 100);
|
||||
this.Controls.Add(this.button1);
|
||||
this.Controls.Add(this.textBox1);
|
||||
this.DataBindings.Add(new System.Windows.Forms.Binding("BackColor", global::AndroidSideloader.Properties.Settings.Default, "BackColor", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
|
||||
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
554
Utilities/DnsHelper.cs
Normal file
@@ -0,0 +1,554 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace AndroidSideloader.Utilities
|
||||
{
|
||||
// Provides DNS fallback functionality using Cloudflare DNS (1.1.1.1, 1.0.0.1) if system DNS fails to resolve critical hostnames
|
||||
// Also provides a proxy for rclone that handles DNS resolution
|
||||
public static class DnsHelper
|
||||
{
|
||||
private static readonly string[] FallbackDnsServers = { "1.1.1.1", "1.0.0.1" };
|
||||
private static readonly string[] CriticalHostnames =
|
||||
{
|
||||
"raw.githubusercontent.com",
|
||||
"downloads.rclone.org",
|
||||
"vrpirates.wiki",
|
||||
"github.com"
|
||||
};
|
||||
|
||||
private static readonly ConcurrentDictionary<string, IPAddress> _dnsCache =
|
||||
new ConcurrentDictionary<string, IPAddress>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
private static readonly object _lock = new object();
|
||||
|
||||
// Local proxy for rclone
|
||||
private static TcpListener _proxyListener;
|
||||
private static CancellationTokenSource _proxyCts;
|
||||
private static int _proxyPort;
|
||||
private static bool _initialized;
|
||||
private static bool _proxyRunning;
|
||||
|
||||
public static bool UseFallbackDns { get; private set; }
|
||||
|
||||
// Gets the proxy URL for rclone to use, or empty string if not needed
|
||||
public static string ProxyUrl => _proxyRunning ? $"http://127.0.0.1:{_proxyPort}" : string.Empty;
|
||||
|
||||
// Called after vrp-public.json is created/updated to test the hostname
|
||||
// Enable fallback DNS if the hostname fails on system DNS but works with fallback DNS
|
||||
public static void TestPublicConfigDns()
|
||||
{
|
||||
string hostname = GetPublicConfigHostname();
|
||||
if (string.IsNullOrEmpty(hostname))
|
||||
return;
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
// If already using fallback, just pre-resolve the new hostname
|
||||
if (UseFallbackDns)
|
||||
{
|
||||
var ip = ResolveWithFallbackDns(hostname);
|
||||
if (ip != null)
|
||||
{
|
||||
_dnsCache[hostname] = ip;
|
||||
Logger.Log($"Pre-resolved public config hostname {hostname} -> {ip}");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Test if system DNS can resolve the public config hostname
|
||||
bool systemDnsWorks = TestHostnameWithSystemDns(hostname);
|
||||
|
||||
if (!systemDnsWorks)
|
||||
{
|
||||
Logger.Log($"System DNS failed for {hostname}. Testing fallback...", LogLevel.WARNING);
|
||||
|
||||
// Test if fallback DNS works for this hostname
|
||||
var ip = ResolveWithFallbackDns(hostname);
|
||||
if (ip != null)
|
||||
{
|
||||
UseFallbackDns = true;
|
||||
_dnsCache[hostname] = ip;
|
||||
Logger.Log($"Enabled fallback DNS for {hostname} -> {ip}", LogLevel.INFO);
|
||||
ServicePointManager.DnsRefreshTimeout = 0;
|
||||
|
||||
// Pre-resolve base hostnames too
|
||||
PreResolveHostnames(CriticalHostnames);
|
||||
|
||||
// Start proxy if not already running
|
||||
if (!_proxyRunning)
|
||||
{
|
||||
StartProxy();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log($"Both system and fallback DNS failed for {hostname}", LogLevel.ERROR);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log($"System DNS works for public config hostname: {hostname}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetPublicConfigHostname()
|
||||
{
|
||||
try
|
||||
{
|
||||
string configPath = Path.Combine(Environment.CurrentDirectory, "vrp-public.json");
|
||||
if (!File.Exists(configPath))
|
||||
return null;
|
||||
|
||||
var config = JsonConvert.DeserializeObject<Dictionary<string, string>>(File.ReadAllText(configPath));
|
||||
if (config != null && config.TryGetValue("baseUri", out string baseUri))
|
||||
{
|
||||
return ExtractHostname(baseUri);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Log($"Failed to get hostname from vrp-public.json: {ex.Message}", LogLevel.WARNING);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string[] GetCriticalHostnames()
|
||||
{
|
||||
var hostnames = new List<string>(CriticalHostnames);
|
||||
|
||||
string host = GetPublicConfigHostname();
|
||||
if (!string.IsNullOrWhiteSpace(host) && !hostnames.Contains(host))
|
||||
{
|
||||
hostnames.Add(host);
|
||||
Logger.Log($"Added {host} from vrp-public.json to critical hostnames");
|
||||
}
|
||||
|
||||
return hostnames.ToArray();
|
||||
}
|
||||
|
||||
private static string ExtractHostname(string uriString)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(uriString)) return null;
|
||||
|
||||
if (!uriString.StartsWith("http://", StringComparison.OrdinalIgnoreCase) &&
|
||||
!uriString.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
uriString = "https://" + uriString;
|
||||
}
|
||||
|
||||
if (Uri.TryCreate(uriString, UriKind.Absolute, out Uri uri))
|
||||
return uri.Host;
|
||||
|
||||
// Fallback: manual extraction
|
||||
string hostname = uriString.Replace("https://", "").Replace("http://", "");
|
||||
int idx = hostname.IndexOfAny(new[] { '/', ':' });
|
||||
return idx > 0 ? hostname.Substring(0, idx) : hostname;
|
||||
}
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (_initialized) return;
|
||||
|
||||
Logger.Log("Testing DNS resolution for critical hostnames...");
|
||||
var hostnames = GetCriticalHostnames();
|
||||
|
||||
if (TestDns(hostnames, useSystem: true))
|
||||
{
|
||||
Logger.Log("System DNS is working correctly.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("System DNS failed. Testing Cloudflare DNS fallback...", LogLevel.WARNING);
|
||||
if (TestDns(hostnames, useSystem: false))
|
||||
{
|
||||
UseFallbackDns = true;
|
||||
Logger.Log("Using Cloudflare DNS fallback.", LogLevel.INFO);
|
||||
PreResolveHostnames(hostnames);
|
||||
ServicePointManager.DnsRefreshTimeout = 0;
|
||||
StartProxy();
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("Both system and fallback DNS failed.", LogLevel.ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Cleanup() => StopProxy();
|
||||
|
||||
private static bool TestHostnameWithSystemDns(string hostname)
|
||||
{
|
||||
try
|
||||
{
|
||||
var addresses = Dns.GetHostAddresses(hostname);
|
||||
return addresses?.Length > 0;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TestDns(string[] hostnames, bool useSystem)
|
||||
{
|
||||
if (useSystem)
|
||||
{
|
||||
return hostnames.All(h =>
|
||||
{
|
||||
try { return Dns.GetHostAddresses(h)?.Length > 0; }
|
||||
catch { return false; }
|
||||
});
|
||||
}
|
||||
|
||||
return FallbackDnsServers.Any(server =>
|
||||
{
|
||||
try { return ResolveWithDns(hostnames[0], server)?.Count > 0; }
|
||||
catch { return false; }
|
||||
});
|
||||
}
|
||||
|
||||
private static void PreResolveHostnames(string[] hostnames)
|
||||
{
|
||||
foreach (string hostname in hostnames)
|
||||
{
|
||||
var ip = ResolveWithFallbackDns(hostname);
|
||||
if (ip != null)
|
||||
{
|
||||
_dnsCache[hostname] = ip;
|
||||
Logger.Log($"Pre-resolved {hostname} -> {ip}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static IPAddress ResolveWithFallbackDns(string hostname)
|
||||
{
|
||||
foreach (string server in FallbackDnsServers)
|
||||
{
|
||||
try
|
||||
{
|
||||
var addresses = ResolveWithDns(hostname, server);
|
||||
if (addresses?.Count > 0) return addresses[0];
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static List<IPAddress> ResolveWithDns(string hostname, string dnsServer, int timeoutMs = 5000)
|
||||
{
|
||||
using (var udp = new UdpClient { Client = { ReceiveTimeout = timeoutMs, SendTimeout = timeoutMs } })
|
||||
{
|
||||
byte[] query = BuildDnsQuery(hostname);
|
||||
udp.Send(query, query.Length, new IPEndPoint(IPAddress.Parse(dnsServer), 53));
|
||||
IPEndPoint remoteEp = null;
|
||||
return ParseDnsResponse(udp.Receive(ref remoteEp));
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] BuildDnsQuery(string hostname)
|
||||
{
|
||||
using (var ms = new MemoryStream())
|
||||
using (var writer = new BinaryWriter(ms))
|
||||
{
|
||||
writer.Write(IPAddress.HostToNetworkOrder((short)new Random().Next(0, ushort.MaxValue)));
|
||||
writer.Write(IPAddress.HostToNetworkOrder((short)0x0100)); // Flags
|
||||
writer.Write(IPAddress.HostToNetworkOrder((short)1)); // Questions
|
||||
writer.Write(IPAddress.HostToNetworkOrder((short)0)); // Answer RRs
|
||||
writer.Write(IPAddress.HostToNetworkOrder((short)0)); // Authority RRs
|
||||
writer.Write(IPAddress.HostToNetworkOrder((short)0)); // Additional RRs
|
||||
|
||||
foreach (string label in hostname.Split('.'))
|
||||
{
|
||||
writer.Write((byte)label.Length);
|
||||
writer.Write(Encoding.ASCII.GetBytes(label));
|
||||
}
|
||||
writer.Write((byte)0);
|
||||
writer.Write(IPAddress.HostToNetworkOrder((short)1)); // Type A
|
||||
writer.Write(IPAddress.HostToNetworkOrder((short)1)); // Class IN
|
||||
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
private static List<IPAddress> ParseDnsResponse(byte[] response)
|
||||
{
|
||||
var addresses = new List<IPAddress>();
|
||||
if (response.Length < 12) return addresses;
|
||||
|
||||
int pos = 12;
|
||||
while (pos < response.Length && response[pos] != 0) pos += response[pos] + 1;
|
||||
pos += 5;
|
||||
|
||||
int answerCount = (response[6] << 8) | response[7];
|
||||
for (int i = 0; i < answerCount && pos + 12 <= response.Length; i++)
|
||||
{
|
||||
pos += (response[pos] & 0xC0) == 0xC0 ? 2 : SkipName(response, pos);
|
||||
if (pos + 10 > response.Length) break;
|
||||
|
||||
ushort type = (ushort)((response[pos] << 8) | response[pos + 1]);
|
||||
ushort rdLength = (ushort)((response[pos + 8] << 8) | response[pos + 9]);
|
||||
pos += 10;
|
||||
|
||||
if (pos + rdLength > response.Length) break;
|
||||
if (type == 1 && rdLength == 4)
|
||||
addresses.Add(new IPAddress(new[] { response[pos], response[pos + 1], response[pos + 2], response[pos + 3] }));
|
||||
pos += rdLength;
|
||||
}
|
||||
return addresses;
|
||||
}
|
||||
|
||||
private static int SkipName(byte[] data, int pos)
|
||||
{
|
||||
int start = pos;
|
||||
while (pos < data.Length && data[pos] != 0) pos += data[pos] + 1;
|
||||
return pos - start + 1;
|
||||
}
|
||||
|
||||
public static IPAddress ResolveHostname(string hostname, bool alwaysTryFallback = false)
|
||||
{
|
||||
if (_dnsCache.TryGetValue(hostname, out IPAddress cached))
|
||||
return cached;
|
||||
|
||||
try
|
||||
{
|
||||
var addresses = Dns.GetHostAddresses(hostname);
|
||||
if (addresses?.Length > 0)
|
||||
{
|
||||
_dnsCache[hostname] = addresses[0];
|
||||
return addresses[0];
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
if (alwaysTryFallback || UseFallbackDns || !_initialized)
|
||||
{
|
||||
var ip = ResolveWithFallbackDns(hostname);
|
||||
if (ip != null)
|
||||
{
|
||||
_dnsCache[hostname] = ip;
|
||||
return ip;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static HttpWebRequest CreateWebRequest(string url)
|
||||
{
|
||||
var uri = new Uri(url);
|
||||
|
||||
if (!UseFallbackDns)
|
||||
{
|
||||
try
|
||||
{
|
||||
Dns.GetHostAddresses(uri.Host);
|
||||
return (HttpWebRequest)WebRequest.Create(url);
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (!_initialized) Initialize();
|
||||
}
|
||||
}
|
||||
|
||||
if (UseFallbackDns)
|
||||
{
|
||||
var ip = ResolveHostname(uri.Host, alwaysTryFallback: true);
|
||||
if (ip != null)
|
||||
{
|
||||
var builder = new UriBuilder(uri) { Host = ip.ToString() };
|
||||
var request = (HttpWebRequest)WebRequest.Create(builder.Uri);
|
||||
request.Host = uri.Host;
|
||||
return request;
|
||||
}
|
||||
}
|
||||
|
||||
return (HttpWebRequest)WebRequest.Create(url);
|
||||
}
|
||||
|
||||
private static void StartProxy()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Find an available port
|
||||
_proxyListener = new TcpListener(IPAddress.Loopback, 0);
|
||||
_proxyListener.Start();
|
||||
_proxyPort = ((IPEndPoint)_proxyListener.LocalEndpoint).Port;
|
||||
_proxyCts = new CancellationTokenSource();
|
||||
_proxyRunning = true;
|
||||
|
||||
Logger.Log($"Started DNS proxy on port {_proxyPort}");
|
||||
|
||||
// Accept connections in background
|
||||
Task.Run(() => ProxyAcceptLoop(_proxyCts.Token));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Log($"Failed to start DNS proxy: {ex.Message}", LogLevel.WARNING);
|
||||
_proxyRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void StopProxy()
|
||||
{
|
||||
_proxyRunning = false;
|
||||
_proxyCts?.Cancel();
|
||||
try { _proxyListener?.Stop(); } catch { }
|
||||
}
|
||||
|
||||
private static async Task ProxyAcceptLoop(CancellationToken ct)
|
||||
{
|
||||
while (!ct.IsCancellationRequested && _proxyRunning)
|
||||
{
|
||||
try
|
||||
{
|
||||
var client = await _proxyListener.AcceptTcpClientAsync();
|
||||
_ = HandleProxyClient(client, ct);
|
||||
}
|
||||
catch (ObjectDisposedException) { break; }
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (!ct.IsCancellationRequested)
|
||||
Logger.Log($"Proxy accept error: {ex.Message}", LogLevel.WARNING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task HandleProxyClient(TcpClient client, CancellationToken ct)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (client)
|
||||
using (var stream = client.GetStream())
|
||||
{
|
||||
client.ReceiveTimeout = client.SendTimeout = 30000;
|
||||
|
||||
var buffer = new byte[8192];
|
||||
int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, ct);
|
||||
if (bytesRead == 0) return;
|
||||
|
||||
string request = Encoding.ASCII.GetString(buffer, 0, bytesRead);
|
||||
string[] lines = request.Split(new[] { "\r\n" }, StringSplitOptions.None);
|
||||
if (lines.Length == 0) return;
|
||||
|
||||
string[] requestLine = lines[0].Split(' ');
|
||||
if (requestLine.Length < 2) return;
|
||||
|
||||
if (requestLine[0] == "CONNECT")
|
||||
// HTTPS proxy - tunnel mode
|
||||
await HandleConnectRequest(stream, requestLine[1], ct);
|
||||
else
|
||||
// HTTP proxy - forward mode
|
||||
await HandleHttpRequest(stream, request, requestLine[1], ct);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (!ct.IsCancellationRequested)
|
||||
Logger.Log($"Proxy client error: {ex.Message}", LogLevel.WARNING);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task HandleConnectRequest(NetworkStream clientStream, string target, CancellationToken ct)
|
||||
{
|
||||
// Parse host:port
|
||||
string[] parts = target.Split(':');
|
||||
string host = parts[0];
|
||||
int port = parts.Length > 1 && int.TryParse(parts[1], out int p) ? p : 443;
|
||||
|
||||
// Resolve hostname
|
||||
IPAddress ip = ResolveHostname(host, alwaysTryFallback: true);
|
||||
if (ip == null)
|
||||
{
|
||||
await SendResponse(clientStream, "HTTP/1.1 502 Bad Gateway\r\n\r\n", ct);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Connect to target
|
||||
using (var targetClient = new TcpClient())
|
||||
{
|
||||
await targetClient.ConnectAsync(ip, port);
|
||||
using (var targetStream = targetClient.GetStream())
|
||||
{
|
||||
// Send 200 OK to client
|
||||
await SendResponse(clientStream, "HTTP/1.1 200 Connection Established\r\n\r\n", ct);
|
||||
// Tunnel data bidirectionally
|
||||
await Task.WhenAny(RelayData(clientStream, targetStream, ct), RelayData(targetStream, clientStream, ct));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Log($"CONNECT tunnel error to {host}: {ex.Message}", LogLevel.WARNING);
|
||||
await SendResponse(clientStream, "HTTP/1.1 502 Bad Gateway\r\n\r\n", ct);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task HandleHttpRequest(NetworkStream clientStream, string request, string url, CancellationToken ct)
|
||||
{
|
||||
try
|
||||
{
|
||||
var uri = new Uri(url);
|
||||
IPAddress ip = ResolveHostname(uri.Host, alwaysTryFallback: true);
|
||||
if (ip == null)
|
||||
{
|
||||
await SendResponse(clientStream, "HTTP/1.1 502 Bad Gateway\r\n\r\n", ct);
|
||||
return;
|
||||
}
|
||||
|
||||
using (var targetClient = new TcpClient())
|
||||
{
|
||||
await targetClient.ConnectAsync(ip, uri.Port > 0 ? uri.Port : 80);
|
||||
using (var targetStream = targetClient.GetStream())
|
||||
{
|
||||
// Modify request to use relative path
|
||||
string modifiedRequest = request.Replace(url, uri.PathAndQuery);
|
||||
byte[] requestBytes = Encoding.ASCII.GetBytes(modifiedRequest);
|
||||
await targetStream.WriteAsync(requestBytes, 0, requestBytes.Length, ct);
|
||||
|
||||
// Relay response
|
||||
await RelayData(targetStream, clientStream, ct);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Log($"HTTP proxy error: {ex.Message}", LogLevel.WARNING);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task SendResponse(NetworkStream stream, string response, CancellationToken ct)
|
||||
{
|
||||
byte[] bytes = Encoding.ASCII.GetBytes(response);
|
||||
try { await stream.WriteAsync(bytes, 0, bytes.Length, ct); } catch { }
|
||||
}
|
||||
|
||||
private static async Task RelayData(NetworkStream from, NetworkStream to, CancellationToken ct)
|
||||
{
|
||||
byte[] buffer = new byte[8192];
|
||||
try
|
||||
{
|
||||
int bytesRead;
|
||||
while ((bytesRead = await from.ReadAsync(buffer, 0, buffer.Length, ct)) > 0)
|
||||
await to.WriteAsync(buffer, 0, bytesRead, ct);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
91
Utilities/FileSystemUtilities.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
|
||||
namespace AndroidSideloader.Utilities
|
||||
{
|
||||
internal static class FileSystemUtilities
|
||||
{
|
||||
public static bool TryDeleteDirectory(string directoryPath, int maxRetries = 3, int delayMs = 150) // 3x 150ms = 450ms total
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(directoryPath))
|
||||
return true;
|
||||
|
||||
if (!Directory.Exists(directoryPath))
|
||||
return true;
|
||||
|
||||
Exception lastError = null;
|
||||
|
||||
// Retry deletion several times in case of lock ups
|
||||
for (int attempt = 0; attempt <= maxRetries; attempt++)
|
||||
{
|
||||
try
|
||||
{
|
||||
StripReadOnlyAttributes(directoryPath);
|
||||
Directory.Delete(directoryPath, true);
|
||||
return true;
|
||||
}
|
||||
catch (DirectoryNotFoundException)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex) when (ex is UnauthorizedAccessException || ex is IOException)
|
||||
{
|
||||
lastError = ex;
|
||||
|
||||
if (attempt < maxRetries)
|
||||
{
|
||||
Thread.Sleep(delayMs);
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Non-retryable error
|
||||
lastError = ex;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Last resort: rename then delete
|
||||
try
|
||||
{
|
||||
string renamedPath = directoryPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)
|
||||
+ ".deleting." + DateTime.UtcNow.Ticks;
|
||||
|
||||
Directory.Move(directoryPath, renamedPath);
|
||||
|
||||
StripReadOnlyAttributes(renamedPath);
|
||||
Directory.Delete(renamedPath, true);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
lastError = ex;
|
||||
}
|
||||
|
||||
Logger.Log($"Failed to delete directory: {directoryPath}. Error: {lastError}", LogLevel.WARNING);
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void StripReadOnlyAttributes(string directoryPath)
|
||||
{
|
||||
var root = new DirectoryInfo(directoryPath);
|
||||
if (!root.Exists) return;
|
||||
|
||||
root.Attributes &= ~FileAttributes.ReadOnly;
|
||||
|
||||
foreach (var dir in root.EnumerateDirectories("*", SearchOption.AllDirectories))
|
||||
{
|
||||
dir.Attributes &= ~FileAttributes.ReadOnly;
|
||||
}
|
||||
|
||||
foreach (var file in root.EnumerateFiles("*", SearchOption.AllDirectories))
|
||||
{
|
||||
file.Attributes &= ~FileAttributes.ReadOnly;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,8 +51,9 @@ namespace AndroidSideloader
|
||||
settings.CurrentLogPath = logFilePath;
|
||||
settings.Save();
|
||||
|
||||
// Initial log entry
|
||||
Log($"Logger initialized at: {DateTime.Now:hh:mmtt(UTC)}", LogLevel.INFO);
|
||||
// Initial log entry, make it stand out
|
||||
string time = DateTime.UtcNow.ToString("hh:mm:ss.fff tt (UTC): ");
|
||||
Log($"\n\n{time}------------ Logger initialized ------------", LogLevel.INFO);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -120,7 +120,6 @@ namespace AndroidSideloader.Utilities
|
||||
public DateTime LastLaunch2 { get; set; } = new DateTime(1969, 4, 20, 16, 20, 0);
|
||||
public bool Wired { get; set; } = false;
|
||||
public string AppPackages { get; set; } = string.Empty;
|
||||
public bool TrailersOn { get; set; } = false;
|
||||
public string DownloadDir { get; set; } = string.Empty;
|
||||
public bool CustomDownloadDir { get; set; } = false;
|
||||
public bool CustomBackupDir { get; set; } = false;
|
||||
@@ -132,8 +131,27 @@ namespace AndroidSideloader.Utilities
|
||||
public bool CreatePubMirrorFile { get; set; } = true;
|
||||
public bool UseDownloadedFiles { get; set; } = false;
|
||||
public float BandwidthLimit { get; set; } = 0f;
|
||||
public bool HideAdultContent { get; set; } = false;
|
||||
public string[] FavoritedGames { get; set; } = new string[0];
|
||||
public bool useProxy { get; set; } = false;
|
||||
public string ProxyAddress { get; set; } = string.Empty;
|
||||
public string ProxyPort { get; set; } = string.Empty;
|
||||
public string selectedMirror { get; set; } = string.Empty;
|
||||
public bool TrailersEnabled { get; set; } = true;
|
||||
public bool UseGalleryView { get; set; } = true;
|
||||
|
||||
// Window state persistence
|
||||
public int WindowX { get; set; } = -1;
|
||||
public int WindowY { get; set; } = -1;
|
||||
public int WindowWidth { get; set; } = -1;
|
||||
public int WindowHeight { get; set; } = -1;
|
||||
public bool WindowMaximized { get; set; } = false;
|
||||
|
||||
// Sort state persistence
|
||||
public int SortColumn { get; set; } = 0;
|
||||
public bool SortAscending { get; set; } = true;
|
||||
|
||||
// Download queue persistence
|
||||
public string[] QueuedGames { get; set; } = new string[0];
|
||||
|
||||
private SettingsManager()
|
||||
{
|
||||
@@ -242,7 +260,6 @@ namespace AndroidSideloader.Utilities
|
||||
LastLaunch2 = new DateTime(1969, 4, 20, 16, 20, 0);
|
||||
Wired = false;
|
||||
AppPackages = string.Empty;
|
||||
TrailersOn = false;
|
||||
DownloadDir = string.Empty;
|
||||
CustomDownloadDir = false;
|
||||
CustomBackupDir = false;
|
||||
@@ -254,10 +271,23 @@ namespace AndroidSideloader.Utilities
|
||||
CreatePubMirrorFile = true;
|
||||
UseDownloadedFiles = false;
|
||||
BandwidthLimit = 0f;
|
||||
HideAdultContent = false;
|
||||
FavoritedGames = new string[0];
|
||||
useProxy = false;
|
||||
ProxyAddress = string.Empty;
|
||||
ProxyPort = string.Empty;
|
||||
selectedMirror = string.Empty;
|
||||
TrailersEnabled = true;
|
||||
UseGalleryView = true;
|
||||
WindowX = -1;
|
||||
WindowY = -1;
|
||||
WindowWidth = -1;
|
||||
WindowHeight = -1;
|
||||
WindowMaximized = false;
|
||||
SortColumn = 0;
|
||||
SortAscending = true;
|
||||
QueuedGames = new string[0];
|
||||
|
||||
Save();
|
||||
Save();
|
||||
Debug.WriteLine("Default settings created.");
|
||||
}
|
||||
|
||||
@@ -283,6 +313,16 @@ namespace AndroidSideloader.Utilities
|
||||
}
|
||||
}
|
||||
|
||||
public string GetEffectiveBackupDir()
|
||||
{
|
||||
if (CustomBackupDir && Directory.Exists(BackupDir))
|
||||
{
|
||||
return BackupDir;
|
||||
}
|
||||
|
||||
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "Rookie Backups");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
FontStyle?.Dispose();
|
||||
|
||||
@@ -61,5 +61,13 @@ namespace AndroidSideloader.Utilities
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
public static bool TryParseDouble(string value, out double result)
|
||||
{
|
||||
return double.TryParse(value,
|
||||
System.Globalization.NumberStyles.Any,
|
||||
System.Globalization.CultureInfo.InvariantCulture,
|
||||
out result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
116
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,22 +35,23 @@ 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")))
|
||||
{
|
||||
_ = Logger.Log("Begin download 7-zip");
|
||||
WebClient client = new WebClient();
|
||||
string architecture = Environment.Is64BitOperatingSystem ? "64" : "";
|
||||
try
|
||||
{
|
||||
client.DownloadFile($"https://github.com/VRPirates/rookie/raw/master/7z{architecture}.exe", $"7z.exe");
|
||||
client.DownloadFile($"https://github.com/VRPirates/rookie/raw/master/7z{architecture}.dll", $"7z.dll");
|
||||
// Use DNS fallback download method from GetDependencies
|
||||
GetDependencies.DownloadFileWithDnsFallback($"https://github.com/VRPirates/rookie/raw/master/7z{architecture}.exe", "7z.exe");
|
||||
GetDependencies.DownloadFileWithDnsFallback($"https://github.com/VRPirates/rookie/raw/master/7z{architecture}.dll", "7z.dll");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_ = FlexibleMessageBox.Show($"You are unable to access the GitHub page with the Exception: {ex.Message}\nSome files may be missing (7z)");
|
||||
_ = FlexibleMessageBox.Show("7z was unable to be downloaded\nRookie will now close");
|
||||
_ = FlexibleMessageBox.Show(Program.form, $"You are unable to access the GitHub page with the Exception: {ex.Message}\nSome files may be missing (7z)");
|
||||
_ = FlexibleMessageBox.Show(Program.form, "7z was unable to be downloaded\nRookie will now close");
|
||||
Application.Exit();
|
||||
}
|
||||
_ = Logger.Log("Complete download 7-zip");
|
||||
@@ -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))
|
||||
@@ -127,7 +214,6 @@ namespace AndroidSideloader.Utilities
|
||||
extractionError = null; // Reset the error message
|
||||
throw new ExtractionException(errorMessage);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
RSL 2.34
|
||||
RSL 3.0.1
|
||||
|
||||
- Feature: Allow users to favorite games (right click on game)
|
||||
- Fix: Release Notes not showing with trailers enabled
|
||||
- Fix: Correct Discord Invite link on connection error
|
||||
- Fixed popularity ranking not working on some systems
|
||||
- Fixed favorites not updating immediately when removing items
|
||||
- Improved YouTube trailer matching accuracy
|
||||
- Implemented real-time progress updates for drag and drop operations
|
||||
- Refined backup button labels and dialogs
|
||||
- Gallery View: Added grouped tiles for games with multiple versions (e.g. Beat Saber)
|
||||
- ListView: Uninstall button now shows on hover instead of click
|
||||
- Public config file is now created automatically without prompt
|
||||
- Sideloading status label now shows device connection state
|
||||
- Download button text now reflects sideloading status
|
||||
BIN
icon.ico
|
Before Width: | Height: | Size: 401 KiB After Width: | Height: | Size: 31 KiB |
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="AdvancedSharpAdbClient" version="3.5.15" targetFramework="net452" />
|
||||
<package id="Costura.Fody" version="5.7.0" targetFramework="net452" developmentDependency="true" />
|
||||
<package id="Fody" version="6.8.1" targetFramework="net452" developmentDependency="true" />
|
||||
<package id="Microsoft.NETCore.Platforms" version="7.0.4" targetFramework="net452" />
|
||||
|
||||