Skip to content

Commit 735d448

Browse files
committed
add optional Explorer context menu to Install (and run) selected APK file to device (fixes #181)
1 parent 54dab25 commit 735d448

File tree

5 files changed

+239
-5
lines changed

5 files changed

+239
-5
lines changed

UnityLauncherPro/MainWindow.xaml

+3-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
xmlns:System="clr-namespace:System;assembly=mscorlib"
88
xmlns:converters="clr-namespace:UnityLauncherPro.Converters" x:Class="UnityLauncherPro.MainWindow"
99
mc:Ignorable="d"
10-
Title="UnityLauncherPro" Height="650" Width="880" WindowStartupLocation="CenterScreen" Background="{DynamicResource ThemeDarkestBackground}" MinWidth="780" MinHeight="650" AllowsTransparency="True" WindowStyle="None" Margin="0" KeyDown="OnWindowKeyDown" Closing="Window_Closing" SizeChanged="Window_SizeChanged" Icon="Images/icon.ico" SourceInitialized="Window_SourceInitialized" MouseDown="Window_MouseDown">
10+
Title="UnityLauncherPro" Height="670" Width="880" WindowStartupLocation="CenterScreen" Background="{DynamicResource ThemeDarkestBackground}" MinWidth="780" MinHeight="650" AllowsTransparency="True" WindowStyle="None" Margin="0" KeyDown="OnWindowKeyDown" Closing="Window_Closing" SizeChanged="Window_SizeChanged" Icon="Images/icon.ico" SourceInitialized="Window_SourceInitialized" MouseDown="Window_MouseDown">
1111
<Window.Resources>
1212
<converters:LastModifiedConverter x:Key="lastModifiedConverter"/>
1313
<converters:LastModifiedConverterTooltip x:Key="LastModifiedConverterTooltip"/>
@@ -842,7 +842,8 @@
842842

843843
<GroupBox Grid.Row="3" Header="System" VerticalAlignment="Top" Margin="0,0,3,0">
844844
<StackPanel Grid.Row="3" Orientation="Vertical" Margin="3,5,0,0" >
845-
<CheckBox x:Name="chkRegisterExplorerMenu" Content="Register Explorer context menu" Unchecked="ChkRegisterExplorerMenu_CheckedChanged" Checked="ChkRegisterExplorerMenu_CheckedChanged" ToolTip="Install registry entry for Explorer context menu" HorizontalAlignment="Left"/>
845+
<CheckBox x:Name="chkRegisterExplorerMenu" Content="Register Explorer context menu" Unchecked="ChkRegisterExplorerMenu_CheckedChanged" Checked="ChkRegisterExplorerMenu_CheckedChanged" ToolTip="Add registry entry for Explorer context menu" HorizontalAlignment="Left"/>
846+
<CheckBox x:Name="chkRegisterInstallAPKMenu" Content="Register 'Install APK' Explorer context menu" ToolTip="Add registry entry for Explorer 'Install APK' context menu" HorizontalAlignment="Left" Checked="chkRegisterInstallAPKMenu_Checked" Unchecked="chkRegisterInstallAPKMenu_Checked"/>
846847
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
847848
<CheckBox x:Name="chkRunAutomatically" Content="Run automatically on startup" ToolTip="Run automatically using startup registry key" HorizontalAlignment="Left" Checked="ChkRunAutomatically_Checked" Unchecked="ChkRunAutomatically_Checked"/>
848849
<CheckBox x:Name="chkRunAutomaticallyMinimized" Content="as minimized" ToolTip="Minimize to tray when started automatically" HorizontalAlignment="Left" Checked="ChkRunAutomaticallyMinimized_Checked" Unchecked="ChkRunAutomaticallyMinimized_Checked" Margin="5,0,0,3"/>

UnityLauncherPro/MainWindow.xaml.cs

+46-3
Original file line numberDiff line numberDiff line change
@@ -276,8 +276,32 @@ void HandleCommandLineLaunch()
276276
string[] args = Environment.GetCommandLineArgs();
277277
if (args != null && args.Length > 2)
278278
{
279+
280+
279281
// first argument needs to be -projectPath
280282
var commandLineArgs = args[1];
283+
284+
// if install argument, then just try to install this file (APK)
285+
if (commandLineArgs == "-install")
286+
{
287+
Console.WriteLine("Launching from commandline...");
288+
289+
// path
290+
var apkPath = args[2];
291+
292+
// resolve full path if path parameter isn't a rooted path
293+
//if (!Path.IsPathRooted(apkPath))
294+
//{
295+
// apkPath = Directory.GetCurrentDirectory() + apkPath;
296+
//}
297+
//MessageBox.Show("APK install not implemented yet: " + apkPath);
298+
// try installing it
299+
Tools.InstallAPK(apkPath);
300+
Environment.Exit(0);
301+
}
302+
else
303+
304+
281305
if (commandLineArgs == "-projectPath")
282306
{
283307
Console.WriteLine("Launching from commandline...");
@@ -328,7 +352,7 @@ void HandleCommandLineLaunch()
328352
}
329353

330354
// quit after launch if enabled in settings
331-
if (Properties.Settings.Default.closeAfterExplorer == true)
355+
if (Settings.Default.closeAfterExplorer == true)
332356
{
333357
Environment.Exit(0);
334358
}
@@ -493,6 +517,7 @@ void LoadSettings()
493517

494518
chkMinimizeToTaskbar.IsChecked = Settings.Default.minimizeToTaskbar;
495519
chkRegisterExplorerMenu.IsChecked = Settings.Default.registerExplorerMenu;
520+
chkRegisterInstallAPKMenu.IsChecked = Settings.Default.registerExplorerMenuAPK;
496521

497522
// update settings window
498523
chkQuitAfterCommandline.IsChecked = Settings.Default.closeAfterExplorer;
@@ -3562,7 +3587,7 @@ private void menuInstallLastAPK_Click(object sender, RoutedEventArgs e)
35623587
}
35633588

35643589
// install the apk using ADB using cmd (-r = replace app)
3565-
var cmd = "cmd.exe";// /C adb install -r \"{playerPath}\"";
3590+
var cmd = "cmd.exe";
35663591
var pars = $"/C adb install -r \"{playerPath}\"";
35673592

35683593
string packageName = null;
@@ -3928,7 +3953,7 @@ private async void chkDisableUnityHubLaunch_Checked(object sender, RoutedEventAr
39283953
{
39293954
if (!this.IsActive) return; // Don't run code during window initialization
39303955

3931-
Console.WriteLine((bool)chkDisableUnityHubLaunch.IsChecked);
3956+
//Console.WriteLine((bool)chkDisableUnityHubLaunch.IsChecked);
39323957

39333958
if ((bool)chkDisableUnityHubLaunch.IsChecked)
39343959
{
@@ -3987,6 +4012,24 @@ private async Task CloseHubPipeAsync()
39874012
}
39884013
}
39894014

4015+
private void chkRegisterInstallAPKMenu_Checked(object sender, RoutedEventArgs e)
4016+
{
4017+
if (this.IsActive == false) return; // dont run code on window init
4018+
4019+
if ((bool)chkRegisterInstallAPKMenu.IsChecked)
4020+
{
4021+
Tools.AddContextMenuRegistryAPKInstall(contextRegRoot);
4022+
}
4023+
else // remove
4024+
{
4025+
Tools.RemoveContextMenuRegistryAPKInstall(contextRegRoot);
4026+
}
4027+
4028+
Settings.Default.registerExplorerMenuAPK = (bool)chkRegisterInstallAPKMenu.IsChecked;
4029+
Settings.Default.Save();
4030+
4031+
}
4032+
39904033

39914034

39924035
//private void menuProjectProperties_Click(object sender, RoutedEventArgs e)

UnityLauncherPro/Properties/Settings.Designer.cs

+12
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

UnityLauncherPro/Properties/Settings.settings

+3
Original file line numberDiff line numberDiff line change
@@ -154,5 +154,8 @@
154154
<Setting Name="disableUnityHubLaunch" Type="System.Boolean" Scope="User">
155155
<Value Profile="(Default)">False</Value>
156156
</Setting>
157+
<Setting Name="registerExplorerMenuAPK" Type="System.Boolean" Scope="User">
158+
<Value Profile="(Default)">False</Value>
159+
</Setting>
157160
</Settings>
158161
</SettingsFile>

UnityLauncherPro/Tools.cs

+175
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using Microsoft.Win32;
22
using System;
33
using System.Collections.Generic;
4+
using System.ComponentModel;
45
using System.Diagnostics;
56
using System.IO;
67
using System.Linq;
@@ -1278,6 +1279,56 @@ public static void AddContextMenuRegistry(string contextRegRoot)
12781279
}
12791280
}
12801281

