Skip to content

Commit

Permalink
add optional Explorer context menu to Install (and run) selected APK …
Browse files Browse the repository at this point in the history
…file to device (fixes #181)
  • Loading branch information
unitycoder committed Jan 15, 2025
1 parent 54dab25 commit 735d448
Show file tree
Hide file tree
Showing 5 changed files with 239 additions and 5 deletions.
5 changes: 3 additions & 2 deletions UnityLauncherPro/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:converters="clr-namespace:UnityLauncherPro.Converters" x:Class="UnityLauncherPro.MainWindow"
mc:Ignorable="d"
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">
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">
<Window.Resources>
<converters:LastModifiedConverter x:Key="lastModifiedConverter"/>
<converters:LastModifiedConverterTooltip x:Key="LastModifiedConverterTooltip"/>
Expand Down Expand Up @@ -842,7 +842,8 @@

<GroupBox Grid.Row="3" Header="System" VerticalAlignment="Top" Margin="0,0,3,0">
<StackPanel Grid.Row="3" Orientation="Vertical" Margin="3,5,0,0" >
<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"/>
<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"/>
<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"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<CheckBox x:Name="chkRunAutomatically" Content="Run automatically on startup" ToolTip="Run automatically using startup registry key" HorizontalAlignment="Left" Checked="ChkRunAutomatically_Checked" Unchecked="ChkRunAutomatically_Checked"/>
<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"/>
Expand Down
49 changes: 46 additions & 3 deletions UnityLauncherPro/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -276,8 +276,32 @@ void HandleCommandLineLaunch()
string[] args = Environment.GetCommandLineArgs();
if (args != null && args.Length > 2)
{


// first argument needs to be -projectPath
var commandLineArgs = args[1];

// if install argument, then just try to install this file (APK)
if (commandLineArgs == "-install")
{
Console.WriteLine("Launching from commandline...");

// path
var apkPath = args[2];

// resolve full path if path parameter isn't a rooted path
//if (!Path.IsPathRooted(apkPath))
//{
// apkPath = Directory.GetCurrentDirectory() + apkPath;
//}
//MessageBox.Show("APK install not implemented yet: " + apkPath);
// try installing it
Tools.InstallAPK(apkPath);
Environment.Exit(0);
}
else


if (commandLineArgs == "-projectPath")
{
Console.WriteLine("Launching from commandline...");
Expand Down Expand Up @@ -328,7 +352,7 @@ void HandleCommandLineLaunch()
}

// quit after launch if enabled in settings
if (Properties.Settings.Default.closeAfterExplorer == true)
if (Settings.Default.closeAfterExplorer == true)
{
Environment.Exit(0);
}
Expand Down Expand Up @@ -493,6 +517,7 @@ void LoadSettings()

chkMinimizeToTaskbar.IsChecked = Settings.Default.minimizeToTaskbar;
chkRegisterExplorerMenu.IsChecked = Settings.Default.registerExplorerMenu;
chkRegisterInstallAPKMenu.IsChecked = Settings.Default.registerExplorerMenuAPK;

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

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

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

Console.WriteLine((bool)chkDisableUnityHubLaunch.IsChecked);
//Console.WriteLine((bool)chkDisableUnityHubLaunch.IsChecked);

if ((bool)chkDisableUnityHubLaunch.IsChecked)
{
Expand Down Expand Up @@ -3987,6 +4012,24 @@ private async Task CloseHubPipeAsync()
}
}

private void chkRegisterInstallAPKMenu_Checked(object sender, RoutedEventArgs e)
{
if (this.IsActive == false) return; // dont run code on window init

if ((bool)chkRegisterInstallAPKMenu.IsChecked)
{
Tools.AddContextMenuRegistryAPKInstall(contextRegRoot);
}
else // remove
{
Tools.RemoveContextMenuRegistryAPKInstall(contextRegRoot);
}

Settings.Default.registerExplorerMenuAPK = (bool)chkRegisterInstallAPKMenu.IsChecked;
Settings.Default.Save();

}



//private void menuProjectProperties_Click(object sender, RoutedEventArgs e)
Expand Down
12 changes: 12 additions & 0 deletions UnityLauncherPro/Properties/Settings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions UnityLauncherPro/Properties/Settings.settings
Original file line number Diff line number Diff line change
Expand Up @@ -154,5 +154,8 @@
<Setting Name="disableUnityHubLaunch" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="registerExplorerMenuAPK" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
</Settings>
</SettingsFile>
175 changes: 175 additions & 0 deletions UnityLauncherPro/Tools.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
Expand Down Expand Up @@ -1278,6 +1279,56 @@ public static void AddContextMenuRegistry(string contextRegRoot)
}
}

