Added Main Project
This commit is contained in:
parent
3272781a12
commit
724c410f36
454
.gitignore
vendored
Normal file
454
.gitignore
vendored
Normal file
@ -0,0 +1,454 @@
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Mono auto generated files
|
||||
mono_crash.*
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
[Ww][Ii][Nn]32/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
[Ll]ogs/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUnit
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
nunit-*.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
# Tye
|
||||
.tye/
|
||||
|
||||
# ASP.NET Scaffolding
|
||||
ScaffoldingReadMe.txt
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
||||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_h.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.iobj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*_wpftmp.csproj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Coverlet is a free, cross platform Code Coverage Tool
|
||||
coverage*.json
|
||||
coverage*.xml
|
||||
coverage*.info
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# NuGet Symbol Packages
|
||||
*.snupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/[Pp]ackages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/[Pp]ackages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/[Pp]ackages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
*.appxbundle
|
||||
*.appxupload
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!?*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
*- [Bb]ackup.rdl
|
||||
*- [Bb]ackup ([0-9]).rdl
|
||||
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# CodeRush personal settings
|
||||
.cr/personal
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
|
||||
# Local History for Visual Studio
|
||||
.localhistory/
|
||||
|
||||
# BeatPulse healthcheck temp database
|
||||
healthchecksdb
|
||||
|
||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||
MigrationBackup/
|
||||
|
||||
# Ionide (cross platform F# VS Code tools) working folder
|
||||
.ionide/
|
||||
|
||||
# Fody - auto-generated XML schema
|
||||
FodyWeavers.xsd
|
||||
|
||||
##
|
||||
## Visual studio for Mac
|
||||
##
|
||||
|
||||
|
||||
# globs
|
||||
Makefile.in
|
||||
*.userprefs
|
||||
*.usertasks
|
||||
config.make
|
||||
config.status
|
||||
aclocal.m4
|
||||
install-sh
|
||||
autom4te.cache/
|
||||
*.tar.gz
|
||||
tarballs/
|
||||
test-results/
|
||||
|
||||
# Mac bundle stuff
|
||||
*.dmg
|
||||
*.app
|
||||
|
||||
# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore
|
||||
# Windows thumbnail cache files
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
ehthumbs_vista.db
|
||||
|
||||
# Dump file
|
||||
*.stackdump
|
||||
|
||||
# Folder config file
|
||||
[Dd]esktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Windows Installer files
|
||||
*.cab
|
||||
*.msi
|
||||
*.msix
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
# JetBrains Rider
|
||||
.idea/
|
||||
*.sln.iml
|
||||
|
||||
##
|
||||
## Visual Studio Code
|
||||
##
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
37
TeamsNetphoneLink.sln
Normal file
37
TeamsNetphoneLink.sln
Normal file
@ -0,0 +1,37 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.10.35027.167
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TeamsLocalLibary", "TeamsLocalAPI\TeamsLocalLibary.csproj", "{0075EE3E-C82F-4EAA-A9A3-32BED89C90EA}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TeamsNetphoneLinkWPF", "TeamsNetphoneLinkWPF\TeamsNetphoneLinkWPF.csproj", "{5904F1F3-755E-4D10-8906-5FC69F85F389}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TeamsNetphoneLinkUpdater", "TeamsNetphoneLinkUpdater\TeamsNetphoneLinkUpdater.csproj", "{BB18A636-95F8-4555-9E96-31575E9CB6A2}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{0075EE3E-C82F-4EAA-A9A3-32BED89C90EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0075EE3E-C82F-4EAA-A9A3-32BED89C90EA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0075EE3E-C82F-4EAA-A9A3-32BED89C90EA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0075EE3E-C82F-4EAA-A9A3-32BED89C90EA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{5904F1F3-755E-4D10-8906-5FC69F85F389}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5904F1F3-755E-4D10-8906-5FC69F85F389}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5904F1F3-755E-4D10-8906-5FC69F85F389}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5904F1F3-755E-4D10-8906-5FC69F85F389}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{BB18A636-95F8-4555-9E96-31575E9CB6A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{BB18A636-95F8-4555-9E96-31575E9CB6A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{BB18A636-95F8-4555-9E96-31575E9CB6A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{BB18A636-95F8-4555-9E96-31575E9CB6A2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {32340761-08D0-41C6-A769-B606A5B73F01}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
63
TeamsNetphoneLinkWPF/App.config
Normal file
63
TeamsNetphoneLinkWPF/App.config
Normal file
@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<configSections>
|
||||
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
|
||||
<section name="TeamsNetphoneLink.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
|
||||
<section name="TeamsNetphoneLink.Settings1" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
|
||||
<section name="TeamsNetphoneLinkWPF.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
|
||||
</sectionGroup>
|
||||
</configSections>
|
||||
<userSettings>
|
||||
<TeamsNetphoneLink.Settings>
|
||||
<setting name="Token" serializeAs="String">
|
||||
<value />
|
||||
</setting>
|
||||
<setting name="TeamsPermission" serializeAs="String">
|
||||
<value>False</value>
|
||||
</setting>
|
||||
<setting name="TenantID" serializeAs="String">
|
||||
<value />
|
||||
</setting>
|
||||
<setting name="AppID" serializeAs="String">
|
||||
<value />
|
||||
</setting>
|
||||
<setting name="SaveEntraCredentials" serializeAs="String">
|
||||
<value>False</value>
|
||||
</setting>
|
||||
<setting name="UseGraphForMeetingState" serializeAs="String">
|
||||
<value>False</value>
|
||||
</setting>
|
||||
<setting name="CurrentVersion" serializeAs="String">
|
||||
<value />
|
||||
</setting>
|
||||
</TeamsNetphoneLink.Settings>
|
||||
<TeamsNetphoneLink.Settings1>
|
||||
<setting name="Token" serializeAs="String">
|
||||
<value />
|
||||
</setting>
|
||||
<setting name="TeamsPermission" serializeAs="String">
|
||||
<value>True</value>
|
||||
</setting>
|
||||
<setting name="TenatID" serializeAs="String">
|
||||
<value />
|
||||
</setting>
|
||||
<setting name="AppID" serializeAs="String">
|
||||
<value />
|
||||
</setting>
|
||||
</TeamsNetphoneLink.Settings1>
|
||||
<TeamsNetphoneLinkWPF.Settings>
|
||||
<setting name="Token" serializeAs="String">
|
||||
<value />
|
||||
</setting>
|
||||
<setting name="TeamsPermission" serializeAs="String">
|
||||
<value>True</value>
|
||||
</setting>
|
||||
<setting name="TenatID" serializeAs="String">
|
||||
<value />
|
||||
</setting>
|
||||
<setting name="AppID" serializeAs="String">
|
||||
<value />
|
||||
</setting>
|
||||
</TeamsNetphoneLinkWPF.Settings>
|
||||
</userSettings>
|
||||
</configuration>
|
9
TeamsNetphoneLinkWPF/App.xaml
Normal file
9
TeamsNetphoneLinkWPF/App.xaml
Normal file
@ -0,0 +1,9 @@
|
||||
<Application x:Class="TeamsNetphoneLink.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:TeamsNetphoneLink.WPF"
|
||||
>
|
||||
<Application.Resources>
|
||||
|
||||
</Application.Resources>
|
||||
</Application>
|
79
TeamsNetphoneLinkWPF/App.xaml.cs
Normal file
79
TeamsNetphoneLinkWPF/App.xaml.cs
Normal file
@ -0,0 +1,79 @@
|
||||
using Azure.Identity.Broker;
|
||||
using Microsoft.Identity.Client;
|
||||
using Microsoft.Identity.Client.NativeInterop;
|
||||
using System;
|
||||
using System.Configuration;
|
||||
using System.Data;
|
||||
using System.Printing;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Media;
|
||||
using TeamsNetphoneLink.WPF;
|
||||
using TeamsNetphoneLink.WPF.MVVM;
|
||||
|
||||
namespace TeamsNetphoneLink
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for App.xaml
|
||||
/// </summary>
|
||||
public partial class App : Application
|
||||
{
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool AllocConsole();
|
||||
|
||||
public App()
|
||||
{
|
||||
#if DEBUG
|
||||
// Konsole für Debugging-Zwecke öffnen
|
||||
AllocConsole();
|
||||
#endif
|
||||
// Initialisierung der Anwendungskomponenten
|
||||
InitializeApplication();
|
||||
}
|
||||
|
||||
private void InitializeApplication()
|
||||
{
|
||||
UpdateCheck.UpgradeSettingsIfRequired();
|
||||
|
||||
|
||||
|
||||
// Erstellen der benötigten Instanzen
|
||||
var TeamsLocalAPI = new Teams.TeamsLocalAPI();
|
||||
var Netphone = new Netphone.NetPhoneEvents();
|
||||
var TeamsGraph = new Teams.TeamsGraph();
|
||||
var dashboardViewModel = new DashboardViewModel();
|
||||
var LogEntries = new LogViewModel();
|
||||
var finishPanelViewModel = new FinishPanelViewModel();
|
||||
|
||||
var latestTag = UpdateCheck.GetLatestReleaseTagAsync().GetAwaiter().GetResult();
|
||||
var currentTag = UpdateCheck.GetCurrentVersion();
|
||||
dashboardViewModel.VersionText = currentTag;
|
||||
|
||||
// Erstellen der Synchronisationsinstanz
|
||||
var sync = new Syncronisation(Netphone, TeamsLocalAPI, TeamsGraph, dashboardViewModel, LogEntries, finishPanelViewModel);
|
||||
|
||||
// Erstellen und Anzeigen des Dashboard-Fensters
|
||||
var dashboard = new WPF.DashboardWindow(sync);
|
||||
dashboard.Show();
|
||||
|
||||
if (UpdateCheck.IsVersionLower(currentTag, latestTag))
|
||||
{
|
||||
UpdateCheck.UpdateAvailable = true;
|
||||
dashboardViewModel.VersionText = String.Format("Version {0} verfügbar!", latestTag);
|
||||
dashboardViewModel.VersionBackground = new SolidColorBrush(Colors.Orange);
|
||||
|
||||
// Erstellen und Anzeigen des Update-Fensters
|
||||
var updateWindow = new WPF.UpdateWindow(currentTag, latestTag);
|
||||
updateWindow.Show();
|
||||
}
|
||||
|
||||
// Asynchrone Initialisierung von Netphone und TeamsLocalAPI
|
||||
sync.InitializeTeamsGraphAsync();
|
||||
sync.InitializeNetphoneAsync();
|
||||
sync.InitializeTeamsLocalAPIAsync();
|
||||
}
|
||||
}
|
||||
}
|
10
TeamsNetphoneLinkWPF/AssemblyInfo.cs
Normal file
10
TeamsNetphoneLinkWPF/AssemblyInfo.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using System.Windows;
|
||||
|
||||
[assembly: ThemeInfo(
|
||||
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||
//(used if a resource is not found in the page,
|
||||
// or application resource dictionaries)
|
||||
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||
//(used if a resource is not found in the page,
|
||||
// app, or any theme specific resource dictionaries)
|
||||
)]
|
121
TeamsNetphoneLinkWPF/Communication/ClientSdkEvents.cs
Normal file
121
TeamsNetphoneLinkWPF/Communication/ClientSdkEvents.cs
Normal file
@ -0,0 +1,121 @@
|
||||
using CLMGRLib;
|
||||
|
||||
namespace TeamsNetphoneLink.Netphone
|
||||
{
|
||||
public class ClientSdkEventArgs : EventArgs
|
||||
{
|
||||
public CLMgrMessage Msg;
|
||||
public int Param;
|
||||
|
||||
public ClientSdkEventArgs(CLMgrMessage msg, int param)
|
||||
{
|
||||
Msg = msg;
|
||||
Param = param;
|
||||
}
|
||||
}
|
||||
|
||||
public delegate void LineManagerMessageHandler(ClientSdkEventArgs e);
|
||||
|
||||
public class ClientSdkEventSink
|
||||
{
|
||||
private ClientLineMgrClass ConnectedLineManager;
|
||||
private IClientLineMgrEventsPub_PubOnLineMgrNotificationEventHandler EventHandler;
|
||||
private LineManagerMessageHandler LineManagerMessageDelegateOfForm;
|
||||
|
||||
public ClientSdkEventSink()
|
||||
{
|
||||
EventHandler = new IClientLineMgrEventsPub_PubOnLineMgrNotificationEventHandler(clmgr_EventSink);
|
||||
}
|
||||
|
||||
public void Connect(ClientLineMgrClass lineManager, LineManagerMessageHandler lineManagerMessageDelegateOfForm)
|
||||
{
|
||||
ConnectedLineManager = lineManager;
|
||||
LineManagerMessageDelegateOfForm = lineManagerMessageDelegateOfForm;
|
||||
|
||||
//add eventhandler for the PubOnlineMgrNotification Events
|
||||
ConnectedLineManager.PubOnLineMgrNotification += EventHandler;
|
||||
}
|
||||
|
||||
public void Disconnect()
|
||||
{
|
||||
//remove eventhandler for the PubOnlineMgrNotification Events
|
||||
ConnectedLineManager.PubOnLineMgrNotification -= EventHandler;
|
||||
ConnectedLineManager = null;
|
||||
LineManagerMessageDelegateOfForm = null;
|
||||
}
|
||||
|
||||
private void clmgr_EventSink(int msg, int param)
|
||||
{
|
||||
//this method receives the COM events from the client line manger
|
||||
if ((LineManagerMessageDelegateOfForm != null))
|
||||
{
|
||||
LineManagerMessageDelegateOfForm(new ClientSdkEventArgs((CLMgrMessage)msg, param));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum CLMgrMessage
|
||||
{
|
||||
CLMgrLineStateChangedMessage = 0, //state of at least one line has changed
|
||||
CLMgrLineSelectionChangedMessage = 1, //line in focus has changed
|
||||
CLMgrLineDetailsChangedMessage = 2, //details of at least one line have changed
|
||||
CLMgrCallDetailsMessage = 4, //details of last call are available, post mortem for logging purpose
|
||||
CLMgrServerDownMessage = 5, //server goes down, keep line manager, wait for ServerUp message
|
||||
CLMgrServerUpMessage = 6, //server is up again, keep interfaces to line manger
|
||||
CLMgrWaveDeviceChanged = 7, //speaker / micro has been switched on / off
|
||||
CLMgrGroupCallNotificationMessage = 8, //notification about group call
|
||||
CLMgrNumberOfLinesChangedMessage = 10, //the number of lines has changed
|
||||
CLMgrClientShutDownRequest = 11, //Client Line Manager requests client to shutdown and release all interfaces
|
||||
CLMgrLineStateChangedMessageEx = 28, //state of certain line has changed, lParam: LOWORD: line index of line that changed its state (starting with 0) HIWORD: new state of this line
|
||||
CLMgrSIPRegistrationStateChanged = 30, //registration state of SIP account has changed
|
||||
//lParam: LOBYTE: Account index
|
||||
// HIBYTE: new state
|
||||
CLMgrWaveFilePlayed = 31, //wave file playback finished
|
||||
//lParam: line index;
|
||||
//if -1, the message is related to a LineMgr function PlaySoundFile or PlayToRtp
|
||||
//if >=0 the message is related to a line function PlaySoundFile of line with this index
|
||||
PubCLMgrFirstDataReceived = 32 //first RTP data received on line, might be silence
|
||||
//lParam: line index;
|
||||
}
|
||||
|
||||
public enum LineState
|
||||
{
|
||||
Inactive = 0, //line is inactive
|
||||
HookOffInternal = 1, //off hook, internal dialtone
|
||||
HookOffExternal = 2, //off hook, external dialtone
|
||||
Ringing = 3, //incoming call, ringing
|
||||
Dialing = 4, //outgoing call, we are dialing, no sound
|
||||
Alerting = 5, //outgoing call, alerting = ringing on destination
|
||||
Knocking = 6, //outgoing call, knocking = second call ringing on destination
|
||||
Busy = 7, //outgoing call, destination is busy
|
||||
Active = 8, //incoming / outgoing call, logical and physical connection is established
|
||||
OnHold = 9, //incoming / outgoing call, logical connection is established, destination gets music on hold
|
||||
ConferenceActive = 10, //incoming / outgoing conference, logical and physical connection is established
|
||||
ConferenceOnHold = 11, //incoming / outgoing conference, logical connection is established, not physcically connected
|
||||
Terminated = 12, //incoming / outgoing connection / call has been disconnected
|
||||
Transferring = 13, //special LSOnHold, call is awaiting to be transferred, peer gets special music on hold
|
||||
Disabled = 14 //special LSInactive: wrap up time
|
||||
}
|
||||
|
||||
public enum DisconnectReason
|
||||
{
|
||||
Normal = 0,
|
||||
Busy = 1,
|
||||
Rejected = 2,
|
||||
Cancelled = 3,
|
||||
Transferred = 4,
|
||||
JoinedConference = 5,
|
||||
NoAnswer = 6,
|
||||
TooLate = 7,
|
||||
DirectCallImpossible = 8,
|
||||
WrongNumber = 9,
|
||||
Unreachable = 10,
|
||||
CallDiverted = 11,
|
||||
CallRoutingFailed = 12,
|
||||
PermissionDenied = 13,
|
||||
NetworkCongestion = 14,
|
||||
NoChannelAvailable = 15,
|
||||
NumberChanged = 16,
|
||||
IncompatibleDestination = 17
|
||||
}
|
||||
}
|
200
TeamsNetphoneLinkWPF/Communication/NetPhone.cs
Normal file
200
TeamsNetphoneLinkWPF/Communication/NetPhone.cs
Normal file
@ -0,0 +1,200 @@
|
||||
using CLMGRLib;
|
||||
using System.Diagnostics;
|
||||
using TeamsLocalLibary.EventArgs;
|
||||
|
||||
namespace TeamsNetphoneLink.Netphone
|
||||
{
|
||||
public delegate void LineStateChangedEventHandler(LineState newLineState);
|
||||
public delegate void LoggedInStateEventHandler(bool loggedInState);
|
||||
|
||||
public class NetPhoneEvents : IDisposable
|
||||
{
|
||||
private ClientLine SelectedLine;
|
||||
private ClientLineMgrClass pCLMgr;
|
||||
private ClientSdkEventSink MyEventSink;
|
||||
private LineState LastLineState;
|
||||
private bool lastLoggedInState;
|
||||
|
||||
private CancellationTokenSource aliveCheckTokenSource;
|
||||
private CancellationToken aliveCheckToken;
|
||||
|
||||
private Dictionary<LineState, List<LineStateChangedEventHandler>> lineStateEvents = new Dictionary<LineState, List<LineStateChangedEventHandler>>();
|
||||
private List<LoggedInStateEventHandler> loggedInStateEvents = new List<LoggedInStateEventHandler>();
|
||||
|
||||
|
||||
private Timer loggedInStateTimer;
|
||||
private Timer aliveTimer;
|
||||
|
||||
public event EventHandler<TokenReceivedEventArgs>? TokenReceived;
|
||||
|
||||
public NetPhoneEvents()
|
||||
{
|
||||
loggedInStateTimer = new Timer(CheckLoggedInState, null, Timeout.Infinite, 1000);
|
||||
aliveTimer = new Timer(AliveCheck, null, Timeout.Infinite, 1000);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
loggedInStateTimer?.Change(Timeout.Infinite, 0);
|
||||
loggedInStateTimer?.Dispose();
|
||||
RemoveAllEventHandlers();
|
||||
}
|
||||
|
||||
public async Task<bool> Initialize(bool waitForNetphone = false, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (Process.GetProcessesByName("CLMgr").Length == 0 && !waitForNetphone)
|
||||
return false;
|
||||
|
||||
while (Process.GetProcessesByName("CLMgr").Length == 0)
|
||||
await Task.Delay(1000, cancellationToken);
|
||||
|
||||
|
||||
Console.WriteLine("new interface");
|
||||
pCLMgr = new ClientLineMgrClass();
|
||||
MyEventSink = new ClientSdkEventSink();
|
||||
MyEventSink.Connect(pCLMgr, new LineManagerMessageHandler(OnLineManagerMessage));
|
||||
|
||||
aliveTimer.Change(0, 1000);
|
||||
loggedInStateTimer.Change(0, 1500);
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void AliveCheck(object state)
|
||||
{
|
||||
if (Process.GetProcessesByName("CLMgr").Length == 0)
|
||||
{
|
||||
_ = Initialize(true).GetAwaiter().GetResult();
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckLoggedInState(object state)
|
||||
{
|
||||
bool currentLoggedInState = false;
|
||||
|
||||
try
|
||||
{
|
||||
currentLoggedInState = pCLMgr.DispIsLoggedIn != 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
currentLoggedInState = false;
|
||||
}
|
||||
|
||||
if (currentLoggedInState != lastLoggedInState)
|
||||
{
|
||||
lastLoggedInState = currentLoggedInState;
|
||||
OnLoggedInStateChanged(currentLoggedInState);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void OnLoggedInStateChanged(bool isLoggedIn)
|
||||
{
|
||||
foreach (var handler in loggedInStateEvents)
|
||||
{
|
||||
handler?.Invoke(isLoggedIn);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddLineStateEventHandler(LineState lineState, LineStateChangedEventHandler handler)
|
||||
{
|
||||
lock (lineStateEvents)
|
||||
{
|
||||
if (!lineStateEvents.ContainsKey(lineState))
|
||||
{
|
||||
lineStateEvents[lineState] = new List<LineStateChangedEventHandler>();
|
||||
}
|
||||
lineStateEvents[lineState].Add(handler);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddLoggedInStateEventHandler(LoggedInStateEventHandler handler)
|
||||
{
|
||||
lock (loggedInStateEvents)
|
||||
{
|
||||
loggedInStateEvents.Add(handler);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveLineStateEventHandler(LineState lineState, LineStateChangedEventHandler handler)
|
||||
{
|
||||
lock (lineStateEvents)
|
||||
{
|
||||
if (lineStateEvents.ContainsKey(lineState))
|
||||
{
|
||||
lineStateEvents[lineState].Remove(handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveLoggedInStateEventHandler(LoggedInStateEventHandler handler)
|
||||
{
|
||||
lock (loggedInStateEvents)
|
||||
{
|
||||
loggedInStateEvents.Remove(handler);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveAllEventHandlers()
|
||||
{
|
||||
lock (lineStateEvents)
|
||||
{
|
||||
lineStateEvents.Clear();
|
||||
}
|
||||
lock (loggedInStateEvents)
|
||||
{
|
||||
loggedInStateEvents.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnLineManagerMessage(ClientSdkEventArgs e)
|
||||
{
|
||||
SelectedLine = (ClientLine)pCLMgr.DispSelectedLine;
|
||||
|
||||
if (e.Msg == CLMgrMessage.CLMgrClientShutDownRequest)
|
||||
{
|
||||
//aliveCheckTokenSource.Cancel();
|
||||
//MyEventSink.Disconnect();
|
||||
//OnConnectionStateChanged(false,true);
|
||||
}
|
||||
|
||||
if (e.Msg == CLMgrMessage.CLMgrLineStateChangedMessageEx)
|
||||
{
|
||||
int line = e.Param & 0xff;
|
||||
int high = e.Param >> 8;
|
||||
|
||||
LineState NewLineState = (LineState)high;
|
||||
|
||||
if (LastLineState != NewLineState)
|
||||
{
|
||||
LastLineState = NewLineState;
|
||||
|
||||
lock (lineStateEvents)
|
||||
{
|
||||
if (lineStateEvents.ContainsKey(NewLineState))
|
||||
{
|
||||
foreach (var handler in lineStateEvents[NewLineState])
|
||||
{
|
||||
handler?.Invoke(NewLineState);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetRichPresenceStatus(int away, int dnd, DateTime expires)
|
||||
{
|
||||
var clientConfig = (ClientConfig)this.pCLMgr.ClientConfig;
|
||||
clientConfig.SetRichPresenceStatus(away, dnd, expires);
|
||||
}
|
||||
|
||||
public void SetAppointmentText(string appointmentText, DateTime expires)
|
||||
{
|
||||
var clientConfig = (ClientConfig)this.pCLMgr.ClientConfig;
|
||||
clientConfig.SetAppointmentText(appointmentText, expires);
|
||||
}
|
||||
}
|
||||
}
|
290
TeamsNetphoneLinkWPF/Communication/TeamsGraph.cs
Normal file
290
TeamsNetphoneLinkWPF/Communication/TeamsGraph.cs
Normal file
@ -0,0 +1,290 @@
|
||||
using Azure.Core;
|
||||
using Azure.Identity;
|
||||
using Azure.Identity.Broker;
|
||||
using Microsoft.Graph;
|
||||
using Microsoft.Graph.Drives.Item.Items.Item.Workbook.Functions.Cosh;
|
||||
using Microsoft.Graph.Me.Presence.SetUserPreferredPresence;
|
||||
using Microsoft.Identity.Client;
|
||||
using Microsoft.Identity.Client.Broker;
|
||||
using Microsoft.Identity.Client.Extensions.Msal;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Http;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
using TeamsLocalLibary;
|
||||
using static System.Formats.Asn1.AsnWriter;
|
||||
using Microsoft.Identity.Client.NativeInterop;
|
||||
using TeamsNetphoneLink.WPF;
|
||||
using TeamsNetphoneLink.Communication;
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace TeamsNetphoneLink.Teams
|
||||
{
|
||||
public delegate void PresenceStausEventHandler(Microsoft.Graph.Models.Presence presence);
|
||||
|
||||
public class TeamsGraph : TeamsGraphEventHandlers
|
||||
{
|
||||
private GraphServiceClient graphClient;
|
||||
public bool Authenticated { get; private set; }
|
||||
|
||||
private Timer presenceStatusTimer;
|
||||
private Microsoft.Graph.Models.Presence lastPresence;
|
||||
public void CheckPresenceStatusTimer()
|
||||
{
|
||||
presenceStatusTimer = new Timer(CheckPresenceStatus, null, Timeout.Infinite, 2000);
|
||||
presenceStatusTimer.Change(0, 1000);
|
||||
}
|
||||
|
||||
public void CheckPresenceStatus(object state)
|
||||
{
|
||||
if (graphClient is not null && Authenticated)
|
||||
{
|
||||
try
|
||||
{
|
||||
var presence = graphClient.Me.Presence.GetAsync().GetAwaiter().GetResult();
|
||||
|
||||
// If lastPresence is null, initialize it with the current presence
|
||||
if (lastPresence == null)
|
||||
{
|
||||
lastPresence = presence;
|
||||
}
|
||||
|
||||
// Check if Availability has changed
|
||||
if (lastPresence.Availability != presence.Availability)
|
||||
{
|
||||
OnAvailabilityChanged(presence);
|
||||
}
|
||||
|
||||
// Check if Activity has changed
|
||||
if (lastPresence.Activity != presence.Activity)
|
||||
{
|
||||
OnActivityChanged(presence);
|
||||
}
|
||||
|
||||
// Update lastPresence to the current presence
|
||||
lastPresence = presence;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error checking presence state: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> IsCachedAccounts()
|
||||
{
|
||||
IPublicClientApplication app = PublicClientApplicationBuilder.Create(Settings.Default.AppID)
|
||||
.WithDefaultRedirectUri()
|
||||
.WithAuthority(String.Format("https://login.microsoftonline.com/{0}", Settings.Default.TenantID))
|
||||
.Build();
|
||||
|
||||
// Register MSAL cache
|
||||
|
||||
var storage = new StorageCreationPropertiesBuilder("teamsnetphonelink.msal.cache", MsalCacheHelper.UserRootDirectory).Build();
|
||||
|
||||
var cacheHelper = await MsalCacheHelper.CreateAsync(storage);
|
||||
|
||||
cacheHelper.RegisterCache(app.UserTokenCache);
|
||||
|
||||
IEnumerable<IAccount> accounts = await app.GetAccountsAsync();
|
||||
|
||||
return accounts.Any();
|
||||
}
|
||||
|
||||
// Authentifizierungsmethode mit Azure Identity
|
||||
public async Task<bool> AuthenticateAsync(bool clearCache = false, bool silent = true)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Check if caching is enabled in settings
|
||||
bool useCache = Settings.Default.SaveEntraCredentials;
|
||||
|
||||
IPublicClientApplication app = PublicClientApplicationBuilder.Create(Settings.Default.AppID)
|
||||
.WithDefaultRedirectUri()
|
||||
.WithAuthority(String.Format("https://login.microsoftonline.com/{0}", Settings.Default.TenantID))
|
||||
.Build();
|
||||
|
||||
// Register MSAL cache only if caching is enabled
|
||||
if (useCache)
|
||||
{
|
||||
var storage = new StorageCreationPropertiesBuilder("teamsnetphonelink.msal.cache", MsalCacheHelper.UserRootDirectory).Build();
|
||||
|
||||
var cacheHelper = await MsalCacheHelper.CreateAsync(storage);
|
||||
|
||||
cacheHelper.RegisterCache(app.UserTokenCache);
|
||||
}
|
||||
|
||||
IEnumerable<IAccount> accounts = await app.GetAccountsAsync();
|
||||
|
||||
//Alle Accounts aus dem Cache entfernen wenn clearCache gesetzt ist
|
||||
if (clearCache)
|
||||
{
|
||||
foreach(var account in accounts)
|
||||
await app.RemoveAsync(account);
|
||||
}
|
||||
|
||||
// Try to use the previously signed-in account from the cache
|
||||
var existingAccount = accounts.FirstOrDefault();
|
||||
|
||||
AuthenticationResult authentication;
|
||||
|
||||
if (existingAccount is not null && useCache && !clearCache)
|
||||
{
|
||||
Console.WriteLine("Attempting to acquire token silently using cached account.");
|
||||
try
|
||||
{
|
||||
authentication = await app.AcquireTokenSilent(new[] { "Presence.ReadWrite", "offline_access" }, existingAccount)
|
||||
.ExecuteAsync();
|
||||
|
||||
await InitializeGraphClient(authentication.AccessToken);
|
||||
|
||||
return Authenticated;
|
||||
}
|
||||
catch (MsalUiRequiredException)
|
||||
{
|
||||
Console.WriteLine("Silent token acquisition failed. Falling back to interactive authentication.");
|
||||
}
|
||||
}
|
||||
|
||||
// If no cached account or silent authentication fails, prompt the user for authentication
|
||||
Console.WriteLine("Prompting user for authentication.");
|
||||
authentication = await app.AcquireTokenInteractive(new[] { "Presence.ReadWrite", "offline_access" })
|
||||
.ExecuteAsync();
|
||||
|
||||
await InitializeGraphClient(authentication.AccessToken);
|
||||
|
||||
return Authenticated;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ExceptionWindowHelper.Show(this.GetType().Name, MethodBase.GetCurrentMethod().Name, "Fehler bei der Authentifizierung", ex.Message, ex.StackTrace);
|
||||
Console.WriteLine($"Error during authentication: {ex.Message}");
|
||||
return Authenticated;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper method to initialize GraphServiceClient
|
||||
private async Task InitializeGraphClient(string accessToken)
|
||||
{
|
||||
graphClient = new GraphServiceClient(new HttpClient(new AuthHandler(accessToken, new HttpClientHandler())));
|
||||
Authenticated = await TestAuthentication();
|
||||
}
|
||||
|
||||
// Testung der Anmeldung und Zugriffsrechte
|
||||
public async Task<bool> TestAuthentication()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Test-Anfrage, um Authentifizierung zu validieren
|
||||
await graphClient.Me.Presence.GetAsync();
|
||||
return true; // Anmeldung und Zugriffsrechte korrekt
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ExceptionWindowHelper.Show(this.GetType().Name, MethodBase.GetCurrentMethod().Name, "Authentifizierung fehlerhaft", ex.Message, ex.StackTrace);
|
||||
return false; // Anmeldung und Zugriffsrechte felerhaft
|
||||
}
|
||||
}
|
||||
|
||||
// Methode zum Setzen des Präsenzstatus
|
||||
public async Task<bool> SetPresenceAsync(PresenceState presenceState)
|
||||
{
|
||||
if (graphClient is null || !Authenticated)
|
||||
{
|
||||
Console.WriteLine("Authentifizierung nicht abgeschlossen. Präsenz kann nicht gesetzt werden.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Zuordnen des Präsenzstatus zu Verfügbarkeit und Aktivität
|
||||
var presenceMap = new Dictionary<PresenceState, (string Availability, string Activity)>
|
||||
{
|
||||
{ PresenceState.Available, ("Available", "Available") },
|
||||
{ PresenceState.Busy, ("Busy", "Busy") },
|
||||
{ PresenceState.DoNotDisturb, ("DoNotDisturb", "DoNotDisturb") },
|
||||
{ PresenceState.BeRightBack, ("BeRightBack", "BeRightBack") },
|
||||
{ PresenceState.Away, ("Away", "Away") },
|
||||
{ PresenceState.Offline, ("Offline", "OffWork") }
|
||||
};
|
||||
|
||||
if (!presenceMap.ContainsKey(presenceState))
|
||||
{
|
||||
Console.WriteLine("Ungültiger Präsenzstatus.");
|
||||
ExceptionWindowHelper.Show(this.GetType().Name, MethodBase.GetCurrentMethod().Name, "Ungültiger Präsenzstatus", presenceState.ToString());
|
||||
return false; // Ungültiger Status
|
||||
}
|
||||
|
||||
var (availability, activity) = presenceMap[presenceState];
|
||||
|
||||
var requestBody = new SetUserPreferredPresencePostRequestBody
|
||||
{
|
||||
Availability = availability,
|
||||
Activity = activity,
|
||||
ExpirationDuration = TimeSpan.FromHours(2) // Ablaufzeit der Präsenz
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
await graphClient.Me.Presence.SetUserPreferredPresence.PostAsync(requestBody);
|
||||
return true; // Präsenz erfolgreich gesetzt
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if(ex.GetType() != typeof(Microsoft.Graph.Models.ODataErrors.ODataError))
|
||||
{
|
||||
ExceptionWindowHelper.Show(this.GetType().Name, MethodBase.GetCurrentMethod().Name, "Fehler beim Setzen der Präsenz", ex.Message, ex.StackTrace);
|
||||
}
|
||||
return false; // Fehler beim Setzen der Präsenz
|
||||
}
|
||||
}
|
||||
|
||||
// Enum für die verschiedenen Präsenzstatus
|
||||
public enum PresenceState
|
||||
{
|
||||
Available,
|
||||
Busy,
|
||||
DoNotDisturb,
|
||||
BeRightBack,
|
||||
Away,
|
||||
Offline
|
||||
}
|
||||
|
||||
private static Regex isGuid =
|
||||
new Regex(@"^(\{){0,1}[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}(\}){0,1}$", RegexOptions.Compiled);
|
||||
|
||||
public static bool IsGuid(string candidate)
|
||||
{
|
||||
bool isValid = false;
|
||||
|
||||
if (candidate != null)
|
||||
{
|
||||
|
||||
if (isGuid.IsMatch(candidate))
|
||||
{
|
||||
isValid = true;
|
||||
}
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
}
|
||||
|
||||
// Custom HttpMessageHandler to inject the access token
|
||||
public class AuthHandler : DelegatingHandler
|
||||
{
|
||||
private readonly string _accessToken;
|
||||
|
||||
public AuthHandler(string accessToken, HttpMessageHandler innerHandler)
|
||||
: base(innerHandler)
|
||||
{
|
||||
_accessToken = accessToken;
|
||||
}
|
||||
|
||||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
// Add the access token to the request headers
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _accessToken);
|
||||
return await base.SendAsync(request, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace TeamsNetphoneLink.Communication
|
||||
{
|
||||
public class TeamsGraphEventHandlers
|
||||
{
|
||||
private readonly object _lock = new object();
|
||||
private readonly List<EventHandler<PresenceChangedEventArgs>> _activityChangedHandlers = new List<EventHandler<PresenceChangedEventArgs>>();
|
||||
private readonly List<EventHandler<PresenceChangedEventArgs>> _availabilityChangedHandlers = new List<EventHandler<PresenceChangedEventArgs>>();
|
||||
|
||||
public event EventHandler<PresenceChangedEventArgs> ActivityChanged
|
||||
{
|
||||
add
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_activityChangedHandlers.Add(value);
|
||||
}
|
||||
}
|
||||
remove
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_activityChangedHandlers.Remove(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event EventHandler<PresenceChangedEventArgs> AvailabilityChanged
|
||||
{
|
||||
add
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_availabilityChangedHandlers.Add(value);
|
||||
}
|
||||
}
|
||||
remove
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_availabilityChangedHandlers.Remove(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void OnActivityChanged(Microsoft.Graph.Models.Presence presence)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
var args = new PresenceChangedEventArgs(presence);
|
||||
foreach (var handler in _activityChangedHandlers)
|
||||
{
|
||||
handler?.Invoke(this, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void OnAvailabilityChanged(Microsoft.Graph.Models.Presence presence)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
var args = new PresenceChangedEventArgs(presence);
|
||||
foreach (var handler in _availabilityChangedHandlers)
|
||||
{
|
||||
handler?.Invoke(this, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveAllEventHandlers()
|
||||
{
|
||||
foreach (var handler in _activityChangedHandlers)
|
||||
{
|
||||
_availabilityChangedHandlers.Remove(handler);
|
||||
}
|
||||
foreach (var handler in _availabilityChangedHandlers)
|
||||
{
|
||||
_availabilityChangedHandlers.Remove(handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class PresenceChangedEventArgs : EventArgs
|
||||
{
|
||||
public Microsoft.Graph.Models.Presence Presence { get; }
|
||||
|
||||
public PresenceChangedEventArgs(Microsoft.Graph.Models.Presence presence)
|
||||
{
|
||||
Presence = presence;
|
||||
}
|
||||
}
|
||||
}
|
118
TeamsNetphoneLinkWPF/Communication/TeamsLocalAPI.cs
Normal file
118
TeamsNetphoneLinkWPF/Communication/TeamsLocalAPI.cs
Normal file
@ -0,0 +1,118 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Net.WebSockets;
|
||||
using System.Reflection;
|
||||
using TeamsLocalLibary;
|
||||
using TeamsLocalLibary.EventArgs;
|
||||
|
||||
namespace TeamsNetphoneLink.Teams
|
||||
{
|
||||
public class TeamsLocalAPI
|
||||
{
|
||||
public event EventHandler<ConnectionStateChangedEventArgs>? ConnectionState;
|
||||
public event EventHandler<TokenReceivedEventArgs>? TokenReceived;
|
||||
|
||||
private Client teamsClient;
|
||||
private string token = Settings.Default.Token != string.Empty ? Settings.Default.Token : null;
|
||||
private Dictionary<string, List<PropertyChangedEventHandler>> propertyChangedHandlers = new Dictionary<string, List<PropertyChangedEventHandler>>();
|
||||
private CancellationTokenSource cts = new();
|
||||
|
||||
// Destructor
|
||||
~TeamsLocalAPI()
|
||||
{
|
||||
RemoveAllEventHandlers();
|
||||
}
|
||||
|
||||
public async Task<bool> Initialize()
|
||||
{
|
||||
// Initialize the TeamsClient with token and no auto-connect
|
||||
teamsClient = new Client(autoConnect: false, token: token) ?? throw new Exception("Could not create client");
|
||||
|
||||
// Event-handler for token reception
|
||||
teamsClient.TokenReceived += (_, args) =>
|
||||
{
|
||||
Settings.Default.Token = args.Token;
|
||||
Settings.Default.Save();
|
||||
|
||||
TokenReceived?.Invoke(_, args);
|
||||
};
|
||||
|
||||
WebSocketState lastConnectionState = WebSocketState.None;
|
||||
teamsClient.ConnectionState += (sender, args) =>
|
||||
{
|
||||
if (args.WebSocketState != lastConnectionState)
|
||||
{
|
||||
lastConnectionState = args.WebSocketState;
|
||||
// Raise the ConnectionState event in TeamsLocalAPI
|
||||
ConnectionState?.Invoke(sender, args);
|
||||
}
|
||||
};
|
||||
|
||||
teamsClient.PropertyChanged += HandlePropertyChanged;
|
||||
|
||||
teamsClient.ErrorReceived += (_, args) => Console.WriteLine("Event: ErrorReceived: {0}", args.ErrorMessage);
|
||||
|
||||
return await teamsClient.Connect(true, cts.Token);
|
||||
}
|
||||
|
||||
public void AddTokenRecievedHandler(EventHandler<TokenReceivedEventArgs> handler)
|
||||
{
|
||||
TokenReceived += handler;
|
||||
}
|
||||
|
||||
public void RemoveTokenRecievedHandler(EventHandler<TokenReceivedEventArgs> handler)
|
||||
{
|
||||
TokenReceived -= handler;
|
||||
}
|
||||
|
||||
public void AddConnectionStateHandler(EventHandler<ConnectionStateChangedEventArgs> handler)
|
||||
{
|
||||
ConnectionState += handler;
|
||||
}
|
||||
|
||||
public void RemoveConnectionStateHandler(EventHandler<ConnectionStateChangedEventArgs> handler)
|
||||
{
|
||||
ConnectionState -= handler;
|
||||
}
|
||||
|
||||
public async void SendDummyCommand()
|
||||
{
|
||||
var dummy = teamsClient.IsMuted = true;
|
||||
}
|
||||
|
||||
public void HandlePropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName is not null && propertyChangedHandlers.ContainsKey(e.PropertyName))
|
||||
{
|
||||
foreach (var handler in propertyChangedHandlers[e.PropertyName])
|
||||
{
|
||||
handler?.Invoke(sender, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void AddEventHandler(string propertyName, PropertyChangedEventHandler handler)
|
||||
{
|
||||
if (!propertyChangedHandlers.ContainsKey(propertyName))
|
||||
{
|
||||
propertyChangedHandlers[propertyName] = new List<PropertyChangedEventHandler>();
|
||||
}
|
||||
|
||||
propertyChangedHandlers[propertyName].Add(handler);
|
||||
}
|
||||
|
||||
public void RemoveEventHandler(string propertyName, PropertyChangedEventHandler handler)
|
||||
{
|
||||
if (propertyChangedHandlers.ContainsKey(propertyName))
|
||||
{
|
||||
propertyChangedHandlers[propertyName].Remove(handler);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveAllEventHandlers()
|
||||
{
|
||||
propertyChangedHandlers.Clear();
|
||||
}
|
||||
}
|
||||
}
|
110
TeamsNetphoneLinkWPF/Settings.Designer.cs
generated
Normal file
110
TeamsNetphoneLinkWPF/Settings.Designer.cs
generated
Normal file
@ -0,0 +1,110 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// Dieser Code wurde von einem Tool generiert.
|
||||
// Laufzeitversion:4.0.30319.42000
|
||||
//
|
||||
// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
|
||||
// der Code erneut generiert wird.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace TeamsNetphoneLink {
|
||||
|
||||
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.10.0.0")]
|
||||
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
|
||||
|
||||
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||
|
||||
public static Settings Default {
|
||||
get {
|
||||
return defaultInstance;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("")]
|
||||
public string Token {
|
||||
get {
|
||||
return ((string)(this["Token"]));
|
||||
}
|
||||
set {
|
||||
this["Token"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("False")]
|
||||
public bool TeamsPermission {
|
||||
get {
|
||||
return ((bool)(this["TeamsPermission"]));
|
||||
}
|
||||
set {
|
||||
this["TeamsPermission"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("")]
|
||||
public string TenantID {
|
||||
get {
|
||||
return ((string)(this["TenantID"]));
|
||||
}
|
||||
set {
|
||||
this["TenantID"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("")]
|
||||
public string AppID {
|
||||
get {
|
||||
return ((string)(this["AppID"]));
|
||||
}
|
||||
set {
|
||||
this["AppID"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("False")]
|
||||
public bool SaveEntraCredentials {
|
||||
get {
|
||||
return ((bool)(this["SaveEntraCredentials"]));
|
||||
}
|
||||
set {
|
||||
this["SaveEntraCredentials"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("False")]
|
||||
public bool UseGraphForMeetingState {
|
||||
get {
|
||||
return ((bool)(this["UseGraphForMeetingState"]));
|
||||
}
|
||||
set {
|
||||
this["UseGraphForMeetingState"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("")]
|
||||
public string CurrentVersion {
|
||||
get {
|
||||
return ((string)(this["CurrentVersion"]));
|
||||
}
|
||||
set {
|
||||
this["CurrentVersion"] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
27
TeamsNetphoneLinkWPF/Settings.settings
Normal file
27
TeamsNetphoneLinkWPF/Settings.settings
Normal file
@ -0,0 +1,27 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="TeamsNetphoneLink" GeneratedClassName="Settings">
|
||||
<Profiles />
|
||||
<Settings>
|
||||
<Setting Name="Token" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)" />
|
||||
</Setting>
|
||||
<Setting Name="TeamsPermission" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">False</Value>
|
||||
</Setting>
|
||||
<Setting Name="TenantID" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)" />
|
||||
</Setting>
|
||||
<Setting Name="AppID" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)" />
|
||||
</Setting>
|
||||
<Setting Name="SaveEntraCredentials" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">False</Value>
|
||||
</Setting>
|
||||
<Setting Name="UseGraphForMeetingState" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">False</Value>
|
||||
</Setting>
|
||||
<Setting Name="CurrentVersion" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)" />
|
||||
</Setting>
|
||||
</Settings>
|
||||
</SettingsFile>
|
273
TeamsNetphoneLinkWPF/Syncronisation.cs
Normal file
273
TeamsNetphoneLinkWPF/Syncronisation.cs
Normal file
@ -0,0 +1,273 @@
|
||||
using System.ComponentModel;
|
||||
using System.Windows.Media;
|
||||
using TeamsLocalLibary.EventArgs;
|
||||
using TeamsNetphoneLink.Teams;
|
||||
using TeamsNetphoneLink.Netphone;
|
||||
using TeamsNetphoneLink.WPF.MVVM;
|
||||
using TeamsNetphoneLink.Communication;
|
||||
using System.Reflection;
|
||||
using TeamsNetphoneLink.WPF;
|
||||
|
||||
namespace TeamsNetphoneLink
|
||||
{
|
||||
public class Syncronisation
|
||||
{
|
||||
public NetPhoneEvents NetphoneEvents { get; set; }
|
||||
public TeamsGraph TeamsGraph { get; set; }
|
||||
public TeamsLocalAPI TeamsEvents { get; set; }
|
||||
|
||||
//MVVM View Models
|
||||
public DashboardViewModel _dashboardViewModel { get; }
|
||||
public LogViewModel LogEntries { get; }
|
||||
public FinishPanelViewModel FinishPanelViewModel { get; }
|
||||
|
||||
public readonly SemaphoreSlim TeamsGraphAuthenticationSemaphore = new(1);
|
||||
|
||||
private bool _isCallActive = false;
|
||||
private bool _isMeetingActive = false;
|
||||
private bool NetphoneConnected = false;
|
||||
|
||||
|
||||
public Syncronisation(NetPhoneEvents netPhoneEvents, TeamsLocalAPI teamsEvents, TeamsGraph teamsGraph, DashboardViewModel dashboardViewModel, LogViewModel logEntries, FinishPanelViewModel finishPanelViewModel)
|
||||
{
|
||||
_dashboardViewModel = dashboardViewModel ?? throw new ArgumentNullException(nameof(dashboardViewModel));
|
||||
LogEntries = logEntries ?? throw new ArgumentNullException(nameof(logEntries));
|
||||
FinishPanelViewModel = finishPanelViewModel ?? throw new ArgumentNullException(nameof(finishPanelViewModel));
|
||||
NetphoneEvents = netPhoneEvents ?? throw new ArgumentNullException(nameof(netPhoneEvents));
|
||||
TeamsEvents = teamsEvents ?? throw new ArgumentNullException(nameof(teamsEvents));
|
||||
TeamsGraph = teamsGraph ?? throw new ArgumentNullException(nameof(teamsGraph));
|
||||
|
||||
// Event-Handler für Netphone-Events registrieren
|
||||
NetphoneEvents.AddLineStateEventHandler(LineState.Dialing, NetphoneEventCall);
|
||||
NetphoneEvents.AddLineStateEventHandler(LineState.Ringing, NetphoneEventCall);
|
||||
NetphoneEvents.AddLineStateEventHandler(LineState.Active, NetphoneEventCall);
|
||||
NetphoneEvents.AddLineStateEventHandler(LineState.Inactive, NetphoneEventTerminated);
|
||||
NetphoneEvents.AddLineStateEventHandler(LineState.Terminated, NetphoneEventTerminated);
|
||||
NetphoneEvents.AddLoggedInStateEventHandler(NetphoneOnLoggedInStateChanged);
|
||||
|
||||
if (!Settings.Default.UseGraphForMeetingState)
|
||||
{
|
||||
// Event-Handler für Teams-Events registrieren
|
||||
TeamsEvents.AddEventHandler("IsInMeeting", TeamsIsInMeetingHandler);
|
||||
TeamsEvents.AddConnectionStateHandler(TeamsOnConnectionStateChanged);
|
||||
TeamsEvents.AddTokenRecievedHandler(TeamsTokenRecievedHandler);
|
||||
}
|
||||
else
|
||||
{
|
||||
TeamsGraph.ActivityChanged += TeamsGraphOnActivityChanged;
|
||||
_dashboardViewModel.UpdateStatusPanel(StatusPanelNames.TeamsLocalAPIStatus, Colors.Orange, "Deaktiviert");
|
||||
}
|
||||
}
|
||||
|
||||
// Event-Handler für Änderungen des Verbindungsstatus in Teams
|
||||
private void TeamsOnConnectionStateChanged(object? sender, ConnectionStateChangedEventArgs args)
|
||||
{
|
||||
var status = args.WebSocketState switch
|
||||
{
|
||||
System.Net.WebSockets.WebSocketState.Open when Settings.Default.TeamsPermission => (Colors.Green, "Verbunden"),
|
||||
System.Net.WebSockets.WebSocketState.Open => (Colors.Orange, "Authorisieren (Klicken)"),
|
||||
System.Net.WebSockets.WebSocketState.Connecting => (Colors.Blue, "Verbinden..."),
|
||||
System.Net.WebSockets.WebSocketState.Closed => (Colors.Red, "Nicht verbunden"),
|
||||
_ => (Colors.Gray, "Unbekannt")
|
||||
};
|
||||
|
||||
_dashboardViewModel.UpdateStatusPanel(StatusPanelNames.TeamsLocalAPIStatus, status.Item1, status.Item2);
|
||||
|
||||
if(Settings.Default.TeamsPermission)
|
||||
LogEntries.AddLogEntry(args.WebSocketState == System.Net.WebSockets.WebSocketState.Open ? "Info" : "Warn", "TeamsLocalAPI", status.Item2);
|
||||
}
|
||||
|
||||
private void NetphoneOnLoggedInStateChanged(bool isLoggedIn)
|
||||
{
|
||||
NetphoneConnected = isLoggedIn;
|
||||
|
||||
var state = isLoggedIn ? ( Colors.Green, "Verbunden" ) : ( Colors.Red, "Nicht verbunden" );
|
||||
|
||||
_dashboardViewModel.UpdateStatusPanel(StatusPanelNames.NetphoneCLMGRStatus, state.Item1, state.Item2);
|
||||
LogEntries.AddLogEntry(isLoggedIn ? "Info" : "Warn", "Netphone", state.Item2);
|
||||
}
|
||||
|
||||
// Event-Handler für den Empfang eines Teams-Tokens
|
||||
private void TeamsTokenRecievedHandler(object sender, TokenReceivedEventArgs e)
|
||||
{
|
||||
if (!Settings.Default.TeamsPermission)
|
||||
{
|
||||
Settings.Default.TeamsPermission = true;
|
||||
Settings.Default.Save();
|
||||
|
||||
FinishPanelViewModel.FinishPanelEffect = null;
|
||||
FinishPanelViewModel.FinishPanelEnabled = true;
|
||||
FinishPanelViewModel.FinishPanelFinishTextText = "Verbindung erfolgreich";
|
||||
FinishPanelViewModel.SetFinishPanelFinishTextColor(Colors.Green);
|
||||
|
||||
_dashboardViewModel.UpdateStatusPanel(StatusPanelNames.TeamsLocalAPIStatus, Colors.Green, "Verbunden");
|
||||
LogEntries.AddLogEntry("TeamsLocalAPI", "Token erfolgreich abgerufen");
|
||||
}
|
||||
}
|
||||
|
||||
// Event-Handler für eingehende Anrufe
|
||||
private async void NetphoneEventCall(LineState newLineState)
|
||||
{
|
||||
if (_isCallActive) return;
|
||||
|
||||
_isCallActive = true;
|
||||
await TeamsGraph.SetPresenceAsync(TeamsGraph.PresenceState.Busy);
|
||||
_dashboardViewModel.UpdateStatusPanel(StatusPanelNames.NetphoneCallStatus, Colors.DarkGreen, "Ongoing");
|
||||
LogEntries.AddLogEntry("Syncronisation", "Netphone Anruf gestartet");
|
||||
}
|
||||
|
||||
// Event-Handler für beendete Anrufe
|
||||
private async void NetphoneEventTerminated(LineState newLineState)
|
||||
{
|
||||
if (!_isCallActive) return;
|
||||
|
||||
_isCallActive = false;
|
||||
await TeamsGraph.SetPresenceAsync(TeamsGraph.PresenceState.Available);
|
||||
_dashboardViewModel.UpdateStatusPanel(StatusPanelNames.NetphoneCallStatus, Colors.DarkOrange, "Not active");
|
||||
LogEntries.AddLogEntry("Syncronisation", "Netphone Anruf gestoppt");
|
||||
}
|
||||
|
||||
|
||||
private void TeamsGraphOnActivityChanged(object sender, PresenceChangedEventArgs args)
|
||||
{
|
||||
// Define activities that indicate the user is in a meeting
|
||||
var meetingActivities = new HashSet<string>
|
||||
{
|
||||
"InACall",
|
||||
"InAConferenceCall",
|
||||
"InAMeeting",
|
||||
"Presenting"
|
||||
};
|
||||
|
||||
if(args.Presence.Activity is null)
|
||||
return;
|
||||
|
||||
// Check if the current activity is in the meetingActivities set
|
||||
bool isInMeeting = meetingActivities.Contains(args.Presence.Activity);
|
||||
|
||||
|
||||
if (!isInMeeting && _isMeetingActive)
|
||||
{
|
||||
_isMeetingActive = false;
|
||||
SetNetphoneStatus(isInMeeting);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isInMeeting && !_isMeetingActive)
|
||||
{
|
||||
_isMeetingActive = true;
|
||||
SetNetphoneStatus(isInMeeting);
|
||||
}
|
||||
}
|
||||
|
||||
// Event-Handler für Änderungen des "IsInMeeting"-Status in Teams
|
||||
private void TeamsIsInMeetingHandler(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
var isInMeeting = (bool)(sender.GetType()?.GetProperty(e.PropertyName)?.GetValue(sender) ?? false);
|
||||
SetNetphoneStatus(isInMeeting);
|
||||
}
|
||||
|
||||
private void SetNetphoneStatus(bool meeting)
|
||||
{
|
||||
if (meeting)
|
||||
{
|
||||
if (NetphoneConnected)
|
||||
{
|
||||
NetphoneEvents.SetRichPresenceStatus(0, 1, DateTime.MaxValue);
|
||||
NetphoneEvents.SetAppointmentText("In einem Teams Meeting", DateTime.MaxValue);
|
||||
}
|
||||
_dashboardViewModel.UpdateStatusPanel(StatusPanelNames.TeamsMeetingStatus, Colors.DarkGreen, "Ongoing");
|
||||
LogEntries.AddLogEntry("Syncronisation", "Teams Meeting gestartet");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (NetphoneConnected)
|
||||
{
|
||||
NetphoneEvents.SetRichPresenceStatus(0, 0, DateTime.MaxValue);
|
||||
NetphoneEvents.SetAppointmentText("", DateTime.MaxValue);
|
||||
}
|
||||
_dashboardViewModel.UpdateStatusPanel(StatusPanelNames.TeamsMeetingStatus, Colors.DarkOrange, "Not active");
|
||||
LogEntries.AddLogEntry("Syncronisation", "Teams Meeting gestoppt");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Methode zur Authentifizierung bei der Graph API
|
||||
public async Task<bool> GraphAuthenticate(bool clearCache = false)
|
||||
{
|
||||
_dashboardViewModel.UpdateStatusPanel(StatusPanelNames.TeamsGraphAPIStatus, Colors.Blue, "Anmeldung läuft...");
|
||||
LogEntries.AddLogEntry("TeamsGraphAPI", String.Format("Anmeldung läuft"));
|
||||
|
||||
var authResult = await TeamsGraph.AuthenticateAsync(clearCache);
|
||||
|
||||
var status = authResult ? (Colors.Green, "Angemeldet") : (Colors.Red, "Fehler - Anmelden (Klicken)");
|
||||
_dashboardViewModel.UpdateStatusPanel(StatusPanelNames.TeamsGraphAPIStatus, status.Item1, status.Item2);
|
||||
LogEntries.AddLogEntry("TeamsGraphAPI", status.Item2);
|
||||
|
||||
return authResult;
|
||||
}
|
||||
|
||||
public async void InitializeNetphoneAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
await this.NetphoneEvents.Initialize(waitForNetphone: true).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Fehlerbehandlung für die Netphone-Initialisierung
|
||||
ExceptionWindowHelper.Show(this.GetType().Name, MethodBase.GetCurrentMethod().Name, "Fehler bei der Initialisierung von der NetphoneAPI", ex.Message, ex.StackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
public async void InitializeTeamsGraphAsync()
|
||||
{
|
||||
if (!Teams.TeamsGraph.IsGuid(Settings.Default.AppID) || !Teams.TeamsGraph.IsGuid(Settings.Default.TenantID))
|
||||
{
|
||||
this._dashboardViewModel.UpdateStatusPanel(StatusPanelNames.TeamsGraphAPIStatus, Colors.Red, "Konfiguration fehlt");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await this.TeamsGraphAuthenticationSemaphore.WaitAsync().ConfigureAwait(false);
|
||||
if (await this.TeamsGraph.IsCachedAccounts() && Settings.Default.SaveEntraCredentials)
|
||||
{
|
||||
await this.GraphAuthenticate().ConfigureAwait(false); ;
|
||||
}
|
||||
else
|
||||
{
|
||||
this._dashboardViewModel.UpdateStatusPanel(StatusPanelNames.TeamsGraphAPIStatus, Colors.Orange, "Anmelden");
|
||||
}
|
||||
this.TeamsGraphAuthenticationSemaphore.Release();
|
||||
|
||||
if (Settings.Default.UseGraphForMeetingState)
|
||||
{
|
||||
this.TeamsGraph.CheckPresenceStatusTimer();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Fehlerbehandlung für die TeamsGraph-Initialisierung
|
||||
ExceptionWindowHelper.Show(this.GetType().Name, MethodBase.GetCurrentMethod().Name, "Fehler bei der Initialisierung von TeamsGraph", ex.Message, ex.StackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
public async void InitializeTeamsLocalAPIAsync()
|
||||
{
|
||||
if (!Settings.Default.UseGraphForMeetingState)
|
||||
{
|
||||
try
|
||||
{
|
||||
await this.TeamsEvents.Initialize().ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Fehlerbehandlung für die TeamsLocalAPI-Initialisierung
|
||||
ExceptionWindowHelper.Show(this.GetType().Name, MethodBase.GetCurrentMethod().Name, "Fehler bei der Initialisierung von der TeamsLocalAPI", ex.Message, ex.StackTrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
62
TeamsNetphoneLinkWPF/TeamsNetphoneLinkWPF.csproj
Normal file
62
TeamsNetphoneLinkWPF/TeamsNetphoneLinkWPF.csproj
Normal file
@ -0,0 +1,62 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<UseWPF>true</UseWPF>
|
||||
<RootNamespace>TeamsNetphoneLink</RootNamespace>
|
||||
<ApplicationIcon>TeamsNetphoneSyncIcon.ico</ApplicationIcon>
|
||||
<BaseOutputPath>..\build\</BaseOutputPath>
|
||||
<SignAssembly>False</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile></AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<COMReference Include="CLMGRLib">
|
||||
<Guid>{F8E552F7-4C00-11D3-80BC-00105A653379}</Guid>
|
||||
<VersionMajor>2</VersionMajor>
|
||||
<VersionMinor>0</VersionMinor>
|
||||
<Lcid>0</Lcid>
|
||||
<WrapperTool>tlbimp</WrapperTool>
|
||||
<Isolated>False</Isolated>
|
||||
<EmbedInteropTypes>False</EmbedInteropTypes>
|
||||
</COMReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="TeamsNetphoneSyncIcon.ico" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Azure.Identity" Version="1.13.2" />
|
||||
<PackageReference Include="Azure.Identity.Broker" Version="1.2.0" />
|
||||
<PackageReference Include="Emoji.Wpf" Version="0.3.4" />
|
||||
<PackageReference Include="Microsoft.Graph" Version="5.74.0" />
|
||||
<PackageReference Include="Microsoft.Identity.Client.Broker" Version="4.67.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\TeamsLocalAPI\TeamsLocalLibary.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Settings.Designer.cs">
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="WPF\UpdateWindow.xaml.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
BIN
TeamsNetphoneLinkWPF/TeamsNetphoneSyncIcon.ico
Normal file
BIN
TeamsNetphoneLinkWPF/TeamsNetphoneSyncIcon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
79
TeamsNetphoneLinkWPF/UpdateCheck.cs
Normal file
79
TeamsNetphoneLinkWPF/UpdateCheck.cs
Normal file
@ -0,0 +1,79 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Threading;
|
||||
using TeamsNetphoneLink.WPF;
|
||||
|
||||
namespace TeamsNetphoneLink
|
||||
{
|
||||
public class UpdateCheck
|
||||
{
|
||||
public static bool UpdateAvailable;
|
||||
|
||||
public static async Task<string> GetLatestReleaseTagAsync()
|
||||
{
|
||||
using (var client = new HttpClient())
|
||||
{
|
||||
var url = "https://git.jan.sx/api/v1/repos/krjan02/TeamsNetphoneLink/releases/latest";
|
||||
|
||||
try
|
||||
{
|
||||
var response = await client.GetStringAsync(url);
|
||||
|
||||
var json = JsonDocument.Parse(response);
|
||||
|
||||
if (json.RootElement.TryGetProperty("tag_name", out var tagName))
|
||||
{
|
||||
return tagName.GetString();
|
||||
}
|
||||
else
|
||||
{
|
||||
ExceptionWindowHelper.Show("UpdateCheck", MethodBase.GetCurrentMethod().Name, "Update Check nicht erfolgreich", "tag_name is not existing");
|
||||
return "";
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ExceptionWindowHelper.Show("UpdateCheck", MethodBase.GetCurrentMethod().Name, "Update Check nicht erfolgreich", ex.Message, ex.StackTrace);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsVersionLower(string currentVersion, string releaseVersion)
|
||||
{
|
||||
if(releaseVersion.Length == 0) { return false; }
|
||||
|
||||
Version current = Version.Parse(currentVersion);
|
||||
Version release = Version.Parse(releaseVersion);
|
||||
|
||||
return current.CompareTo(release) < 0;
|
||||
}
|
||||
|
||||
public static string GetCurrentVersion()
|
||||
{
|
||||
var assembly = Assembly.GetEntryAssembly();
|
||||
var versionAttribute = assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
|
||||
var productVersion = versionAttribute?.InformationalVersion;
|
||||
|
||||
return productVersion.Split("+").FirstOrDefault(); ;
|
||||
}
|
||||
|
||||
public static void UpgradeSettingsIfRequired()
|
||||
{
|
||||
var version = GetCurrentVersion();
|
||||
if (Settings.Default.CurrentVersion != version)
|
||||
{
|
||||
Settings.Default.Upgrade();
|
||||
Settings.Default.CurrentVersion = version;
|
||||
Settings.Default.Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
263
TeamsNetphoneLinkWPF/WPF/DashboardWindow.xaml
Normal file
263
TeamsNetphoneLinkWPF/WPF/DashboardWindow.xaml
Normal file
@ -0,0 +1,263 @@
|
||||
<Window x:Class="TeamsNetphoneLink.WPF.DashboardWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:TeamsNetphoneLink.WPF.MVVM"
|
||||
Title="TeamsNetphoneLink Dashboard" Height="554" Width="800"
|
||||
Background="#FF1E1E1E" FontFamily="Segoe UI" ResizeMode="CanMinimize"
|
||||
WindowStartupLocation="CenterScreen">
|
||||
|
||||
<Window.DataContext>
|
||||
<local:DashboardViewModel />
|
||||
</Window.DataContext>
|
||||
|
||||
<Window.Resources>
|
||||
<!-- Button Style -->
|
||||
<Style TargetType="Button">
|
||||
<Setter Property="Background" Value="#FF0078D4"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="BorderBrush" Value="Transparent"/>
|
||||
<Setter Property="Padding" Value="10,5"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border Background="{TemplateBinding Background}"
|
||||
CornerRadius="4"
|
||||
Padding="{TemplateBinding Padding}">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="#0063B1"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
<!-- Status Border Style -->
|
||||
<Style TargetType="Border" x:Key="StatusBorder">
|
||||
<Setter Property="CornerRadius" Value="12"/>
|
||||
<Setter Property="Padding" Value="10,5"/>
|
||||
<Setter Property="Background" Value="#FF252526"/>
|
||||
<Setter Property="MinWidth" Value="100"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Left"/>
|
||||
</Style>
|
||||
|
||||
<!-- Status Text Style -->
|
||||
<Style TargetType="TextBlock" x:Key="StatusText">
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Center"/>
|
||||
</Style>
|
||||
|
||||
<!-- ListView Styling -->
|
||||
<Style TargetType="ListView">
|
||||
<Setter Property="Background" Value="#FF252526"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="BorderBrush" Value="#FF3E3E40"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
|
||||
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
|
||||
</Style>
|
||||
|
||||
<!-- GridViewColumnHeader Style -->
|
||||
<Style TargetType="GridViewColumnHeader">
|
||||
<Setter Property="Background" Value="#FF252526"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="BorderBrush" Value="#FF3E3E40"/>
|
||||
<Setter Property="Padding" Value="5"/>
|
||||
<Setter Property="Height" Value="32"/>
|
||||
<Setter Property="FontSize" Value="12"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Left"/>
|
||||
</Style>
|
||||
</Window.Resources>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<!-- Header -->
|
||||
<RowDefinition Height="Auto"/>
|
||||
<!-- Status Panels -->
|
||||
<RowDefinition Height="*"/>
|
||||
<!-- Log Section -->
|
||||
<RowDefinition Height="Auto"/>
|
||||
<!-- Footer -->
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Header -->
|
||||
<StackPanel Grid.Row="0" Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,15">
|
||||
<TextBlock FontSize="24" FontWeight="Bold" Text="{Binding AppTitle2}" Foreground="White"/>
|
||||
<Border Visibility="{Binding VersionVisibility}" Background="{Binding VersionBackground}" CornerRadius="12" Padding="12,4" Margin="15,0">
|
||||
<TextBlock Text="{Binding VersionText}" Foreground="White" FontSize="16"/>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Status Indicators -->
|
||||
<Grid Grid.Row="1" Margin="15,20">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Left Status Column -->
|
||||
<StackPanel Grid.Column="0">
|
||||
<!-- Teams Local API Status -->
|
||||
<StackPanel Orientation="Horizontal" Margin="0,8">
|
||||
<TextBlock Text="Teams Local API Status" Width="180" FontWeight="Bold" Foreground="White"/>
|
||||
<Border Style="{StaticResource StatusBorder}"
|
||||
Background="{Binding TeamsLocalAPIStatusBackground}"
|
||||
MouseDown="TeamsLocalAPIBorder_MouseDown">
|
||||
<TextBlock Style="{StaticResource StatusText}" Text="{Binding TeamsLocalAPIStatusText}"/>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Teams Graph API Status -->
|
||||
<StackPanel Orientation="Horizontal" Margin="0,8">
|
||||
<TextBlock Text="Teams Graph API Status" Width="180" FontWeight="Bold" Foreground="White"/>
|
||||
<Border Style="{StaticResource StatusBorder}"
|
||||
Background="{Binding TeamsGraphAPIStatusBackground}"
|
||||
MouseDown="TeamsGraphAPIBorder_MouseDown">
|
||||
<TextBlock Style="{StaticResource StatusText}" Text="{Binding TeamsGraphAPIStatusText}"/>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
<!-- NetPhone CLMGR Status -->
|
||||
<StackPanel Orientation="Horizontal" Margin="0,8">
|
||||
<TextBlock Text="NetPhone CLMGR Status" Width="180" FontWeight="Bold" Foreground="White"/>
|
||||
<Border Style="{StaticResource StatusBorder}"
|
||||
Background="{Binding NetphoneCLMGRStatusBackground}">
|
||||
<TextBlock Style="{StaticResource StatusText}" Text="{Binding NetphoneCLMGRStatusText}"/>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Right Status Column -->
|
||||
<StackPanel Grid.Column="1">
|
||||
<!-- Teams Meeting Status -->
|
||||
<StackPanel Orientation="Horizontal" Margin="0,8">
|
||||
<TextBlock Text="Teams Meeting" Width="180" FontWeight="Bold" Foreground="White"/>
|
||||
<Border Style="{StaticResource StatusBorder}"
|
||||
Background="{Binding TeamsMeetingStatusBackground}">
|
||||
<TextBlock Style="{StaticResource StatusText}" Text="{Binding TeamsMeetingStatusText}"/>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
<!-- NetPhone Call Status -->
|
||||
<StackPanel Orientation="Horizontal" Margin="0,8">
|
||||
<TextBlock Text="NetPhone Call" Width="180" FontWeight="Bold" Foreground="White"/>
|
||||
<Border Style="{StaticResource StatusBorder}"
|
||||
Background="{Binding NetphoneCallStatusBackground}">
|
||||
<TextBlock Style="{StaticResource StatusText}" Text="{Binding NetphoneCallStatusText}"/>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- Log Section -->
|
||||
<Border Grid.Row="2" Margin="15,0,15,10" Background="#FF252526" CornerRadius="8">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Text="Activity Log"
|
||||
FontSize="18"
|
||||
FontWeight="Bold"
|
||||
Foreground="White"
|
||||
Margin="15,10"/>
|
||||
|
||||
<ListView x:Name="Log" Grid.Row="1"
|
||||
ItemsSource="{Binding LogEntries}"
|
||||
Margin="10,0,10,10"
|
||||
VirtualizingPanel.IsVirtualizing="True"
|
||||
IsHitTestVisible="False">
|
||||
<ListView.View>
|
||||
<GridView>
|
||||
<GridViewColumn Width="80" Header="Time">
|
||||
<GridViewColumn.DisplayMemberBinding>
|
||||
<Binding Path="Time" StringFormat="{}{0:HH:mm:ss}"/>
|
||||
</GridViewColumn.DisplayMemberBinding>
|
||||
</GridViewColumn>
|
||||
|
||||
<GridViewColumn Width="60" Header="Type">
|
||||
<GridViewColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Type}" Foreground="White">
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="Foreground" Value="#FF0078D4"/>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding Type}" Value="Warn">
|
||||
<Setter Property="Foreground" Value="Orange"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding Type}" Value="Error">
|
||||
<Setter Property="Foreground" Value="Red"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
</DataTemplate>
|
||||
</GridViewColumn.CellTemplate>
|
||||
</GridViewColumn>
|
||||
|
||||
<GridViewColumn Width="120" Header="Subsystem">
|
||||
<GridViewColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Subsystem}">
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding Subsystem}" Value="Netphone">
|
||||
<Setter Property="Foreground" Value="#E20074"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding Subsystem}" Value="TeamsGraphAPI">
|
||||
<Setter Property="Foreground" Value="#4E5FBF"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding Subsystem}" Value="TeamsLocalAPI">
|
||||
<Setter Property="Foreground" Value="#4E5FBF"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
</DataTemplate>
|
||||
</GridViewColumn.CellTemplate>
|
||||
</GridViewColumn>
|
||||
|
||||
<GridViewColumn Width="Auto" Header="Message"
|
||||
DisplayMemberBinding="{Binding Message}"/>
|
||||
</GridView>
|
||||
</ListView.View>
|
||||
</ListView>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- Footer -->
|
||||
<StackPanel Grid.Row="3" Orientation="Horizontal" Margin="15,0,0,8">
|
||||
<Button Click="SettingsButton_Click"
|
||||
Padding="8"
|
||||
FontSize="18"
|
||||
VerticalAlignment="Center"
|
||||
ToolTip="Settings">
|
||||
<Button.Content>
|
||||
<Path Data="M12,15.5A3.5,3.5 0 0,1 8.5,12A3.5,3.5 0 0,1 12,8.5A3.5,3.5 0 0,1 15.5,12A3.5,3.5 0 0,1 12,15.5M19.43,12.97C19.47,12.65 19.5,12.33 19.5,12C19.5,11.67 19.47,11.34 19.43,11L21.54,9.37C21.73,9.22 21.78,8.95 21.66,8.73L19.66,5.27C19.54,5.05 19.27,4.96 19.05,5.05L16.56,6.05C16.04,5.66 15.5,5.32 14.87,5.07L14.5,2.42C14.46,2.18 14.25,2 14,2H10C9.75,2 9.54,2.18 9.5,2.42L9.13,5.07C8.5,5.32 7.96,5.66 7.44,6.05L4.95,5.05C4.73,4.96 4.46,5.05 4.34,5.27L2.34,8.73C2.21,8.95 2.27,9.22 2.46,9.37L4.57,11C4.53,11.34 4.5,11.67 4.5,12C4.5,12.33 4.53,12.65 4.57,12.97L2.46,14.63C2.27,14.78 2.21,15.05 2.34,15.27L4.34,18.73C4.46,18.95 4.73,19.03 4.95,18.95L7.44,17.94C7.96,18.34 8.5,18.68 9.13,18.93L9.5,21.58C9.54,21.82 9.75,22 10,22H14C14.25,22 14.46,21.82 14.5,21.58L14.87,18.93C15.5,18.68 16.04,18.34 16.56,17.94L19.05,18.95C19.27,19.03 19.54,18.95 19.66,18.73L21.66,15.27C21.78,15.05 21.73,14.78 21.54,14.63L19.43,12.97Z"
|
||||
Fill="White" Stretch="Uniform"/>
|
||||
</Button.Content>
|
||||
</Button>
|
||||
|
||||
<TextBlock Text="Entwickelt von Jan Krampitz"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="White"
|
||||
FontSize="10"
|
||||
Margin="15,0"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
95
TeamsNetphoneLinkWPF/WPF/DashboardWindow.xaml.cs
Normal file
95
TeamsNetphoneLinkWPF/WPF/DashboardWindow.xaml.cs
Normal file
@ -0,0 +1,95 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using TeamsNetphoneLink.WPF.MVVM;
|
||||
|
||||
namespace TeamsNetphoneLink.WPF
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for MainWindow.xaml
|
||||
/// </summary>
|
||||
///
|
||||
public partial class DashboardWindow : Window
|
||||
{
|
||||
private bool isDarkMode = true;
|
||||
private Syncronisation sync;
|
||||
private Task<bool> graphAuthenticationTask;
|
||||
|
||||
public DashboardViewModel _dashboardViewModel { get; }
|
||||
|
||||
public DashboardWindow(Syncronisation _syncronisation)
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
sync = _syncronisation;
|
||||
_dashboardViewModel = sync._dashboardViewModel;
|
||||
DataContext = _dashboardViewModel;
|
||||
// Set the ViewModel instance & Set the DataContext to the ViewModel
|
||||
//Set the DataContext to the ViewModel
|
||||
Log.DataContext = sync.LogEntries;
|
||||
}
|
||||
|
||||
private void ToggleDarkMode(object sender, RoutedEventArgs e)
|
||||
{
|
||||
isDarkMode = !isDarkMode;
|
||||
|
||||
if (isDarkMode)
|
||||
{
|
||||
this.Style = (Style)FindResource("DarkModeStyle");
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Style = (Style)FindResource("LightModeStyle");
|
||||
}
|
||||
}
|
||||
|
||||
private void SettingsButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Einstellungsfenster öffnen
|
||||
SettingsWindow settingsWindow = new SettingsWindow(sync);
|
||||
settingsWindow.ShowDialog();
|
||||
}
|
||||
|
||||
private async void TeamsGraphAPIBorder_MouseDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (!Teams.TeamsGraph.IsGuid(Settings.Default.AppID) || !Teams.TeamsGraph.IsGuid(Settings.Default.TenantID))
|
||||
{
|
||||
SettingsWindow settingsWindow = new SettingsWindow(sync);
|
||||
settingsWindow.ShowDialog();
|
||||
return;
|
||||
};
|
||||
|
||||
if (sync.TeamsGraphAuthenticationSemaphore.CurrentCount == 0)
|
||||
return;
|
||||
|
||||
bool saveCredentials = Settings.Default.SaveEntraCredentials;
|
||||
if (!sync.TeamsGraph.Authenticated)
|
||||
{
|
||||
await sync.TeamsGraphAuthenticationSemaphore.WaitAsync();
|
||||
_ = await sync.GraphAuthenticate(saveCredentials);
|
||||
sync.TeamsGraphAuthenticationSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
private void TeamsLocalAPIBorder_MouseDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (!Settings.Default.TeamsPermission)
|
||||
{
|
||||
var setupLocalTeams = new WPF.SetupLocalTeamsAPI(sync);
|
||||
setupLocalTeams.ShowDialog();
|
||||
}
|
||||
}
|
||||
|
||||
private void LogListView_UpdateColumnsWidth(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
ListView _ListView = sender as ListView;
|
||||
GridView _GridView = _ListView.View as GridView;
|
||||
var _ActualWidth = _ListView.ActualWidth - SystemParameters.VerticalScrollBarWidth;
|
||||
for (Int32 i = 1; i < _GridView.Columns.Count; i++)
|
||||
{
|
||||
_ActualWidth = _ActualWidth - _GridView.Columns[i].ActualWidth;
|
||||
}
|
||||
_GridView.Columns[0].Width = _ActualWidth;
|
||||
}
|
||||
}
|
||||
}
|
123
TeamsNetphoneLinkWPF/WPF/ExceptionWindow.xaml
Normal file
123
TeamsNetphoneLinkWPF/WPF/ExceptionWindow.xaml
Normal file
@ -0,0 +1,123 @@
|
||||
<Window x:Class="TeamsNetphoneLink.WPF.ExceptionWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="Fehlerbericht" Height="400" Width="600"
|
||||
Background="#FF1E1E1E" FontFamily="Segoe UI"
|
||||
WindowStyle="ToolWindow" ResizeMode="NoResize"
|
||||
WindowStartupLocation="CenterScreen" Topmost="True">
|
||||
|
||||
<!-- Dark Mode Resources -->
|
||||
<Window.Resources>
|
||||
<!-- Button Style -->
|
||||
<Style TargetType="Button">
|
||||
<Setter Property="Background" Value="#FF0078D4"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="BorderBrush" Value="Transparent"/>
|
||||
<Setter Property="Padding" Value="10,5"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border Background="{TemplateBinding Background}"
|
||||
CornerRadius="4"
|
||||
Padding="{TemplateBinding Padding}">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="OverridesDefaultStyle" Value="True"/>
|
||||
</Style>
|
||||
|
||||
<!-- TextBox Style -->
|
||||
<Style TargetType="TextBox">
|
||||
<Setter Property="Background" Value="#FF252526"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="BorderBrush" Value="#FF3E3E40"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="Padding" Value="3,0,0,0"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Center"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="TextBox">
|
||||
<Border Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="4">
|
||||
<ScrollViewer x:Name="PART_ContentHost" Margin="{TemplateBinding Padding}"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- TextBlock Style -->
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
</Style>
|
||||
</Window.Resources>
|
||||
|
||||
<Grid Margin="10">
|
||||
<!-- Fehlertext -->
|
||||
<TextBlock x:Name="ErrorText"
|
||||
Text="Es ist ein Fehler aufgetreten!"
|
||||
FontSize="18"
|
||||
FontWeight="Bold"
|
||||
Margin="10,10,10,20"
|
||||
VerticalAlignment="Top"
|
||||
Foreground="White"/>
|
||||
|
||||
<!-- Modul Feld -->
|
||||
<StackPanel Orientation="Horizontal" Margin="10,50,10,0">
|
||||
<TextBlock Text="Modul:" VerticalAlignment="Top" Width="60" Foreground="White" Margin="0,15,0,0"/>
|
||||
<TextBox x:Name="ModuleText"
|
||||
FontSize="14"
|
||||
VerticalAlignment="Top"
|
||||
Width="480"
|
||||
Margin="5,5,5,0"
|
||||
IsEnabled="False"
|
||||
Background="#FF252526"
|
||||
Foreground="White" Text="tetet" Height="37"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Scrollbare Fehlerdetails -->
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto"
|
||||
HorizontalScrollBarVisibility="Auto"
|
||||
Margin="10,100,10,50">
|
||||
<TextBox x:Name="TraceText"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
HorizontalScrollBarVisibility="Auto"
|
||||
IsReadOnly="True"
|
||||
FontSize="14"
|
||||
Padding="3,3,0,0"
|
||||
HorizontalContentAlignment="Left"
|
||||
VerticalContentAlignment="Top"
|
||||
Height="214"
|
||||
Background="#FF252526"
|
||||
Foreground="White" Text="tttt">
|
||||
<!-- Beispiel für Fehlerdetails -->
|
||||
</TextBox>
|
||||
</ScrollViewer>
|
||||
|
||||
<!-- Button zum Schließen -->
|
||||
<Button Content="Schließen"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Bottom"
|
||||
Width="100"
|
||||
Margin="10,0,0,10"
|
||||
Click="CloseButton_Click"/>
|
||||
|
||||
<!-- Button zum Issue melden -->
|
||||
<Button Content="Issue melden"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Bottom"
|
||||
Width="120"
|
||||
Margin="0,0,10,10"
|
||||
Click="ReportButton_Click"/>
|
||||
</Grid>
|
||||
</Window>
|
67
TeamsNetphoneLinkWPF/WPF/ExceptionWindow.xaml.cs
Normal file
67
TeamsNetphoneLinkWPF/WPF/ExceptionWindow.xaml.cs
Normal file
@ -0,0 +1,67 @@
|
||||
using System.Diagnostics;
|
||||
using System.Media;
|
||||
using System.Windows;
|
||||
|
||||
namespace TeamsNetphoneLink.WPF
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaktionslogik für ExceptionWindow.xaml
|
||||
/// </summary>
|
||||
public partial class ExceptionWindow : Window
|
||||
{
|
||||
public ExceptionWindow(string module, string trace)
|
||||
{
|
||||
InitializeComponent();
|
||||
ModuleText.Text = module;
|
||||
TraceText.Text = trace;
|
||||
SystemSounds.Hand.Play();
|
||||
}
|
||||
|
||||
// Button zum Schließen
|
||||
private void CloseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
this.Close();
|
||||
}
|
||||
|
||||
// Button zum Issue melden
|
||||
private void ReportButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Fehlerdetails aus der TextBox holen
|
||||
string moduleDetails = ModuleText.Text;
|
||||
string errorDetails = TraceText.Text;
|
||||
|
||||
// URL für das GitHub Issue, Fehlerdetails werden als Parameter angehängt
|
||||
string url = $"https://git.jan.sx/krjan02/TeamsNetphoneLink/issues/new?body={Uri.EscapeDataString(String.Format("Modul: {0}\n\nTrace:\n{1}", moduleDetails, errorDetails))}";
|
||||
|
||||
// GitHub im Standardbrowser öffnen
|
||||
Process.Start(new ProcessStartInfo(url) { UseShellExecute = true });
|
||||
}
|
||||
}
|
||||
|
||||
public static class ExceptionWindowHelper
|
||||
{
|
||||
public static void Show(string classname, string method, string message, string trace)
|
||||
{
|
||||
var modulestr = String.Format("{0} - {1}", classname, method);
|
||||
var errorstr = String.Format("{0}\n{1}", message, trace);
|
||||
|
||||
// Use the Dispatcher to show the window on the UI thread
|
||||
Application.Current.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
new ExceptionWindow(modulestr, errorstr).Show();
|
||||
});
|
||||
}
|
||||
|
||||
public static void Show(string classname, string method, string message, string tracemessage, string trace)
|
||||
{
|
||||
var modulestr = String.Format("{0} - {1}", classname, method);
|
||||
var errorstr = String.Format("{0}\n{1}\n{2}", message, tracemessage, trace);
|
||||
|
||||
// Use the Dispatcher to show the window on the UI thread
|
||||
Application.Current.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
new ExceptionWindow(modulestr, errorstr).Show();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
250
TeamsNetphoneLinkWPF/WPF/MVVM/DashboardViewModel.cs
Normal file
250
TeamsNetphoneLinkWPF/WPF/MVVM/DashboardViewModel.cs
Normal file
@ -0,0 +1,250 @@
|
||||
using System.ComponentModel;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace TeamsNetphoneLink.WPF.MVVM
|
||||
{
|
||||
public static class StatusPanelNames
|
||||
{
|
||||
public const string TeamsLocalAPIStatus = nameof(TeamsLocalAPIStatus);
|
||||
public const string TeamsGraphAPIStatus = nameof(TeamsGraphAPIStatus);
|
||||
public const string NetphoneCLMGRStatus = nameof(NetphoneCLMGRStatus);
|
||||
public const string TeamsMeetingStatus = nameof(TeamsMeetingStatus);
|
||||
public const string NetphoneCallStatus = nameof(NetphoneCallStatus);
|
||||
}
|
||||
|
||||
public class DashboardViewModel : INotifyPropertyChanged
|
||||
{
|
||||
// Parameterloser Konstruktor
|
||||
public DashboardViewModel()
|
||||
{
|
||||
// Initialisieren Sie hier Standardwerte, falls erforderlich
|
||||
TeamsLocalAPIStatusText = "Nicht verbunden";
|
||||
TeamsLocalAPIStatusBackground = new SolidColorBrush(Colors.Red);
|
||||
TeamsGraphAPIStatusText = "Konfiguration fehlt";
|
||||
TeamsGraphAPIStatusBackground = new SolidColorBrush(Colors.Red);
|
||||
NetphoneCLMGRStatusText = "Nicht verbunden";
|
||||
NetphoneCLMGRStatusBackground = new SolidColorBrush(Colors.Red);
|
||||
TeamsMeetingStatusText = "Not Active";
|
||||
TeamsMeetingStatusBackground = new SolidColorBrush(Colors.DarkOrange);
|
||||
NetphoneCallStatusText = "Not Active";
|
||||
NetphoneCallStatusBackground = new SolidColorBrush(Colors.DarkOrange);
|
||||
|
||||
AppTitle2 = "Teams Netphone Link";
|
||||
VersionText = "Aktuell";
|
||||
VersionVisibility = Visibility.Visible;
|
||||
VersionBackground = new SolidColorBrush(Colors.Green);
|
||||
}
|
||||
|
||||
|
||||
private string _appTitle;
|
||||
|
||||
private string _versionText;
|
||||
private Visibility _versionVisibility;
|
||||
private Brush _versionBackground;
|
||||
|
||||
private string _teamsLocalAPIStatusText;
|
||||
private Brush _teamsLocalAPIStatusBackground;
|
||||
private string _teamsGraphAPIStatusText;
|
||||
private Brush _teamsGraphAPIStatusBackground;
|
||||
private string _netphoneCLMGRStatusText;
|
||||
private Brush _netphoneCLMGRStatusBackground;
|
||||
private string _teamsMeetingStatusText;
|
||||
private Brush _teamsMeetingStatusBackground;
|
||||
private string _netphoneCallStatusText;
|
||||
private Brush _netphoneCallStatusBackground;
|
||||
|
||||
public string TeamsLocalAPIStatusText
|
||||
{
|
||||
get => _teamsLocalAPIStatusText;
|
||||
set
|
||||
{
|
||||
_teamsLocalAPIStatusText = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public Brush TeamsLocalAPIStatusBackground
|
||||
{
|
||||
get => _teamsLocalAPIStatusBackground;
|
||||
set
|
||||
{
|
||||
_teamsLocalAPIStatusBackground = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public string TeamsGraphAPIStatusText
|
||||
{
|
||||
get => _teamsGraphAPIStatusText;
|
||||
set
|
||||
{
|
||||
_teamsGraphAPIStatusText = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public Brush TeamsGraphAPIStatusBackground
|
||||
{
|
||||
get => _teamsGraphAPIStatusBackground;
|
||||
set
|
||||
{
|
||||
_teamsGraphAPIStatusBackground = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public string NetphoneCLMGRStatusText
|
||||
{
|
||||
get => _netphoneCLMGRStatusText;
|
||||
set
|
||||
{
|
||||
_netphoneCLMGRStatusText = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public Brush NetphoneCLMGRStatusBackground
|
||||
{
|
||||
get => _netphoneCLMGRStatusBackground;
|
||||
set
|
||||
{
|
||||
_netphoneCLMGRStatusBackground = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public string TeamsMeetingStatusText
|
||||
{
|
||||
get => _teamsMeetingStatusText;
|
||||
set
|
||||
{
|
||||
_teamsMeetingStatusText = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public Brush TeamsMeetingStatusBackground
|
||||
{
|
||||
get => _teamsMeetingStatusBackground;
|
||||
set
|
||||
{
|
||||
_teamsMeetingStatusBackground = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public string NetphoneCallStatusText
|
||||
{
|
||||
get => _netphoneCallStatusText;
|
||||
set
|
||||
{
|
||||
_netphoneCallStatusText = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public Brush NetphoneCallStatusBackground
|
||||
{
|
||||
get => _netphoneCallStatusBackground;
|
||||
set
|
||||
{
|
||||
_netphoneCallStatusBackground = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public string AppTitle2
|
||||
{
|
||||
get => _appTitle;
|
||||
set
|
||||
{
|
||||
Application.Current.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
_appTitle = value;
|
||||
OnPropertyChanged();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public string VersionText
|
||||
{
|
||||
get => _versionText;
|
||||
set
|
||||
{
|
||||
Application.Current.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
_versionText = value;
|
||||
OnPropertyChanged();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public Visibility VersionVisibility
|
||||
{
|
||||
get => _versionVisibility;
|
||||
set
|
||||
{
|
||||
Application.Current.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
_versionVisibility = value;
|
||||
OnPropertyChanged();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public Brush VersionBackground
|
||||
{
|
||||
get => _versionBackground;
|
||||
set
|
||||
{
|
||||
Application.Current.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
_versionBackground = value;
|
||||
OnPropertyChanged();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
// Method to update status panel
|
||||
public void UpdateStatusPanel(string statusPanelName, Color color, string text)
|
||||
{
|
||||
Application.Current.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
// Get the type of the ViewModel
|
||||
var type = GetType();
|
||||
|
||||
// Construct property names for Text and Background
|
||||
string textPropertyName = $"{statusPanelName}Text";
|
||||
string backgroundPropertyName = $"{statusPanelName}Background";
|
||||
|
||||
// Get the properties using reflection
|
||||
PropertyInfo textProperty = type.GetProperty(textPropertyName); //CS8600
|
||||
PropertyInfo backgroundProperty = type.GetProperty(backgroundPropertyName); //CS8600
|
||||
|
||||
if (textProperty != null && backgroundProperty != null)
|
||||
{
|
||||
// Update the Text property
|
||||
textProperty.SetValue(this, text);
|
||||
|
||||
// Update the Background property
|
||||
backgroundProperty.SetValue(this, new SolidColorBrush(color));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException($"Invalid status panel name: {statusPanelName}");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
94
TeamsNetphoneLinkWPF/WPF/MVVM/FinishPanelViewModel.cs
Normal file
94
TeamsNetphoneLinkWPF/WPF/MVVM/FinishPanelViewModel.cs
Normal file
@ -0,0 +1,94 @@
|
||||
using Emoji.Wpf;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Effects;
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace TeamsNetphoneLink.WPF.MVVM
|
||||
{
|
||||
public class FinishPanelViewModel : INotifyPropertyChanged
|
||||
{
|
||||
// Parameterloser Konstruktor
|
||||
public FinishPanelViewModel()
|
||||
{
|
||||
// Initialisieren Sie hier Standardwerte, falls erforderlich
|
||||
FinishPanelEnabled = false;
|
||||
FinishPanelFinishTextColor = new SolidColorBrush(Colors.DarkOrange);
|
||||
FinishPanelFinishTextText = "Authentifizierung ausstehend...";
|
||||
FinishPanelEffect = new TintEffect { Tint = Colors.DarkGray };
|
||||
}
|
||||
|
||||
private bool _finishPanelEnabled;
|
||||
private Brush _finishPanelFinishTextColor;
|
||||
private string _finishPanelFinishTextText;
|
||||
private Effect _finishPanelEffect;
|
||||
|
||||
public bool FinishPanelEnabled
|
||||
{
|
||||
get => _finishPanelEnabled;
|
||||
set
|
||||
{
|
||||
System.Windows.Application.Current.Dispatcher.Invoke(delegate
|
||||
{
|
||||
_finishPanelEnabled = value;
|
||||
OnPropertyChanged();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public Brush FinishPanelFinishTextColor
|
||||
{
|
||||
get => _finishPanelFinishTextColor;
|
||||
set
|
||||
{
|
||||
System.Windows.Application.Current.Dispatcher.Invoke(delegate
|
||||
{
|
||||
_finishPanelFinishTextColor = value;
|
||||
OnPropertyChanged();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public string FinishPanelFinishTextText
|
||||
{
|
||||
get => _finishPanelFinishTextText;
|
||||
set
|
||||
{
|
||||
System.Windows.Application.Current.Dispatcher.Invoke(delegate
|
||||
{
|
||||
_finishPanelFinishTextText = value;
|
||||
OnPropertyChanged();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public Effect FinishPanelEffect
|
||||
{
|
||||
get => _finishPanelEffect;
|
||||
set
|
||||
{
|
||||
System.Windows.Application.Current.Dispatcher.Invoke(delegate
|
||||
{
|
||||
_finishPanelEffect = value;
|
||||
OnPropertyChanged();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void SetFinishPanelFinishTextColor(Color color)
|
||||
{
|
||||
System.Windows.Application.Current.Dispatcher.Invoke(delegate
|
||||
{
|
||||
FinishPanelFinishTextColor = new SolidColorBrush((Color)color);
|
||||
});
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
}
|
||||
}
|
75
TeamsNetphoneLinkWPF/WPF/MVVM/LogViewModel.cs
Normal file
75
TeamsNetphoneLinkWPF/WPF/MVVM/LogViewModel.cs
Normal file
@ -0,0 +1,75 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Windows;
|
||||
|
||||
|
||||
namespace TeamsNetphoneLink.WPF.MVVM
|
||||
{
|
||||
public class LogEntry
|
||||
{
|
||||
public DateTime Time { get; set; }
|
||||
public string Subsystem { get; set; }
|
||||
public string Type { get; set; }
|
||||
public string Message { get; set; }
|
||||
}
|
||||
|
||||
public class LogViewModel : INotifyPropertyChanged
|
||||
{
|
||||
// Collection to hold log entries
|
||||
public ObservableCollection<LogEntry> LogEntries { get; } = new ObservableCollection<LogEntry>();
|
||||
|
||||
// Add a log entry
|
||||
public void AddLogEntry(string entry)
|
||||
{
|
||||
Application.Current.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
LogEntries.Add(new LogEntry { Time = DateTime.Now, Type = "Info", Subsystem = "NONE", Message = entry });
|
||||
});
|
||||
}
|
||||
|
||||
// Add a log entry
|
||||
public void AddLogEntry(string subsystem, string entry)
|
||||
{
|
||||
Application.Current.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
LogEntries.Add(new LogEntry { Time = DateTime.Now, Type = "Info", Subsystem = subsystem, Message = entry });
|
||||
});
|
||||
}
|
||||
|
||||
// Add a log entry
|
||||
public void AddLogEntry(string type, string subsystem, string entry)
|
||||
{
|
||||
Application.Current.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
LogEntries.Add(new LogEntry { Time = DateTime.Now, Type = type, Subsystem = subsystem, Message = entry });
|
||||
});
|
||||
}
|
||||
|
||||
public void AddLogEntry(DateTime time, string subsystem, string message)
|
||||
{
|
||||
Application.Current.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
LogEntries.Add(new LogEntry { Time = time, Type = "Info", Subsystem = subsystem, Message = message });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Clear all log entries
|
||||
public void ClearLogEntries()
|
||||
{
|
||||
Application.Current.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
LogEntries.Clear();
|
||||
});
|
||||
}
|
||||
|
||||
// Implement INotifyPropertyChanged
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
}
|
||||
}
|
143
TeamsNetphoneLinkWPF/WPF/SettingsWindow.xaml
Normal file
143
TeamsNetphoneLinkWPF/WPF/SettingsWindow.xaml
Normal file
@ -0,0 +1,143 @@
|
||||
<Window x:Class="TeamsNetphoneLink.WPF.SettingsWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="Einstellungen" Height="373" Width="500"
|
||||
WindowStyle="ToolWindow" ResizeMode="NoResize"
|
||||
Background="#FF1E1E1E" FontFamily="Segoe UI"
|
||||
WindowStartupLocation="CenterScreen">
|
||||
|
||||
<!-- Dark Mode Resources -->
|
||||
<Window.Resources>
|
||||
<!-- Button Style -->
|
||||
<Style TargetType="Button">
|
||||
<Setter Property="Background" Value="#FF0078D4"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="BorderBrush" Value="Transparent"/>
|
||||
<Setter Property="Padding" Value="10,5"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border x:Name="border"
|
||||
Background="{TemplateBinding Background}"
|
||||
CornerRadius="4"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
Opacity="1">
|
||||
<ContentPresenter HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<!-- Hover Effect -->
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="border" Property="Background" Value="#0066B3"/>
|
||||
</Trigger>
|
||||
|
||||
<!-- Pressed Effect -->
|
||||
<Trigger Property="IsPressed" Value="True">
|
||||
<Setter TargetName="border" Property="Background" Value="#004D86"/>
|
||||
<Setter TargetName="border" Property="RenderTransform">
|
||||
<Setter.Value>
|
||||
<ScaleTransform ScaleX="0.98" ScaleY="0.98"/>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Trigger>
|
||||
|
||||
<!-- Disabled State -->
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter TargetName="border" Property="Background" Value="#3A3A3A"/>
|
||||
<Setter TargetName="border" Property="Opacity" Value="0.7"/>
|
||||
<Setter Property="Foreground" Value="#A0A0A0"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="OverridesDefaultStyle" Value="True"/>
|
||||
</Style>
|
||||
|
||||
<!-- TextBox Style -->
|
||||
<Style TargetType="TextBox">
|
||||
<Setter Property="Background" Value="#FF252526"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="BorderBrush" Value="#FF3E3E40"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="Padding" Value="3,0,0,0"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Center"/>
|
||||
<Setter Property="Validation.ErrorTemplate">
|
||||
<Setter.Value>
|
||||
<ControlTemplate>
|
||||
<Border BorderBrush="Red" BorderThickness="1">
|
||||
<AdornedElementPlaceholder/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="TextBox">
|
||||
<Border Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="4">
|
||||
<ScrollViewer x:Name="PART_ContentHost" Margin="0,0,0,0"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- CheckBox Style -->
|
||||
<Style TargetType="CheckBox">
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
</Style>
|
||||
|
||||
<!-- TextBlock Style -->
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
</Style>
|
||||
</Window.Resources>
|
||||
|
||||
<Grid Margin="20">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<!-- Title -->
|
||||
<TextBlock Text="Einstellungen" FontSize="24" FontWeight="SemiBold" Margin="0,0,0,20"/>
|
||||
|
||||
<!-- Microsoft 365 Tenant ID -->
|
||||
<StackPanel Orientation="Horizontal" Margin="0,10">
|
||||
<TextBlock Text="Tenant ID" VerticalAlignment="Center" Width="70" FontSize="14"/>
|
||||
<TextBox x:Name="TenantIDTextBox" Width="372" Height="30" FontSize="14" TextChanged="TextBox_TextChanged" Text="dddddd"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Microsoft 365 App ID -->
|
||||
<StackPanel Orientation="Horizontal" Margin="0,10">
|
||||
<TextBlock Text="App ID" VerticalAlignment="Center" Width="70" FontSize="14"/>
|
||||
<TextBox x:Name="AppIDTextBox" Width="372" Height="30" TextChanged="TextBox_TextChanged"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Toggle SaveEntraCredentials -->
|
||||
<StackPanel Orientation="Horizontal" Margin="0,10">
|
||||
<CheckBox x:Name="SaveEntraCredentialsCheck" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="Entra Login speichern" Width="137" FontSize="14" Margin="10,0,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Toggle UseGraphForMeetingState -->
|
||||
<StackPanel Orientation="Horizontal" Margin="0,10" ToolTip="Nutzt statt der lokalen Teams API die Graph API um festzustellen das ein Meeting läuft">
|
||||
<CheckBox x:Name="UseGraphForMeetingStateCheck"/>
|
||||
<TextBlock Text="Graph für den Meeting Status verwenden" VerticalAlignment="Center" Width="259" FontSize="14" Margin="10,0,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Save and Reset Buttons -->
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,30,0,0">
|
||||
<Button x:Name="ResetButton" Background="DarkRed" Content="Alles zurücksetzen" Width="140" Height="35" Margin="0,0,10,0" Click="ResetButton_Click"/>
|
||||
<Button x:Name="SaveButton" Content="Speichern" Width="100" Height="35" Click="SaveButton_Click" IsEnabled="False"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
132
TeamsNetphoneLinkWPF/WPF/SettingsWindow.xaml.cs
Normal file
132
TeamsNetphoneLinkWPF/WPF/SettingsWindow.xaml.cs
Normal file
@ -0,0 +1,132 @@
|
||||
using Microsoft.Identity.Client.NativeInterop;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Shapes;
|
||||
using TeamsNetphoneLink;
|
||||
|
||||
namespace TeamsNetphoneLink.WPF
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaktionslogik für SettingsWindow.xaml
|
||||
/// </summary>
|
||||
public partial class SettingsWindow : Window
|
||||
{
|
||||
Syncronisation _sync;
|
||||
|
||||
public SettingsWindow(Syncronisation sync)
|
||||
{
|
||||
_sync = sync;
|
||||
InitializeComponent();
|
||||
TenantIDTextBox.Text = Settings.Default.TenantID;
|
||||
AppIDTextBox.Text = Settings.Default.AppID;
|
||||
SaveEntraCredentialsCheck.IsChecked = Settings.Default.SaveEntraCredentials;
|
||||
UseGraphForMeetingStateCheck.IsChecked = Settings.Default.UseGraphForMeetingState;
|
||||
}
|
||||
|
||||
private void RestartApplication()
|
||||
{
|
||||
string exePath = Process.GetCurrentProcess().MainModule.FileName;
|
||||
var startInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = exePath,
|
||||
UseShellExecute = true
|
||||
};
|
||||
Process.Start(startInfo);
|
||||
Application.Current.Shutdown();
|
||||
}
|
||||
|
||||
// Speichern der Einstellungen
|
||||
private void SaveButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
string tenantId = TenantIDTextBox.Text.Replace(" ", "");
|
||||
string appId = AppIDTextBox.Text.Replace(" ", "");
|
||||
bool SaveEntraCredentials = SaveEntraCredentialsCheck.IsChecked.HasValue ? SaveEntraCredentialsCheck.IsChecked.Value : false;
|
||||
bool UseGraphForMeetingState = UseGraphForMeetingStateCheck.IsChecked.HasValue ? UseGraphForMeetingStateCheck.IsChecked.Value : false;
|
||||
|
||||
if (SaveEntraCredentials && !ShowSecurityWarningMessage())
|
||||
return;
|
||||
|
||||
Settings.Default.TenantID = tenantId;
|
||||
Settings.Default.AppID = appId;
|
||||
Settings.Default.SaveEntraCredentials = SaveEntraCredentials;
|
||||
Settings.Default.UseGraphForMeetingState = UseGraphForMeetingState;
|
||||
Settings.Default.Save();
|
||||
|
||||
RestartApplication();
|
||||
}
|
||||
|
||||
// Reset der Einstellungen
|
||||
private void ResetButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Settings.Default.Reset();
|
||||
RestartApplication();
|
||||
}
|
||||
|
||||
private void SaveEntraCredentialsCheck_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
//ShowSecurityWarningMessage();
|
||||
}
|
||||
|
||||
public bool ShowSecurityWarningMessage()
|
||||
{
|
||||
string message =
|
||||
"Die Anmeldedaten werden mit dem MSAL Cache unter Verwendung von DPAPI (Data Protection API) gespeichert. " +
|
||||
"DPAPI bietet eine sichere Verschlüsselung der Daten auf Benutzerebene, sodass die Anmeldedaten nur für das Benutzerkonto zugänglich sind, " +
|
||||
"unter dem sie gespeichert wurden.\n\n" +
|
||||
"Allerdings ist zu beachten, dass theoretisch jede Anwendung mit derselben App-ID auf den MSAL Cache zugreifen könnte. " +
|
||||
"Das liegt daran, dass der Cache anhand der App-ID identifiziert wird. Wenn eine andere Anwendung dieselbe App-ID verwendet, " +
|
||||
"könnte sie versuchen, auf die gespeicherten Anmeldedaten zuzugreifen.\n\n" +
|
||||
"Bewertung des Sicherheitsrisikos:\n" +
|
||||
"- In einer vertrauenswürdigen Umgebung ist das Risiko gering.\n" +
|
||||
"- Die App-ID sollte geheim gehalten werden, um unbefugten Zugriff zu verhindern.\n" +
|
||||
"- DPAPI bietet eine starke Verschlüsselung, solange das Benutzerkonto nicht kompromittiert ist.\n\n" +
|
||||
"Unter normalen Umständen stellt die Verwendung des MSAL Caches kein signifikantes Sicherheitsrisiko dar. " +
|
||||
"Es ist jedoch wichtig, die App-ID geheim zu halten und sicherzustellen, dass nur vertrauenswürdige Anwendungen dieselbe App-ID verwenden.\n\n" +
|
||||
"Sollen die Einstellungen gespeichert werden?.\n\n";
|
||||
|
||||
var result = MessageBox.Show(
|
||||
message,
|
||||
"Sicherheitshinweis zur Speicherung von Anmeldedaten",
|
||||
MessageBoxButton.YesNo,
|
||||
MessageBoxImage.Warning
|
||||
);
|
||||
|
||||
if (result == MessageBoxResult.Yes)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
ValidateInput();
|
||||
}
|
||||
|
||||
private void ValidateInput()
|
||||
{
|
||||
if (this.IsInitialized)
|
||||
{
|
||||
bool isAppIDValid = Teams.TeamsGraph.IsGuid(AppIDTextBox.Text);
|
||||
bool isTenantIDValid = Teams.TeamsGraph.IsGuid(TenantIDTextBox.Text);
|
||||
|
||||
// Set border color based on validation
|
||||
AppIDTextBox.BorderBrush = isAppIDValid ? System.Windows.Media.Brushes.Green : System.Windows.Media.Brushes.Red;
|
||||
TenantIDTextBox.BorderBrush = isTenantIDValid ? System.Windows.Media.Brushes.Green : System.Windows.Media.Brushes.Red;
|
||||
|
||||
// Enable Save button only if both fields are valid
|
||||
SaveButton.IsEnabled = isAppIDValid && isTenantIDValid;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
83
TeamsNetphoneLinkWPF/WPF/SetupLocalTeamsAPI.xaml
Normal file
83
TeamsNetphoneLinkWPF/WPF/SetupLocalTeamsAPI.xaml
Normal file
@ -0,0 +1,83 @@
|
||||
<Window
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:Wpf="clr-namespace:Emoji.Wpf;assembly=Emoji.Wpf" xmlns:local="clr-namespace:TeamsNetphoneLink.WPF.MVVM" x:Class="TeamsNetphoneLink.WPF.SetupLocalTeamsAPI"
|
||||
WindowStyle="ToolWindow" ResizeMode="NoResize"
|
||||
Background="#FF1E1E1E" FontFamily="Segoe UI"
|
||||
Title="Teams API Integration" Height="497" Width="525">
|
||||
|
||||
<Window.Resources>
|
||||
<Style TargetType="Button">
|
||||
<Setter Property="Background" Value="#FF0078D4"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="BorderBrush" Value="Transparent"/>
|
||||
<Setter Property="Padding" Value="10,5"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border Background="{TemplateBinding Background}"
|
||||
CornerRadius="4"
|
||||
Padding="{TemplateBinding Padding}">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="OverridesDefaultStyle" Value="True"/>
|
||||
</Style>
|
||||
|
||||
<!-- TextBlock Style -->
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
</Style>
|
||||
|
||||
</Window.Resources>
|
||||
|
||||
<Grid>
|
||||
<StackPanel>
|
||||
<TextBlock Text="Bitte folgende Schritte durchführen, um die Teams API zu aktivieren und damit die Anwendung zu genehmigen." Margin="10" Height="45" TextWrapping="Wrap" TextAlignment="Center" FontWeight="Bold" FontSize="16"/>
|
||||
|
||||
<StackPanel x:Name="Step1Panel" Margin="10">
|
||||
<TextBlock Text="1. Aktivieren der Teams Drittanbieter API:"/>
|
||||
<!-- Hyperlink in einem TextBlock unterbringen -->
|
||||
<TextBlock>
|
||||
<Hyperlink x:Name="Step1Link" NavigateUri="https://support.microsoft.com/de-de/office/herstellen-einer-verbindung-mit-drittanbieterger%C3%A4ten-in-microsoft-teams-aabca9f2-47bb-407f-9f9b-81a104a883d6" RequestNavigate="Step1Link_RequestNavigate">
|
||||
<Run Text="Anleitung für die Aktivierung der Drittanbieter API"/>
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
<Button x:Name="Step1Button" Content="Drittanbieter-API aktiviert" Width="200" Margin="10" Click="Step1Button_Click"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel x:Name="Step2Panel" Margin="10" Height="61" IsEnabled="False" >
|
||||
<StackPanel.Effect >
|
||||
<Wpf:TintEffect Tint="#FFA4A0A0"/>
|
||||
</StackPanel.Effect>
|
||||
<TextBlock Text="2. Starten Sie ein Teams Meeting mit sich selbst:"/>
|
||||
<Button x:Name="Step2Button" Content="Teams Meeting ist gestartet" Width="200" Margin="10" Click="Step2Button_Click"/>
|
||||
<TextBlock x:Name="Step2Status" Margin="10"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Schritt 3: Authentifizieren -->
|
||||
<StackPanel x:Name="Step3Panel" Margin="10" IsEnabled="False" >
|
||||
<StackPanel.Effect >
|
||||
<Wpf:TintEffect Tint="#FFA4A0A0"/>
|
||||
</StackPanel.Effect>
|
||||
<TextBlock Text="3. Zulassen der Verbindungsanforderung:"/>
|
||||
<TextBlock Margin="10,0,10,0" Height="34" TextWrapping="Wrap" TextAlignment="Center" FontSize="12"><Run Text="Nach dem Klick auf dem Button wird in Teams eine Anforderung sichtbar, diese bitte zulassen"/></TextBlock>
|
||||
<Button x:Name="Step3Button" Content="Verbindungsanforderung senden" Width="227" Margin="10" Click="Step3Button_Click"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Schritt 3: Authentifizieren -->
|
||||
<StackPanel x:Name="FinishPanel" Margin="10" IsEnabled="{Binding FinishPanelEnabled}" Effect="{Binding FinishPanelEffect}" >
|
||||
<TextBlock Foreground="{Binding FinishPanelFinishTextColor}" Text="{Binding FinishPanelFinishTextText}" FontWeight="Bold" HorizontalAlignment="Center"/>
|
||||
<Button x:Name="FinishButton" Content="Abschließen" Width="200" Margin="10" Click="FinishButton_Click"/>
|
||||
</StackPanel>
|
||||
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
83
TeamsNetphoneLinkWPF/WPF/SetupLocalTeamsAPI.xaml.cs
Normal file
83
TeamsNetphoneLinkWPF/WPF/SetupLocalTeamsAPI.xaml.cs
Normal file
@ -0,0 +1,83 @@
|
||||
using Emoji.Wpf;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Security.Policy;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
using TeamsNetphoneLink.WPF.MVVM;
|
||||
|
||||
namespace TeamsNetphoneLink.WPF
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaktionslogik für SetupLocalTeamsAPI.xaml
|
||||
/// </summary>
|
||||
public partial class SetupLocalTeamsAPI : Window
|
||||
{
|
||||
private Teams.TeamsLocalAPI teamsLocalAPI;
|
||||
public FinishPanelViewModel finishPanelViewModel { get; }
|
||||
|
||||
public SetupLocalTeamsAPI(Syncronisation sync)
|
||||
{
|
||||
teamsLocalAPI = sync.TeamsEvents;
|
||||
finishPanelViewModel = sync.FinishPanelViewModel;
|
||||
DataContext = finishPanelViewModel;
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void Step1Link_RequestNavigate(object sender, RequestNavigateEventArgs e)
|
||||
{
|
||||
// Öffnet den Link, um die Teams API zu aktivieren
|
||||
Process.Start(new ProcessStartInfo(e.Uri.ToString().Replace("&", "^&")) { UseShellExecute = true });
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void Step1Button_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Schritt 1 abgeschlossen - Schalte auf den nächsten Schritt
|
||||
Step1Panel.IsEnabled = false;
|
||||
Step2Panel.IsEnabled = true;
|
||||
Step1Panel.Effect = new TintEffect { Tint = Colors.DarkGray };
|
||||
Step2Panel.Effect = null;
|
||||
}
|
||||
|
||||
// Schritt 2: Teams Meeting starten
|
||||
private void Step2Button_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Öffne die URL, um ein Meeting zu starten (Hier muss der genaue Link oder die API-Integration erfolgen)
|
||||
Step2Panel.IsEnabled = false;
|
||||
Step3Panel.IsEnabled = true;
|
||||
Step2Panel.Effect = new TintEffect { Tint = Colors.DarkGray };
|
||||
Step3Panel.Effect = null;
|
||||
}
|
||||
|
||||
// Schritt 3: Authentifizierung durchführen
|
||||
private async void Step3Button_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Simuliere den Authentifizierungsprozess
|
||||
Step3Button.Content = "Authentifizierung gesendet...";
|
||||
Step3Button.IsEnabled = false;
|
||||
|
||||
this.finishPanelViewModel.FinishPanelFinishTextText = "Authentifizierung läuft...";
|
||||
this.finishPanelViewModel.FinishPanelFinishTextColor = new SolidColorBrush(Colors.Orange);
|
||||
|
||||
teamsLocalAPI.SendDummyCommand();
|
||||
}
|
||||
|
||||
private void FinishButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
this.Close();
|
||||
}
|
||||
}
|
||||
}
|
183
TeamsNetphoneLinkWPF/WPF/UpdateWindow.xaml
Normal file
183
TeamsNetphoneLinkWPF/WPF/UpdateWindow.xaml
Normal file
@ -0,0 +1,183 @@
|
||||
<Window x:Class="TeamsNetphoneLink.WPF.UpdateWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="Update verfügbar" Height="514" Width="833"
|
||||
Background="#FF1E1E1E" FontFamily="Segoe UI"
|
||||
WindowStyle="ToolWindow" ResizeMode="NoResize"
|
||||
WindowStartupLocation="CenterScreen" Closing="Window_Closing">
|
||||
|
||||
<!-- Dark Mode Resources -->
|
||||
<Window.Resources>
|
||||
<!-- Button Style -->
|
||||
<Style TargetType="Button">
|
||||
<Setter Property="Background" Value="#FF0078D4"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="BorderBrush" Value="Transparent"/>
|
||||
<Setter Property="Padding" Value="10,5"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border Background="{TemplateBinding Background}"
|
||||
CornerRadius="4"
|
||||
Padding="{TemplateBinding Padding}">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="OverridesDefaultStyle" Value="True"/>
|
||||
</Style>
|
||||
|
||||
<!-- Special Style for Markdown TextBox -->
|
||||
<Style x:Key="MarkdownTextBoxStyle" TargetType="TextBox" BasedOn="{StaticResource {x:Type TextBox}}">
|
||||
<Setter Property="FontFamily" Value="Consolas"/>
|
||||
<Setter Property="Foreground" Value="#FFD4D4D4"/>
|
||||
<Setter Property="Background" Value="#FF1E1E1E"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="IsReadOnly" Value="True"/>
|
||||
<Setter Property="TextWrapping" Value="Wrap"/>
|
||||
<Setter Property="VerticalScrollBarVisibility" Value="Auto"/>
|
||||
<Setter Property="HorizontalScrollBarVisibility" Value="Disabled"/>
|
||||
</Style>
|
||||
|
||||
<!-- TextBox Style -->
|
||||
<Style TargetType="TextBox">
|
||||
<Setter Property="Background" Value="#FF252526"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="BorderBrush" Value="#FF3E3E40"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="Padding" Value="5"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Center"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="TextBox">
|
||||
<Border Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="4">
|
||||
<ScrollViewer x:Name="PART_ContentHost" Margin="{TemplateBinding Padding}"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- ProgressBar Style -->
|
||||
<Style TargetType="ProgressBar">
|
||||
<Setter Property="Background" Value="#FF252526"/>
|
||||
<Setter Property="BorderBrush" Value="#FF3E3E40"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="Foreground" Value="#FF0078D4"/>
|
||||
<Setter Property="Height" Value="10"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ProgressBar">
|
||||
<Grid x:Name="Root">
|
||||
<Border x:Name="PART_Track"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="5"/>
|
||||
<Border x:Name="PART_Indicator"
|
||||
CornerRadius="5"
|
||||
HorizontalAlignment="Left">
|
||||
<Border.Background>
|
||||
<LinearGradientBrush StartPoint="0,0.5" EndPoint="1,0.5">
|
||||
<GradientStop Color="#FF0078D4" Offset="0"/>
|
||||
<GradientStop Color="#FF00B4FF" Offset="1"/>
|
||||
</LinearGradientBrush>
|
||||
</Border.Background>
|
||||
</Border>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- TextBlock Style -->
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
</Style>
|
||||
|
||||
<!-- Title TextBlock Style -->
|
||||
<Style x:Key="TitleTextStyle" TargetType="TextBlock" BasedOn="{StaticResource {x:Type TextBlock}}">
|
||||
<Setter Property="FontSize" Value="18"/>
|
||||
<Setter Property="FontWeight" Value="Bold"/>
|
||||
<Setter Property="Margin" Value="10,10,10,20"/>
|
||||
</Style>
|
||||
|
||||
<!-- Status TextBlock Style -->
|
||||
<Style x:Key="StatusTextStyle" TargetType="TextBlock" BasedOn="{StaticResource {x:Type TextBlock}}">
|
||||
<Setter Property="Margin" Value="10,0,10,5"/>
|
||||
<Setter Property="Foreground" Value="#FFD4D4D4"/>
|
||||
</Style>
|
||||
|
||||
</Window.Resources>
|
||||
|
||||
<Grid Margin="10">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Title -->
|
||||
<TextBlock x:Name="TitleText"
|
||||
Text="Es ist ein neues Update verfügbar"
|
||||
Style="{StaticResource TitleTextStyle}"
|
||||
Grid.Row="0"/>
|
||||
|
||||
<!-- Markdown Release Notes -->
|
||||
<Border Grid.Row="1"
|
||||
Margin="10,0,10,10"
|
||||
Background="#FF252526"
|
||||
CornerRadius="4"
|
||||
|
||||
BorderBrush="#FF3E3E40"
|
||||
BorderThickness="1">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" Padding="5">
|
||||
<TextBox x:Name="ReleaseNotesTextBox"
|
||||
Style="{StaticResource MarkdownTextBoxStyle}"
|
||||
HorizontalContentAlignment="Left"
|
||||
VerticalContentAlignment="Top"
|
||||
Text="Lade Release Notes..."/>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
|
||||
<!-- Progress Status -->
|
||||
<TextBlock x:Name="ProgressStatusText"
|
||||
Text=""
|
||||
Style="{StaticResource StatusTextStyle}"
|
||||
Grid.Row="2"/>
|
||||
|
||||
<!-- Progress Bar -->
|
||||
<ProgressBar x:Name="UpdateProgress"
|
||||
Margin="10,0,10,10"
|
||||
Grid.Row="3"/>
|
||||
|
||||
<!-- Buttons -->
|
||||
<StackPanel Grid.Row="4"
|
||||
HorizontalAlignment="Right"
|
||||
Orientation="Horizontal"
|
||||
Margin="0,10,0,0">
|
||||
<Button x:Name="CloseButton"
|
||||
Content="Schließen"
|
||||
Width="100"
|
||||
Margin="0,0,10,0"
|
||||
Click="CloseButton_Click"/>
|
||||
<Button x:Name="UpdateButton"
|
||||
Content="Update durchführen"
|
||||
Width="150"
|
||||
Background="Green"
|
||||
Click="UpdateButton_Click"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
229
TeamsNetphoneLinkWPF/WPF/UpdateWindow.xaml.cs
Normal file
229
TeamsNetphoneLinkWPF/WPF/UpdateWindow.xaml.cs
Normal file
@ -0,0 +1,229 @@
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text.Json;
|
||||
using System.Windows;
|
||||
|
||||
namespace TeamsNetphoneLink.WPF
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaktionslogik für ExceptionWindow.xaml
|
||||
/// </summary>
|
||||
public partial class UpdateWindow : Window
|
||||
{
|
||||
private bool _updateRunning = false;
|
||||
private string _currentVersion;
|
||||
private string _latestVersion;
|
||||
|
||||
|
||||
public UpdateWindow(string currentVersion, string latestVersion)
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
// Step 1: Check for updates
|
||||
_currentVersion = currentVersion;
|
||||
_latestVersion = latestVersion;
|
||||
|
||||
TitleText.Text = $"Es ist ein Update von {currentVersion} auf {latestVersion} verfügbar";
|
||||
|
||||
LoadReleaseNotesAsync();
|
||||
}
|
||||
|
||||
private async void LoadReleaseNotesAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var notes = await GetReleaseNotes();
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
ReleaseNotesTextBox.Text = notes;
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
ReleaseNotesTextBox.Text = $"Failed to load release notes: {ex.Message}";
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Button zum Schließen
|
||||
private void CloseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
this.Close();
|
||||
}
|
||||
|
||||
// Button zum Updaten
|
||||
private void UpdateButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
this._updateRunning = true;
|
||||
this.UpdateButton.IsEnabled = false;
|
||||
this.CloseButton.IsEnabled = false;
|
||||
_ = UpdateApplicationAsync();
|
||||
}
|
||||
|
||||
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
|
||||
{
|
||||
if (this._updateRunning)
|
||||
{
|
||||
e.Cancel = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void StartUpdater(string tempExtractPath)
|
||||
{
|
||||
string appPath = AppDomain.CurrentDomain.BaseDirectory;
|
||||
string updaterPathEXE = Path.Combine(appPath, "TeamsNetphoneLinkUpdater.exe");
|
||||
string updaterPathDLL = Path.Combine(appPath, "TeamsNetphoneLinkUpdater.dll");
|
||||
string newUpdaterPathEXE = Path.Combine(tempExtractPath, "TeamsNetphoneLinkUpdater.exe");
|
||||
string newUpdaterPathDLL = Path.Combine(tempExtractPath, "TeamsNetphoneLinkUpdater.dll");
|
||||
|
||||
//Replace the updater if it exists in the new release
|
||||
if (File.Exists(newUpdaterPathEXE))
|
||||
{
|
||||
File.Copy(newUpdaterPathEXE, updaterPathEXE, true);
|
||||
}
|
||||
if (File.Exists(newUpdaterPathDLL))
|
||||
{
|
||||
File.Copy(newUpdaterPathDLL, updaterPathDLL, true);
|
||||
}
|
||||
|
||||
System.Diagnostics.Process p = new System.Diagnostics.Process();
|
||||
p.StartInfo.FileName = updaterPathEXE;
|
||||
p.StartInfo.ArgumentList.Add(appPath);
|
||||
p.StartInfo.ArgumentList.Add(tempExtractPath);
|
||||
p.StartInfo.UseShellExecute = true; // This will start the process in a new shell
|
||||
p.Start();
|
||||
|
||||
// Exit the application
|
||||
Application.Current.Shutdown();
|
||||
}
|
||||
|
||||
private static void UnzipRelease(string zipPath, string extractPath)
|
||||
{
|
||||
ZipFile.ExtractToDirectory(zipPath, extractPath);
|
||||
}
|
||||
|
||||
private async Task UpdateApplicationAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
if (_latestVersion != _currentVersion)
|
||||
{
|
||||
UpdateProgress.Value = 0;
|
||||
|
||||
// Step 2: Download the new release
|
||||
string downloadUrl = $"https://git.jan.sx/krjan02/TeamsNetphoneLink/releases/download/latest/NetphoneTeamsLink{_latestVersion}.zip";
|
||||
var tempZipPath = Path.GetTempFileName();
|
||||
tempZipPath = tempZipPath + ".TEAMSNETPHONEUPDATE";
|
||||
Console.WriteLine(tempZipPath);
|
||||
await DownloadReleaseAsync(downloadUrl, tempZipPath);
|
||||
|
||||
// Step 3: Unzip the release
|
||||
var tempExtractPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString() + "TEAMSNETPHONEUPDATE");
|
||||
|
||||
Directory.CreateDirectory(tempExtractPath);
|
||||
UnzipRelease(tempZipPath, tempExtractPath);
|
||||
|
||||
File.Delete(tempZipPath);
|
||||
|
||||
// Step 4: Replace Updater.exe and run the updater
|
||||
StartUpdater(tempExtractPath);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Dispatcher.Invoke(() => ProgressStatusText.Text = $"Error: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<string> GetReleaseNotes()
|
||||
{
|
||||
string apiUrl = $"https://git.jan.sx/api/v1/repos/krjan02/TeamsNetphoneLink/releases/tags/{_latestVersion}";
|
||||
using (HttpClient client = new HttpClient())
|
||||
{
|
||||
// Send a GET request to the Gitea API
|
||||
HttpResponseMessage response = await client.GetAsync(apiUrl);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
// Parse the JSON response
|
||||
string jsonResponse = await response.Content.ReadAsStringAsync();
|
||||
using (JsonDocument doc = JsonDocument.Parse(jsonResponse))
|
||||
{
|
||||
JsonElement root = doc.RootElement;
|
||||
JsonElement body = root.GetProperty("body");
|
||||
|
||||
return body.GetString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async Task<long> GetFileSize()
|
||||
{
|
||||
string apiUrl = $"https://git.jan.sx/api/v1/repos/krjan02/TeamsNetphoneLink/releases/tags/{_latestVersion}";
|
||||
using (HttpClient client = new HttpClient())
|
||||
{
|
||||
// Send a GET request to the Gitea API
|
||||
HttpResponseMessage response = await client.GetAsync(apiUrl);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
// Parse the JSON response
|
||||
string jsonResponse = await response.Content.ReadAsStringAsync();
|
||||
using (JsonDocument doc = JsonDocument.Parse(jsonResponse))
|
||||
{
|
||||
JsonElement root = doc.RootElement;
|
||||
JsonElement assets = root.GetProperty("assets");
|
||||
|
||||
// Iterate through the assets to find the file
|
||||
foreach (JsonElement asset in assets.EnumerateArray())
|
||||
{
|
||||
string name = asset.GetProperty("name").GetString();
|
||||
if (name == $"NetphoneTeamsLink{_latestVersion}.zip")
|
||||
{
|
||||
long size = asset.GetProperty("size").GetInt64();
|
||||
return size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private async Task DownloadReleaseAsync(string downloadUrl, string destinationPath)
|
||||
{
|
||||
using (var client = new HttpClient())
|
||||
{
|
||||
var response = await client.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var totalBytes = await GetFileSize();
|
||||
var downloadedBytes = 0L;
|
||||
var buffer = new byte[8192];
|
||||
|
||||
using (var fileStream = new FileStream(destinationPath, FileMode.Create, FileAccess.Write, FileShare.None))
|
||||
using (var stream = await response.Content.ReadAsStreamAsync())
|
||||
{
|
||||
int bytesRead;
|
||||
while ((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length)) > 0)
|
||||
{
|
||||
fileStream.Write(buffer, 0, bytesRead);
|
||||
downloadedBytes += bytesRead;
|
||||
// Update progress in the UI
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
ProgressStatusText.Text = $"Update wird heruntergeladen... ({downloadedBytes/1000}kB/{totalBytes/1000}kB)";
|
||||
UpdateProgress.Value = (double)downloadedBytes / totalBytes * 100;
|
||||
//StatusText.Text = $"Downloading... {DownloadProgress.Value:F2}%";
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user