1282+
public static void AddContextMenuRegistryAPKInstall(string contextRegRoot)
1283+
{
1284+
// Define the registry key path for .apk file association
1285+
string apkFileTypeRegPath = @"Software\Classes\.apk";
1286+
1287+
// Open or create the registry key for .apk files
1288+
RegistryKey apkKey = Registry.CurrentUser.OpenSubKey(apkFileTypeRegPath, true);
1289+
1290+
if (apkKey == null)
1291+
{
1292+
apkKey = Registry.CurrentUser.CreateSubKey(apkFileTypeRegPath);
1293+
}
1294+
1295+
if (apkKey != null)
1296+
{
1297+
// Create or open the Shell subkey for context menu options
1298+
RegistryKey shellKey = apkKey.CreateSubKey("shell", true);
1299+
1300+
if (shellKey != null)
1301+
{
1302+
var appName = "UnityLauncherPro";
1303+
// Create a subkey for the app's context menu item
1304+
RegistryKey appKey = shellKey.CreateSubKey(appName, true);
1305+
1306+
if (appKey != null)
1307+
{
1308+
appKey.SetValue("", "Install with " + appName); // Display name in context menu
1309+
appKey.SetValue("Icon", "\"" + Process.GetCurrentProcess().MainModule.FileName + "\"");
1310+
appKey.SetValue("Position", "Bottom"); // Set position to adjust order
1311+
1312+
// Create the command subkey to specify the action
1313+
RegistryKey commandKey = appKey.CreateSubKey("command", true);
1314+
1315+
if (commandKey != null)
1316+
{
1317+
// Build the command string to launch with -install argument
1318+
var executeString = "\"" + Process.GetCurrentProcess().MainModule.FileName + "\" -install \"%1\"";
1319+
commandKey.SetValue("", executeString);
1320+
}
1321+
}
1322+
}
1323+
}
1324+
else
1325+
{
1326+
Console.WriteLine("Error> Cannot create or access registry key for .apk file association: " + apkFileTypeRegPath);
1327+
}
1328+
}
1329+
1330+
1331+
12811332
/// <summary>
12821333
/// uninstall context menu item from registry
12831334
/// </summary>
@@ -1305,6 +1356,37 @@ public static void RemoveContextMenuRegistry(string contextRegRoot)
13051356
}
13061357
}
13071358

