Initial commit (1.0.0)
Build and Relase / build-release (push) Failing after 38s
Build and Relase / create-release (push) Failing after 10s

This commit is contained in:
2025-01-13 16:27:29 +01:00
commit 26cde137c9
36 changed files with 4875 additions and 0 deletions
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
</startup>
</configuration>
+40
View File
@@ -0,0 +1,40 @@
namespace SoraV2BatteryHelperNotification
{
partial class NotificationInstaller
{
/// <summary>
/// Erforderliche Designervariable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Verwendete Ressourcen bereinigen.
/// </summary>
/// <param name="disposing">True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Vom Komponenten-Designer generierter Code
/// <summary>
/// Erforderliche Methode für die Designerunterstützung.
/// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden.
/// </summary>
private void InitializeComponent()
{
//
// Installer1
//
this.BeforeUninstall += new System.Configuration.Install.InstallEventHandler(this.NotificationInstaller_BeforeUninstall);
}
#endregion
}
}
@@ -0,0 +1,25 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration.Install;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace SoraV2BatteryHelperNotification
{
[RunInstaller(true)]
public partial class NotificationInstaller : System.Configuration.Install.Installer
{
public NotificationInstaller()
{
InitializeComponent();
}
private void NotificationInstaller_BeforeUninstall(object sender, InstallEventArgs e)
{
System.Environment.Exit(1);
}
}
}
@@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="$this.TrayLargeIcon" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
</root>
+28
View File
@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Data.Xml.Dom;
using Windows.UI.Notifications;
namespace SoraV2Utils_Agent
{
public interface IToastNotification
{
void SendToastNotification(string xml, string title);
}
public class NotificationSender : IToastNotification
{
public void SendToastNotification(string xml, string title)
{
XmlDocument tileXml = new XmlDocument();
tileXml.LoadXml(xml);
var toastNotification = new ToastNotification(tileXml);
toastNotification.Priority = ToastNotificationPriority.High;
ToastNotificationManager.CreateToastNotifier(title).Show(toastNotification);
}
}
}
+33
View File
@@ -0,0 +1,33 @@
using EasyPipes;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.ServiceProcess;
namespace SoraV2Utils_Agent
{
internal class Program
{
static void Main(string[] args)
{
new ServiceIPC();
Server server = new Server("SoraV2UtilsNotifcation");
server.RegisterService<IToastNotification>(new NotificationSender());
server.Start();
var serviceMonitor = new ServiceMonitor("SoraV2Utils_Service");
serviceMonitor.ServiceStopped += (sender, e) =>
{
server.Stop();
System.Environment.Exit(1);
};;
var trayicon = new TrayIcon();
trayicon.Display();
}
}
}
@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// Allgemeine Informationen über eine Assembly werden über die folgenden
// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
// die einer Assembly zugeordnet sind.
[assembly: AssemblyTitle("SoraV2Utils_Agent")]
[assembly: AssemblyDescription("Agent for SoraV2 Utils")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("SoraV2Utils_Agent")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly
// für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von
// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen.
[assembly: ComVisible(false)]
// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
[assembly: Guid("763d3776-c431-44dc-81ea-721b66f5fe65")]
// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
//
// Hauptversion
// Nebenversion
// Buildnummer
// Revision
//
// Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden,
// indem Sie "*" wie unten gezeigt eingeben:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
+87
View File
@@ -0,0 +1,87 @@
using EasyPipes;
using System;
using System.Runtime.InteropServices;
using System.Threading;
namespace SoraV2Utils_Agent
{
public struct DeviceStatus
{
public byte Battery;
public byte Charging;
public byte FullCharge;
public byte Online;
}
public interface IMouseData
{
byte[] GetDeviceStatus();
double GetBatteryRuntime();
void Exit();
}
public class ServiceIPC
{
public static ServiceIPC Instance;
public static DeviceStatus DeviceStatus;
public static double BatteryRuntime = 0;
public ServiceIPC()
{
ThreadPool.QueueUserWorkItem(state =>
{
while (true)
{
GetDeviceDataFromService();
Thread.Sleep(5000); // Periodically fetch data every 5 seconds
}
});
Instance = this;
}
public static void GetDeviceDataFromService()
{
var client = new Client("SoraV2UtilsDeviceData");
var service = client.GetServiceProxy<IMouseData>();
try
{
// Allocate memory and copy device status data
int size = Marshal.SizeOf(typeof(DeviceStatus));
IntPtr ptr = Marshal.AllocHGlobal(size);
try
{
byte[] statusData = service.GetDeviceStatus();
Marshal.Copy(statusData, 0, ptr, size);
// Marshal data into DeviceStatus struct
DeviceStatus = (DeviceStatus)Marshal.PtrToStructure(ptr, typeof(DeviceStatus));
}
finally
{
// Clean up allocated memory
Marshal.FreeHGlobal(ptr);
}
BatteryRuntime = service.GetBatteryRuntime();
}
catch (Exception ex)
{
//TODO: Log or handle exceptions properly
Console.WriteLine($"Error fetching device data: {ex.Message}");
}
}
public void Exit()
{
var client = new Client("SoraV2UtilsDeviceData");
var service = client.GetServiceProxy<IMouseData>();
service.Exit();
// Exit the application gracefully
Environment.Exit(0);
}
}
}
+55
View File
@@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
namespace SoraV2Utils_Agent
{
public class ServiceMonitor
{
// Event to notify when the service stops
public event EventHandler ServiceStopped;
private Timer _timer;
private string _serviceName;
public ServiceMonitor(string serviceName)
{
_serviceName = serviceName;
// Set up the timer to check service status periodically
_timer = new Timer(1000);
_timer.Elapsed += CheckServiceStatus;
_timer.Start();
}
private void CheckServiceStatus(object sender, ElapsedEventArgs e)
{
try
{
ServiceController serviceController = new ServiceController(_serviceName);
// Check the current status of the service
if (serviceController.Status == ServiceControllerStatus.Stopped)
{
// Raise the ServiceStopped event
OnServiceStopped();
_timer.Stop(); // Stop the timer after detecting that the service has stopped
}
}
catch (Exception ex)
{
Console.WriteLine($"Error checking service status: {ex.Message}");
}
}
// Event handler to raise the ServiceStopped event
protected virtual void OnServiceStopped()
{
ServiceStopped?.Invoke(this, EventArgs.Empty);
}
}
}
+120
View File
@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{763D3776-C431-44DC-81EA-721B66F5FE65}</ProjectGuid>
<OutputType>WinExe</OutputType>
<RootNamespace>SoraV2Utils_Agent</RootNamespace>
<AssemblyName>SoraV2Utils_Agent</AssemblyName>
<targetplatformversion>8.0</targetplatformversion>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>
<IsWebBootstrapper>false</IsWebBootstrapper>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
</PropertyGroup>
<PropertyGroup>
<RuntimeIdentifiers>win</RuntimeIdentifiers>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<targetplatformversion>8.0</targetplatformversion>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\build\Release\</OutputPath>
<targetplatformversion>8.0</targetplatformversion>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<StartupObject />
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Configuration.Install" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Management" />
<Reference Include="System.ServiceProcess" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<Reference Include="Windows.Data" />
<Reference Include="Windows.UI" />
</ItemGroup>
<ItemGroup>
<Compile Include="NotificationInstaller.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="NotificationInstaller.Designer.cs">
<DependentUpon>NotificationInstaller.cs</DependentUpon>
</Compile>
<Compile Include="ServiceIPC.cs" />
<Compile Include="NotificationSender.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ServiceMonitor.cs" />
<Compile Include="TrayIcon.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="EasyPipes">
<Version>1.3.0</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include=".NETFramework,Version=v4.7.2">
<Visible>False</Visible>
<ProductName>Microsoft .NET Framework 4.7.2 %28x86 und x64%29</ProductName>
<Install>true</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>false</Install>
</BootstrapperPackage>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="NotificationInstaller.resx">
<DependentUpon>NotificationInstaller.cs</DependentUpon>
</EmbeddedResource>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
</Project>
+93
View File
@@ -0,0 +1,93 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing;
using System.Threading;
namespace SoraV2Utils_Agent
{
public class TrayIcon
{
public TrayIcon()
{
// Create a NotifyIcon instance
using (NotifyIcon trayIcon = new NotifyIcon())
{
// Set up the tray icon properties
trayIcon.Icon = new Icon(SystemIcons.WinLogo, 40, 40); // Choose your own icon
trayIcon.Visible = true;
// Create a context menu
var contextMenu = new ContextMenu();
// Add a non-clickable dynamic text item
var batteryLevelItem = new MenuItem("Battery Level: Loading...");
var batteryRuntimeItem = new MenuItem("Battery Runtime: Loading...");
batteryLevelItem.Enabled = false;
batteryRuntimeItem.Enabled = false;
// Add an exit menu item
contextMenu.MenuItems.Add("Exit", (sender, e) => ServiceIPC.Instance.Exit());
contextMenu.MenuItems.Add(batteryLevelItem);
contextMenu.MenuItems.Add(batteryRuntimeItem);
trayIcon.ContextMenu = contextMenu;
// Update the dynamic text in the context menu
ThreadPool.QueueUserWorkItem(state =>
{
while (true)
{
// Format battery level with charging status
string batteryLevelText = ServiceIPC.DeviceStatus.Charging == 1
? $"Battery Level: {ServiceIPC.DeviceStatus.Battery} (Charging...)"
: $"Battery Level: {ServiceIPC.DeviceStatus.Battery}";
// Format battery runtime (convert minutes to hours and minutes)
string batteryRuntimeText = FormatBatteryRuntime(ServiceIPC.BatteryRuntime);
// Update the context menu items
batteryLevelItem.Text = batteryLevelText;
batteryRuntimeItem.Text = $"Battery Runtime: {batteryRuntimeText}";
// Update every second
Thread.Sleep(1000);
}
});
Application.Run();
}
}
// Formats the runtime in hours and minutes.
private string FormatBatteryRuntime(double runtimeInMinutes)
{
if(runtimeInMinutes < 0)
{
return "No data yet";
}
int hours = (int)(runtimeInMinutes / 60);
int minutes = (int)(runtimeInMinutes % 60);
// If runtime is greater than 1 hour, display hours and minutes
if (hours > 0)
{
return $"{hours} hour{(hours > 1 ? "s" : "")} {minutes} minute{(minutes > 1 ? "s" : "")}";
}
else
{
return $"{minutes} minute{(minutes > 1 ? "s" : "")}";
}
}
public void Display()
{
}
}
}