public static void AddContextMenuRegistryAPKInstall(string contextRegRoot)
{
// Define the registry key path for .apk file association
string apkFileTypeRegPath = @"Software\Classes\.apk";

// Open or create the registry key for .apk files
RegistryKey apkKey = Registry.CurrentUser.OpenSubKey(apkFileTypeRegPath, true);

if (apkKey == null)
{
apkKey = Registry.CurrentUser.CreateSubKey(apkFileTypeRegPath);
}

if (apkKey != null)
{
// Create or open the Shell subkey for context menu options
RegistryKey shellKey = apkKey.CreateSubKey("shell", true);

if (shellKey != null)
{
var appName = "UnityLauncherPro";
// Create a subkey for the app's context menu item
RegistryKey appKey = shellKey.CreateSubKey(appName, true);

if (appKey != null)
{
appKey.SetValue("", "Install with " + appName); // Display name in context menu
appKey.SetValue("Icon", "\"" + Process.GetCurrentProcess().MainModule.FileName + "\"");
appKey.SetValue("Position", "Bottom"); // Set position to adjust order

// Create the command subkey to specify the action
RegistryKey commandKey = appKey.CreateSubKey("command", true);

if (commandKey != null)
{
// Build the command string to launch with -install argument
var executeString = "\"" + Process.GetCurrentProcess().MainModule.FileName + "\" -install \"%1\"";
commandKey.SetValue("", executeString);
}
}
}
}
else
{
Console.WriteLine("Error> Cannot create or access registry key for .apk file association: " + apkFileTypeRegPath);
}
}



/// <summary>
/// uninstall context menu item from registry
/// </summary>
Expand Down Expand Up @@ -1305,6 +1356,37 @@ public static void RemoveContextMenuRegistry(string contextRegRoot)
}
}

public static void RemoveContextMenuRegistryAPKInstall(string contextRegRoot)
{
// Define the registry key path for .apk file association
string apkFileTypeRegPath = @"Software\Classes\.apk\shell";

// Open the registry key for the shell context menu
RegistryKey shellKey = Registry.CurrentUser.OpenSubKey(apkFileTypeRegPath, true);

if (shellKey != null)
{
var appName = "UnityLauncherPro";

// Check if the app's context menu key exists
RegistryKey appKey = shellKey.OpenSubKey(appName, false);
if (appKey != null)
{
// Delete the app's context menu key
shellKey.DeleteSubKeyTree(appName);
Console.WriteLine("Removed context menu for .apk files.");
}
else
{
Console.WriteLine("No context menu found for .apk files.");
}
}
else
{
Console.WriteLine("Error> Cannot find registry key for .apk shell context: " + apkFileTypeRegPath);
}
}

/// <summary>
/// reads .git/HEAD file from the project to get current branch name
/// </summary>
Expand Down Expand Up @@ -2505,6 +2587,99 @@ internal static string GetSRP(string projectPath)
}

}

internal static void InstallAPK(string ApkPath)
{
try
{
var cmd = "cmd.exe";
var pars = $"/C adb install -r \"{ApkPath}\""; // Use /C to execute and close the window after finishing

var processStartInfo = new ProcessStartInfo
{
FileName = cmd,
Arguments = pars,
RedirectStandardOutput = true, // Capture output to wait for completion
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = false
};

string installOutput = null;
string installError = null;

using (var installProcess = Process.Start(processStartInfo))
{
installOutput = installProcess.StandardOutput.ReadToEnd();
installError = installProcess.StandardError.ReadToEnd();
installProcess.WaitForExit();

if (installProcess.ExitCode != 0 || !string.IsNullOrEmpty(installError))
{
SetStatus($"Error installing APK: {installError.Trim()}\n{installOutput.Trim()}");
return;
}
}

// Attempt to extract package name using aapt
var aaptCmd = $"aapt dump badging \"{ApkPath}\" | findstr package:";
var aaptProcessStartInfo = new ProcessStartInfo
{
FileName = "cmd.exe",
Arguments = $"/C {aaptCmd}",
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true
};

string packageName = null;
using (var process = Process.Start(aaptProcessStartInfo))
{
var output = process.StandardOutput.ReadToEnd();
process.WaitForExit();

var match = System.Text.RegularExpressions.Regex.Match(output, "package: name='(.*?)'");
if (match.Success)
{
packageName = match.Groups[1].Value;
}
}

if (!string.IsNullOrEmpty(packageName))
{
// Run the application using adb
var runPars = $"/C adb shell monkey -p {packageName} 1";
var runProcessStartInfo = new ProcessStartInfo
{
FileName = cmd,
Arguments = runPars,
UseShellExecute = true,
CreateNoWindow = false,
WindowStyle = ProcessWindowStyle.Normal
};
Process.Start(runProcessStartInfo);

SetStatus($"Installed and launched APK with package name: {packageName}");
}
else
{
SetStatus("ADB install completed, but failed to extract package name. Application not launched.");
}
}
catch (Win32Exception ex)
{
// Handle case where adb or aapt is not found
SetStatus($"Error: Required tool not found. Ensure adb and aapt are installed and added to PATH. Details: {ex.Message}");
}
catch (Exception ex)
{
// Handle other unexpected exceptions
SetStatus($"An unexpected error occurred: {ex.Message}");
}
}



} // class

} // namespace

0 comments on commit 735d448

Please sign in to comment.