1359+
public static void RemoveContextMenuRegistryAPKInstall(string contextRegRoot)
1360+
{
1361+
// Define the registry key path for .apk file association
1362+
string apkFileTypeRegPath = @"Software\Classes\.apk\shell";
1363+
1364+
// Open the registry key for the shell context menu
1365+
RegistryKey shellKey = Registry.CurrentUser.OpenSubKey(apkFileTypeRegPath, true);
1366+
1367+
if (shellKey != null)
1368+
{
1369+
var appName = "UnityLauncherPro";
1370+
1371+
// Check if the app's context menu key exists
1372+
RegistryKey appKey = shellKey.OpenSubKey(appName, false);
1373+
if (appKey != null)
1374+
{
1375+
// Delete the app's context menu key
1376+
shellKey.DeleteSubKeyTree(appName);
1377+
Console.WriteLine("Removed context menu for .apk files.");
1378+
}
1379+
else
1380+
{
1381+
Console.WriteLine("No context menu found for .apk files.");
1382+
}
1383+
}
1384+
else
1385+
{
1386+
Console.WriteLine("Error> Cannot find registry key for .apk shell context: " + apkFileTypeRegPath);
1387+
}
1388+
}
1389+
13081390
/// <summary>
13091391
/// reads .git/HEAD file from the project to get current branch name
13101392
/// </summary>
@@ -2505,6 +2587,99 @@ internal static string GetSRP(string projectPath)
25052587
}
25062588

25072589
}
2590+
2591+
internal static void InstallAPK(string ApkPath)
2592+
{
2593+
try
2594+
{
2595+
var cmd = "cmd.exe";
2596+
var pars = $"/C adb install -r \"{ApkPath}\""; // Use /C to execute and close the window after finishing
2597+
2598+
var processStartInfo = new ProcessStartInfo
2599+
{
2600+
FileName = cmd,
2601+
Arguments = pars,
2602+
RedirectStandardOutput = true, // Capture output to wait for completion
2603+
RedirectStandardError = true,
2604+
UseShellExecute = false,
2605+
CreateNoWindow = false
2606+
};
2607+
2608+
string installOutput = null;
2609+
string installError = null;
2610+
2611+
using (var installProcess = Process.Start(processStartInfo))
2612+
{
2613+
installOutput = installProcess.StandardOutput.ReadToEnd();
2614+
installError = installProcess.StandardError.ReadToEnd();
2615+
installProcess.WaitForExit();
2616+
2617+
if (installProcess.ExitCode != 0 || !string.IsNullOrEmpty(installError))
2618+
{
2619+
SetStatus($"Error installing APK: {installError.Trim()}\n{installOutput.Trim()}");
2620+
return;
2621+
}
2622+
}
2623+
2624+
// Attempt to extract package name using aapt
2625+
var aaptCmd = $"aapt dump badging \"{ApkPath}\" | findstr package:";
2626+
var aaptProcessStartInfo = new ProcessStartInfo
2627+
{
2628+
FileName = "cmd.exe",
2629+
Arguments = $"/C {aaptCmd}",
2630+
RedirectStandardOutput = true,
2631+
UseShellExecute = false,
2632+
CreateNoWindow = true
2633+
};
2634+
2635+
string packageName = null;
2636+
using (var process = Process.Start(aaptProcessStartInfo))
2637+
{
2638+
var output = process.StandardOutput.ReadToEnd();
2639+
process.WaitForExit();
2640+
2641+
var match = System.Text.RegularExpressions.Regex.Match(output, "package: name='(.*?)'");
2642+
if (match.Success)
2643+
{
2644+
packageName = match.Groups[1].Value;
2645+
}
2646+
}
2647+
2648+
if (!string.IsNullOrEmpty(packageName))
2649+
{
2650+
// Run the application using adb
2651+
var runPars = $"/C adb shell monkey -p {packageName} 1";
2652+
var runProcessStartInfo = new ProcessStartInfo
2653+
{
2654+
FileName = cmd,
2655+
Arguments = runPars,
2656+
UseShellExecute = true,
2657+
CreateNoWindow = false,
2658+
WindowStyle = ProcessWindowStyle.Normal
2659+
};
2660+
Process.Start(runProcessStartInfo);
2661+
2662+
SetStatus($"Installed and launched APK with package name: {packageName}");
2663+
}
2664+
else
2665+
{
2666+
SetStatus("ADB install completed, but failed to extract package name. Application not launched.");
2667+
}
2668+
}
2669+
catch (Win32Exception ex)
2670+
{
2671+
// Handle case where adb or aapt is not found
2672+
SetStatus($"Error: Required tool not found. Ensure adb and aapt are installed and added to PATH. Details: {ex.Message}");
2673+
}
2674+
catch (Exception ex)
2675+
{
2676+
// Handle other unexpected exceptions
2677+
SetStatus($"An unexpected error occurred: {ex.Message}");
2678+
}
2679+
}
2680+
2681+
2682+
25082683
} // class
25092684

25102685
} // namespace

0 commit comments

Comments
 (0)