Tutorial for LinphoneSDK x UWP - C#

This commit is contained in:
Anthony Gauchy
2020-12-10 17:20:19 +01:00
parent 50483699b5
commit 29f4bbef5c
215 changed files with 13724 additions and 0 deletions

View File

@@ -0,0 +1,215 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProjectGuid>{C3BB19EB-D50B-4FEB-A097-899C67281A13}</ProjectGuid>
<OutputType>AppContainerExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>_05_FileTransfer</RootNamespace>
<AssemblyName>05_FileTransfer</AssemblyName>
<DefaultLanguage>en-US</DefaultLanguage>
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
<TargetPlatformVersion Condition=" '$(TargetPlatformVersion)' == '' ">10.0.19041.0</TargetPlatformVersion>
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
<MinimumVisualStudioVersion>14</MinimumVisualStudioVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WindowsXamlEnableOverview>true</WindowsXamlEnableOverview>
<AppxPackageSigningEnabled>false</AppxPackageSigningEnabled>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<NoWarn>;2008</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<Optimize>true</Optimize>
<NoWarn>;2008</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\ARM\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<NoWarn>;2008</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>ARM</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM'">
<OutputPath>bin\ARM\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<Optimize>true</Optimize>
<NoWarn>;2008</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>ARM</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\ARM64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<NoWarn>;2008</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>ARM64</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM64'">
<OutputPath>bin\ARM64\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<Optimize>true</Optimize>
<NoWarn>;2008</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>ARM64</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<NoWarn>;2008</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<Optimize>true</Optimize>
<NoWarn>;2008</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
</PropertyGroup>
<PropertyGroup>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
</PropertyGroup>
<ItemGroup>
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\MessageDisplay.xaml.cs">
<DependentUpon>MessageDisplay.xaml</DependentUpon>
</Compile>
<Compile Include="Service\NavigationService.cs" />
<Compile Include="Service\VideoService.cs" />
<Compile Include="Views\CallsPage.xaml.cs">
<DependentUpon>CallsPage.xaml</DependentUpon>
</Compile>
<Compile Include="Service\CoreService.cs" />
<Compile Include="Views\ChatPage.xaml.cs">
<DependentUpon>ChatPage.xaml</DependentUpon>
</Compile>
<Compile Include="Views\ChatsPage.xaml.cs">
<DependentUpon>ChatsPage.xaml</DependentUpon>
</Compile>
<Compile Include="Views\LoginPage.xaml.cs">
<DependentUpon>LoginPage.xaml</DependentUpon>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Views\NavigationRoot.xaml.cs">
<DependentUpon>NavigationRoot.xaml</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<AppxManifest Include="Package.appxmanifest">
<SubType>Designer</SubType>
</AppxManifest>
</ItemGroup>
<ItemGroup>
<Content Include="Properties\Default.rd.xml" />
<Content Include="Assets\LockScreenLogo.scale-200.png" />
<Content Include="Assets\SplashScreen.scale-200.png" />
<Content Include="Assets\Square150x150Logo.scale-200.png" />
<Content Include="Assets\Square44x44Logo.scale-200.png" />
<Content Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png" />
<Content Include="Assets\StoreLogo.png" />
<Content Include="Assets\Wide310x150Logo.scale-200.png" />
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="App.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Page Include="Controls\MessageDisplay.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\ChatPage.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Views\ChatsPage.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Views\LoginPage.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Views\CallsPage.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Views\NavigationRoot.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<PackageReference Include="ANGLE.WindowsStore">
<Version>2.1.13</Version>
</PackageReference>
<PackageReference Include="LinphoneSDK">
<Version>5.1.0-alpha.56</Version>
</PackageReference>
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
<Version>6.2.11</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
<None Include="Readme.md" />
</ItemGroup>
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '14.0' ">
<VisualStudioVersion>14.0</VisualStudioVersion>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@@ -0,0 +1,4 @@
<Application
x:Class="_05_FileTransfer.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" />

View File

@@ -0,0 +1,129 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of Linphone TutorialCS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using _05_FileTransfer.Service;
using _05_FileTransfer.Views;
using Linphone;
using System;
using System.Diagnostics;
using System.Text;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
namespace _05_FileTransfer
{
/// <summary>
/// Provides application-specific behavior to supplement the default Application class.
/// </summary>
sealed partial class App : Application
{
private CoreService CoreService { get; } = CoreService.Instance;
/// <summary>
/// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain().
/// </summary>
public App()
{
this.InitializeComponent();
this.Suspending += OnSuspending;
}
/// <summary>
/// Invoked when the application is launched normally by the end user. Other entry points
/// will be used such as when the application is launched to open a specific file.
/// </summary>
/// <param name="e">Details about the launch request and process.</param>
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
Frame rootFrame = Window.Current.Content as Frame;
// Start Linphone
LoggingService.Instance.LogLevel = LogLevel.Debug;
LoggingService.Instance.Listener.OnLogMessageWritten = OnLog;
CoreService.CoreStart(Windows.ApplicationModel.Core.CoreApplication.GetCurrentView().CoreWindow.Dispatcher);
// Do not repeat app initialization when the Window already has content,
// just ensure that the window is active
if (rootFrame == null)
{
// Create a Frame to act as the navigation context and navigate to the first page
rootFrame = new Frame();
rootFrame.NavigationFailed += OnNavigationFailed;
if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
{
//TODO: Load state from previously suspended application
}
// Place the frame in the current Window
Window.Current.Content = rootFrame;
}
if (e.PrelaunchActivated == false)
{
if (rootFrame.Content == null)
{
// When the navigation stack isn't restored navigate to the first page,
// configuring the new page by passing required information as a navigation
// parameter
rootFrame.Navigate(typeof(LoginPage), e.Arguments);
}
// Ensure the current window is active
Window.Current.Activate();
}
}
private void OnLog(LoggingService logService, string domain, LogLevel lev, string message)
{
StringBuilder builder = new StringBuilder();
_ = builder.Append("Linphone-[").Append(lev.ToString()).Append("](").Append(domain).Append(")").Append(message);
Debug.WriteLine(builder.ToString());
}
/// <summary>
/// Invoked when Navigation to a certain page fails
/// </summary>
/// <param name="sender">The Frame which failed navigation</param>
/// <param name="e">Details about the navigation failure</param>
private void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
{
throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
}
/// <summary>
/// Invoked when application execution is being suspended. Application state is saved
/// without knowing whether the application will be terminated or resumed with the contents
/// of memory still intact.
/// </summary>
/// <param name="sender">The source of the suspend request.</param>
/// <param name="e">Details about the suspend request.</param>
private void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
//TODO: Save application state and stop any background activity
deferral.Complete();
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -0,0 +1,36 @@
<UserControl
x:Class="_05_FileTransfer.Controls.MessageDisplay"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Loaded="MessageDisplay_Loaded"
Unloaded="MessageDisplay_Unloaded">
<Border BorderBrush="{ThemeResource SystemAccentColorDark3}" BorderThickness="1" Padding="2" CornerRadius="20,20,20,20">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition MaxWidth="500" />
</Grid.ColumnDefinitions>
<TextBlock x:Name="ReceiveDate" Grid.Column="0" Text="" VerticalAlignment="Center" />
<StackPanel Orientation="Vertical" Grid.Column="1" Padding="10">
<StackPanel x:Name="TextStack" Orientation="Vertical" Visibility="Collapsed" Margin="0,0,0,10">
<TextBlock Text="Text :" />
<TextBlock x:Name="TextMessage" Text="" FontWeight="Bold" TextWrapping="Wrap" />
</StackPanel>
<StackPanel x:Name="FileStack" Orientation="Vertical" Visibility="Collapsed" Margin="0,0,0,10">
<TextBlock Text="File : " />
<TextBlock x:Name="FileName" Text="" FontWeight="Bold" />
<TextBlock x:Name="FileSize" Text="" />
<Button x:Name="Download" Content="Download" Click="Download_Click" Visibility="Collapsed" />
<Button x:Name="OpenFile" Content="Open file" Click="OpenFile_Click" Visibility="Collapsed" />
</StackPanel>
<TextBlock x:Name="MessageState" />
</StackPanel>
</Grid>
</Border>
</UserControl>

View File

@@ -0,0 +1,188 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of Linphone TutorialCS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using Linphone;
using System;
using System.IO;
using System.Linq;
using Windows.Storage;
using Windows.System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace _05_FileTransfer.Controls
{
public sealed partial class MessageDisplay : UserControl
{
private readonly ChatMessage ChatMessage;
private Content CurrentShownContent;
public MessageDisplay(ChatMessage message)
{
this.InitializeComponent();
// We link every MessageDisplay object to a ChatMessage.
ChatMessage = message;
UpdateLayoutFromMessage();
}
private void MessageDisplay_Loaded(object sender, RoutedEventArgs e)
{
// Like other Linphone objects you can register to a variety
// of callbacks on a ChatMessage object (see ChatMessage.Listener for the list).
// Here we want to be called when the message state is updated.
ChatMessage.Listener.OnMsgStateChanged += OnMessageStateChanged;
}
private void MessageDisplay_Unloaded(object sender, RoutedEventArgs e)
{
// Again, don't forget to unregister to avoid memory leak.
ChatMessage.Listener = null;
}
private void OnMessageStateChanged(ChatMessage message, ChatMessageState state)
{
// We display the message state. It can be really useful for the user
// to know if the remote received the message (state = Delivered) or
// if he read it (state = Displayed)
MessageState.Text = "The message state is : " + state;
switch (state)
{
// They're is multiple state during a file transfer (FileTransferInProgress,
// FileTransferDone, FileTransferError). We update the layout if the file
// is done downloading to replace the "Download" button by an "Open file" button.
case ChatMessageState.FileTransferDone:
UpdateLayoutFromMessage();
return;
}
}
private void UpdateLayoutFromMessage()
{
MessageState.Text = "The message state is : " + ChatMessage.State;
// You can find the sending date of a ChatMessage in ChatMessage.Time.
// The time number respect the time_t type specification.
ReceiveDate.Text = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(ChatMessage.Time).ToLocalTime().ToString("HH:mm");
if (ChatMessage.IsOutgoing)
{
this.HorizontalAlignment = HorizontalAlignment.Right;
}
else
{
this.HorizontalAlignment = HorizontalAlignment.Left;
}
// A ChatMessage hold a list of Content object, Contents.
// But in a basic ChatRoom, using a basic backend, by default the multipart
// is disable. So in any received message there is only one Content in the list.
// You can enable multipart on a ChatRoom object with ChatRoom.AllowMultipart() but it
// can be risky. In fact if your remote doesn't support multipart and you send him
// a multipart message it could not work properly.
if (ChatMessage.Contents.Any(c => c.IsFile))
{
// If the Content object isFile it means that it is an already
// downloaded file, so we display the OpenFile button. See
// this.OpenFile_Click to understand how to find the file from
// the Content object.
TextStack.Visibility = Visibility.Collapsed;
FileStack.Visibility = Visibility.Visible;
OpenFile.Visibility = Visibility.Visible;
Download.Visibility = Visibility.Collapsed;
// We can do this because we don't allowMultipart and can assume
// they're is only one element, and ChatMessage.Contents.Any(c => c.IsFile)
// returned true.
Content content = ChatMessage.Contents.First((c) => c.IsFile);
// Here we are displaying the name and the size of the file
FileName.Text = content.Name;
FileSize.Text = content.FileSize + " bits";
CurrentShownContent = content;
}
else if (ChatMessage.Contents.Any(c => c.IsFileTransfer))
{
// If the Content object IsFileTransfer it means that the file is
// not downloaded yet, so we display the Download button. See
// this.Download_Click to understand how to download the file from
// the Content object.
TextStack.Visibility = Visibility.Collapsed;
FileStack.Visibility = Visibility.Visible;
Download.Visibility = Visibility.Visible;
OpenFile.Visibility = Visibility.Collapsed;
Content content = ChatMessage.Contents.First((c) => c.IsFileTransfer);
FileName.Text = content.Name;
FileSize.Text = content.FileSize + " bits";
CurrentShownContent = content;
}
else if (ChatMessage.Contents.Any(c => c.IsText))
{
// If the content isText we only display the text value like before
TextStack.Visibility = Visibility.Visible;
FileStack.Visibility = Visibility.Collapsed;
Content content = ChatMessage.Contents.First((c) => c.IsText);
TextMessage.Text = content.Utf8Text;
CurrentShownContent = null;
}
}
/// <summary>
/// Method called when the "Download" button is clicked
/// </summary>
private void Download_Click(object sender, RoutedEventArgs e)
{
if (CurrentShownContent != null)
{
Download.Visibility = Visibility.Collapsed;
FileSize.Text = "Download in progress ...";
// We create a directory where we have write rights.
string downloadPathFolder = ApplicationData.Current.LocalFolder.Path + @"\Downloads\";
Directory.CreateDirectory(downloadPathFolder);
// We set the future file path before we start the download.
CurrentShownContent.FilePath = downloadPathFolder + CurrentShownContent.Name;
// And we use ChatMessage.DownloadContent(Content content) with
// our Content object as parameter. The download is async and
// you can follow the file transfer with OnFileTransferProgressIndicationDelegate
// or simply wait the FileTransferDone state on the ChatMessage like we are
// doing here.
ChatMessage.DownloadContent(CurrentShownContent);
}
}
private async void OpenFile_Click(object sender, RoutedEventArgs e)
{
// Just get the FilePath attribute from the Content object
string filePath = CurrentShownContent.FilePath;
// Only keep the folder part
string folderPath = filePath.Substring(0, filePath.LastIndexOf("\\"));
// And launch the Windows explorer
await Launcher.LaunchFolderAsync(await StorageFolder.GetFolderFromPathAsync(folderPath));
}
}
}

View File

@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:uap7="http://schemas.microsoft.com/appx/manifest/uap/windows10/7"
xmlns:uap8="http://schemas.microsoft.com/appx/manifest/uap/windows10/8"
IgnorableNamespaces="uap mp uap7 uap8">
<Identity
Name="754c9d43-0820-475a-b433-8a1be86b241c"
Publisher="CN=Anthony"
Version="1.0.0.0" />
<mp:PhoneIdentity PhoneProductId="754c9d43-0820-475a-b433-8a1be86b241c" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
<Properties>
<DisplayName>05_FileTransfer</DisplayName>
<PublisherDisplayName>Anthony</PublisherDisplayName>
<Logo>Assets\StoreLogo.png</Logo>
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.0.0" MaxVersionTested="10.0.0.0" />
</Dependencies>
<Resources>
<Resource Language="x-generate"/>
</Resources>
<Applications>
<Application Id="App"
Executable="$targetnametoken$.exe"
EntryPoint="_05_FileTransfer.App">
<uap:VisualElements
DisplayName="05_FileTransfer"
Square150x150Logo="Assets\Square150x150Logo.png"
Square44x44Logo="Assets\Square44x44Logo.png"
Description="05_FileTransfer"
BackgroundColor="transparent">
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png"/>
<uap:SplashScreen Image="Assets\SplashScreen.png" />
</uap:VisualElements>
<uap7:Properties>
<uap8:ActiveCodePage>UTF-8</uap8:ActiveCodePage>
</uap7:Properties>
</Application>
</Applications>
<Capabilities>
<Capability Name="internetClient" />
<uap:Capability Name="voipCall"/>
<Capability Name="codeGeneration"/>
<uap:Capability Name="picturesLibrary"/>
<uap:Capability Name="removableStorage"/>
<DeviceCapability Name="microphone"/>
<DeviceCapability Name="webcam"/>
</Capabilities>
</Package>

View File

@@ -0,0 +1,28 @@
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("05_FileTransfer")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("05_FileTransfer")]
[assembly: AssemblyCopyright("Copyright © 2020")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: ComVisible(false)]

View File

@@ -0,0 +1,28 @@
<!--
This file contains Runtime Directives used by .NET Native. The defaults here are suitable for most
developers. However, you can modify these parameters to modify the behavior of the .NET Native
optimizer.
Runtime Directives are documented at https://go.microsoft.com/fwlink/?LinkID=391919
To fully enable reflection for App1.MyClass and all of its public/private members
<Type Name="App1.MyClass" Dynamic="Required All" />
To enable dynamic creation of the specific instantiation of AppClass<T> over System.Int32
<TypeInstantiation Name="App1.AppClass" Arguments="System.Int32" Activate="Required Public" />
Using the Namespace directive to apply reflection policy to all the types in a particular namespace
<Namespace Name="DataClasses.ViewModels" Serialize="All" />
-->
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
<Application>
<!--
An Assembly element with Name="*Application*" applies to all assemblies in
the application package. The asterisks are not wildcards.
-->
<Assembly Name="*Application*" Dynamic="Required All" />
<!-- Add your application specific runtime directives here. -->
</Application>
</Directives>

View File

@@ -0,0 +1,33 @@
Linphone X UWP tutorial 05_FileTransfer
========================================
Learn how to send files over SIP using Linphone SDK.
We added a button to send file to your peer, and we improved how messages are displayed to show
you more information about them and allow you to download files sent by the remote end.
Most of the new Linphone usage are in Controls/MessageDisplay.xaml(.cs) and ChatPage.xaml(.cs) but don't
forget to set the attribute FileTransferServer on your Core ! (see Core creation in CoreService.cs)
Don't forget to install those NuGet packages :
- LinphoneSDK (can be found here : https://www.linphone.org/snapshots/windows/sdk/)
- Microsoft.NETCore.UniversalWindowsPlatform (version 6.2.12 recommended)
- ANGLE.WindowsStore (for video rendering, version 2.1.13 recommended)
New/updated files :
```
05_FileTransfer
└───Controls :
│ │ MessageDisplay.xaml(.cs) : A user control to display chat bubbles with more
│ │ information. Learn how to handle the different types of ChatMessage here.
│ │
└───Service :
│ │ CoreService.cs : A singleton service which contains the Linphone.Core.
│ │ We setup FileTransferServer during core creation now.
└───Views :
│ │
│ │ ChatPage.xaml(.cs) : This is the frame displayed when you select a chat room.
│ │ You can now send file and the message display is improved (see MessageDisplay)
```

View File

@@ -0,0 +1,275 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of Linphone TutorialCS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using Linphone;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Windows.Media.Audio;
using Windows.Media.Capture;
using Windows.Storage;
using Windows.UI.Core;
using static Linphone.CoreListener;
namespace _05_FileTransfer.Service
{
internal class CoreService
{
private Timer Timer;
private static readonly CoreService instance = new CoreService();
public static CoreService Instance
{
get
{
return instance;
}
}
private Core core;
public Core Core
{
get
{
if (core == null)
{
Factory factory = Factory.Instance;
string assetsPath = Path.Combine(Windows.ApplicationModel.Package.Current.InstalledLocation.Path, "share");
factory.TopResourcesDir = assetsPath;
factory.DataResourcesDir = assetsPath;
factory.SoundResourcesDir = Path.Combine(assetsPath, "sounds", "linphone");
factory.RingResourcesDir = Path.Combine(factory.SoundResourcesDir, "rings");
factory.ImageResourcesDir = Path.Combine(assetsPath, "images");
factory.MspluginsDir = ".";
core = factory.CreateCore("", "", IntPtr.Zero);
core.AudioPort = 7666;
core.VideoPort = 9666;
core.RootCa = Path.Combine(Windows.ApplicationModel.Package.Current.InstalledLocation.Path, "share", "Linphone", "rootca.pem");
core.UserCertificatesPath = ApplicationData.Current.LocalFolder.Path;
VideoActivationPolicy videoActivationPolicy = factory.CreateVideoActivationPolicy();
videoActivationPolicy.AutomaticallyAccept = true;
videoActivationPolicy.AutomaticallyInitiate = false;
core.VideoActivationPolicy = videoActivationPolicy;
if (core.VideoSupported())
{
core.VideoCaptureEnabled = true;
}
core.UsePreviewWindow(true);
// You must set up your file transfer server if you want to transfer files.
core.FileTransferServer = "https://www.linphone.org:444/lft.php";
}
return core;
}
}
public void CoreStart(CoreDispatcher dispatcher)
{
Core.Start();
Timer = new Timer(OnTimedEvent, dispatcher, 20, 20);
}
private async void OnTimedEvent(object state)
{
await ((CoreDispatcher)state).RunIdleAsync((args) =>
{
Core.Iterate();
});
}
public void AddOnAccountRegistrationStateChangedDelegate(OnAccountRegistrationStateChangedDelegate myDelegate)
{
Core.Listener.OnAccountRegistrationStateChanged += myDelegate;
}
public void RemoveOnAccountRegistrationStateChangedDelegate(OnAccountRegistrationStateChangedDelegate myDelegate)
{
Core.Listener.OnAccountRegistrationStateChanged -= myDelegate;
}
public void AddOnCallStateChangedDelegate(OnCallStateChangedDelegate myDelegate)
{
Core.Listener.OnCallStateChanged += myDelegate;
}
public void RemoveOnCallStateChangedDelegate(OnCallStateChangedDelegate myDelegate)
{
Core.Listener.OnCallStateChanged -= myDelegate;
}
public void AddOnOnMessageReceivedDelegate(OnMessageReceivedDelegate myDelegate)
{
Core.Listener.OnMessageReceived += myDelegate;
}
public void RemoveOnOnMessageReceivedDelegate(OnMessageReceivedDelegate myDelegate)
{
Core.Listener.OnMessageReceived -= myDelegate;
}
public void AddOnMessageSentDelegate(OnMessageSentDelegate myDelegate)
{
Core.Listener.OnMessageSent += myDelegate;
}
public void RemoveOnMessageSentDelegate(OnMessageSentDelegate myDelegate)
{
Core.Listener.OnMessageSent -= myDelegate;
}
public void LogIn(string identity, string password)
{
Address address = Factory.Instance.CreateAddress(identity);
AuthInfo authInfo = Factory.Instance.CreateAuthInfo(address.Username, "", password, "", "", address.Domain);
Core.AddAuthInfo(authInfo);
AccountParams accountParams = Core.CreateAccountParams();
accountParams.IdentityAddress = address;
string serverAddr = "sip:" + address.Domain + ";transport=tls";
accountParams.ServerAddr = serverAddr;
accountParams.RegisterEnabled = true;
Account account = Core.CreateAccount(accountParams);
Core.AddAccount(account);
Core.DefaultAccount = account;
}
public void LogOut()
{
Account account = Core.DefaultAccount;
if (account != null)
{
AccountParams accountParams = account.Params.Clone();
accountParams.RegisterEnabled = false;
account.Params = accountParams;
}
}
public void ClearCoreAfterLogOut()
{
Core.ClearAllAuthInfo();
Core.ClearAccounts();
}
public async void Call(string uriToCall)
{
await OpenMicrophonePopup();
Address address = Core.InterpretUrl(uriToCall);
Core.InviteAddress(address);
}
public bool MicEnabledSwitch()
{
return Core.MicEnabled = !Core.MicEnabled;
}
public bool SpeakerMutedSwitch()
{
return Core.CurrentCall.SpeakerMuted = !Core.CurrentCall.SpeakerMuted;
}
public async Task<bool> CameraEnabledSwitchAsync()
{
await OpenCameraPopup();
Call call = Core.CurrentCall;
CallParams param = core.CreateCallParams(call);
bool newValue = !param.VideoEnabled;
param.VideoEnabled = newValue;
call.Update(param);
return newValue;
}
public ChatRoom CreateOrGetChatRoom(string sipAddress)
{
Address remoteAddress = Core.InterpretUrl(sipAddress);
Address localAdress = Core.DefaultProxyConfig.IdentityAddress;
ChatRoomParams chatRoomParams = Core.CreateDefaultChatRoomParams();
chatRoomParams.Backend = ChatRoomBackend.Basic;
chatRoomParams.EncryptionBackend = ChatRoomEncryptionBackend.None;
chatRoomParams.EncryptionEnabled = false;
chatRoomParams.GroupEnabled = false;
chatRoomParams.RttEnabled = false;
return Core.CreateChatRoom(chatRoomParams, localAdress, new[] { remoteAddress });
}
public async Task<Content> CreateContentFromFile(StorageFile file)
{
// Copy the file where the LinphoneSDK has read access.
StorageFile fileCopy = await file.CopyAsync(ApplicationData.Current.LocalFolder, file.Name, NameCollisionOption.ReplaceExisting);
// Always use Linphone's method and not new() to create Linphone objects.
Content content = Core.CreateContent();
// File Path is the only mandatory field to set.
content.FilePath = fileCopy.Path;
// You can set the type and subtype of your file, it help
// the server and receiver identifying the file (images can
// be directly displayed for example).
string[] splittedMimeType = fileCopy.ContentType.Split("/");
content.Type = splittedMimeType[0];
content.Subtype = splittedMimeType[1];
// Set the file name for the receiver, by default the same name is taken.
// This line is useful only for the explanation.
content.Name = fileCopy.Name;
return content;
}
private async Task OpenMicrophonePopup()
{
AudioGraphSettings settings = new AudioGraphSettings(Windows.Media.Render.AudioRenderCategory.Media);
CreateAudioGraphResult result = await AudioGraph.CreateAsync(settings);
AudioGraph audioGraph = result.Graph;
CreateAudioDeviceInputNodeResult resultNode = await audioGraph.CreateDeviceInputNodeAsync(Windows.Media.Capture.MediaCategory.Media);
AudioDeviceInputNode deviceInputNode = resultNode.DeviceInputNode;
deviceInputNode.Dispose();
audioGraph.Dispose();
}
private async Task OpenCameraPopup()
{
MediaCapture mediaCapture = new Windows.Media.Capture.MediaCapture();
await mediaCapture.InitializeAsync(new MediaCaptureInitializationSettings
{
StreamingCaptureMode = StreamingCaptureMode.Video
});
mediaCapture.Dispose();
}
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of Linphone TutorialCS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using _05_FileTransfer.Views;
namespace _05_FileTransfer.Service
{
internal class NavigationService
{
private static readonly NavigationService instance = new NavigationService();
public static NavigationService Instance
{
get
{
return instance;
}
}
public NavigationRoot CurrentNavigationRoot { get; set; }
public ChatsPage CurrentChatspage { get; set; }
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of Linphone TutorialCS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using Windows.UI.Xaml.Controls;
namespace _05_FileTransfer.Service
{
internal class VideoService
{
private static readonly VideoService instance = new VideoService();
public static VideoService Instance
{
get
{
return instance;
}
}
private CoreService CoreService { get; } = CoreService.Instance;
public void StartVideoStream(SwapChainPanel main, SwapChainPanel preview)
{
CoreService.Core.NativePreviewWindowId = preview;
CoreService.Core.NativeVideoWindowId = main;
}
public void StopVideoStream()
{
CoreService.Core.NativePreviewWindowId = null;
CoreService.Core.NativeVideoWindowId = null;
}
}
}

View File

@@ -0,0 +1,72 @@
<Page
x:Class="_05_FileTransfer.Views.CallsPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border Grid.Row="0" Background="{ThemeResource SystemAccentColorLight3}" Padding="10">
<TextBlock x:Name="HelloText" HorizontalAlignment="Center" VerticalAlignment="Center" Style="{ThemeResource HeaderTextBlockStyle}" Text="Hello " />
</Border>
<Grid Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="*" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" VerticalAlignment="Center" Margin="20">
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Text="URI to call : " />
<TextBox x:Name="UriToCall" Width="350" MinWidth="350" MaxWidth="350" Text="sip:" />
</StackPanel>
<Button x:Name="CallButton" Content="Call" Click="CallClick" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="5" />
<TextBlock x:Name="CallText" Text="Your call state is : Idle" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="10" />
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="10">
<Button x:Name="HangOut" Content="Hang out" Click="HangOutClick" IsEnabled="False" />
<Button x:Name="Sound" Content="Switch off Sound" Click="SoundClick" IsEnabled="False" />
<Button x:Name="Camera" Content="Switch on Camera" Click="CameraClick" IsEnabled="False" />
<Button x:Name="Mic" Content="Mute" Click="MicClick" IsEnabled="False" />
</StackPanel>
</StackPanel>
<Grid Grid.Row="1" x:Name="VideoGrid" Canvas.ZIndex="-1" Background="Black" Visibility="Collapsed">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<SwapChainPanel x:Name="VideoSwapChainPanel" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" Grid.RowSpan="3" />
<SwapChainPanel x:Name="PreviewSwapChainPanel" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Column="2" Grid.Row="0" Grid.RowSpan="3">
</SwapChainPanel>
</Grid>
<StackPanel Grid.Row="2" x:Name="IncomingCallStackPanel" Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="Collapsed" Margin="10">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Text="You have a call from :" />
<TextBlock x:Name="IncommingCallText" Text="" />
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<Button x:Name="Answer" Content="Answer" Click="AnswerClick" />
<Button x:Name="Decline" Content="Decline" Click="DeclineClick" />
</StackPanel>
</StackPanel>
</Grid>
</Grid>
</Page>

View File

@@ -0,0 +1,201 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of Linphone TutorialCS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using _05_FileTransfer.Service;
using Linphone;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
namespace _05_FileTransfer.Views
{
public sealed partial class CallsPage : Page
{
private CoreService CoreService { get; } = CoreService.Instance;
private VideoService VideoService { get; } = VideoService.Instance;
private Call IncommingCall;
public CallsPage()
{
this.InitializeComponent();
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
VideoService.StopVideoStream();
CoreService.RemoveOnCallStateChangedDelegate(OnCallStateChanged);
base.OnNavigatedFrom(e);
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
HelloText.Text += CoreService.Core.DefaultProxyConfig.FindAuthInfo().Username;
CoreService.AddOnCallStateChangedDelegate(OnCallStateChanged);
if (CoreService.Core.CurrentCall != null)
{
OnCallStateChanged(CoreService.Core, CoreService.Core.CurrentCall, CoreService.Core.CurrentCall.State, null);
}
}
private void CallClick(object sender, RoutedEventArgs e)
{
CoreService.Call(UriToCall.Text);
}
private void HangOutClick(object sender, RoutedEventArgs e)
{
CoreService.Core.TerminateAllCalls();
}
private void SoundClick(object sender, RoutedEventArgs e)
{
if (CoreService.SpeakerMutedSwitch())
{
Sound.Content = "Switch on Sound";
}
else
{
Sound.Content = "Switch off Sound";
}
}
private async void CameraClick(object sender, RoutedEventArgs e)
{
await CoreService.CameraEnabledSwitchAsync();
Camera.Content = "Waiting for accept ...";
Camera.IsEnabled = false;
}
private void MicClick(object sender, RoutedEventArgs e)
{
if (CoreService.MicEnabledSwitch())
{
Mic.Content = "Mute";
}
else
{
Mic.Content = "Unmute";
}
}
private void AnswerClick(object sender, RoutedEventArgs e)
{
IncommingCall.Accept();
IncommingCall = null;
}
private void DeclineClick(object sender, RoutedEventArgs e)
{
if (IncommingCall != null)
{
IncommingCall.Decline(Reason.Declined);
IncommingCall = null;
}
}
private void OnCallStateChanged(Core core, Call call, CallState state, string message)
{
CallText.Text = "Your call state is : " + state.ToString();
switch (state)
{
case CallState.IncomingReceived:
IncommingCall = call;
IncomingCallStackPanel.Visibility = Visibility.Visible;
IncommingCallText.Text = " " + call.RemoteAddress.AsString();
break;
case CallState.OutgoingInit:
case CallState.OutgoingProgress:
case CallState.OutgoingRinging:
HangOut.IsEnabled = true;
break;
case CallState.StreamsRunning:
case CallState.UpdatedByRemote:
CallInProgressGuiUpdates();
if (call.CurrentParams.VideoEnabled)
{
StartVideoAndUpdateGui();
}
else
{
StopVideoAndUpdateGui();
}
break;
case CallState.Error:
case CallState.End:
case CallState.Released:
IncommingCall = null;
EndingCallGuiUpdates();
VideoService.StopVideoStream();
break;
}
}
private void StopVideoAndUpdateGui()
{
Camera.Content = "Switch on Camera";
Camera.IsEnabled = true;
VideoGrid.Visibility = Visibility.Collapsed;
VideoService.StopVideoStream();
}
private void StartVideoAndUpdateGui()
{
VideoGrid.Visibility = Visibility.Visible;
Camera.Content = "Switch off Camera";
VideoService.StartVideoStream(VideoSwapChainPanel, PreviewSwapChainPanel);
Camera.IsEnabled = true;
}
private void EndingCallGuiUpdates()
{
IncomingCallStackPanel.Visibility = Visibility.Collapsed;
CallButton.IsEnabled = true;
HangOut.IsEnabled = false;
Sound.IsEnabled = false;
Camera.IsEnabled = false;
Mic.IsEnabled = false;
VideoGrid.Visibility = Visibility.Collapsed;
Camera.Content = "Switch on Camera";
Mic.Content = "Mute";
Sound.Content = "Switch off Sound";
}
private void CallInProgressGuiUpdates()
{
IncomingCallStackPanel.Visibility = Visibility.Collapsed;
CallButton.IsEnabled = false;
HangOut.IsEnabled = true;
Sound.IsEnabled = true;
Camera.IsEnabled = true;
Mic.IsEnabled = true;
}
}
}

View File

@@ -0,0 +1,48 @@
<Page
x:Class="_05_FileTransfer.Views.ChatPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="0.1*" />
<RowDefinition Height="auto" />
<RowDefinition Height="0.75*" />
<RowDefinition Height="0.15*" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0">
<TextBlock x:Name="ChatHeaderText" Text="Your conversation with : " Style="{ThemeResource HeaderTextBlockStyle}" />
</StackPanel>
<Grid Grid.Row="1" Padding="10,0,10,0">
<TextBlock x:Name="PeerUsername" Text="Peer user-name : " />
<TextBlock x:Name="YourUsername" Text="Your user-name : " HorizontalAlignment="Right" />
</Grid>
<ScrollViewer Grid.Row="2" x:Name="MessagesScroll">
<StackPanel Padding="10"
x:Name="MessagesList"
Orientation="Vertical"
VerticalAlignment="Bottom">
</StackPanel>
</ScrollViewer>
<Grid Grid.Row="3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.80*" />
<ColumnDefinition Width="0.20*" />
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0" x:Name="OutgoingMessageText" Text="" AcceptsReturn="True" TextWrapping="Wrap" />
<Grid Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button Grid.Row="0" x:Name="OutgoingMessageButton" Click="OutgoingMessageButton_Click" Content="Send" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="1" />
<Button Grid.Row="1" x:Name="SendFileButton" Click="SendFileButton_Click" Content="Send a file" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="1" />
</Grid>
</Grid>
</Grid>
</Page>

View File

@@ -0,0 +1,137 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of Linphone TutorialCS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using _05_FileTransfer.Controls;
using _05_FileTransfer.Service;
using Linphone;
using System;
using Windows.Storage;
using Windows.Storage.Pickers;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
namespace _05_FileTransfer.Views
{
public sealed partial class ChatPage : Page
{
private NavigationService NavigationService { get; } = NavigationService.Instance;
private CoreService CoreService { get; } = CoreService.Instance;
private ChatRoom ChatRoom;
public ChatPage()
{
this.InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
ChatRoom = ((ChatRoom)e.Parameter);
ChatHeaderText.Text += ChatRoom.PeerAddress.Username;
ChatRoom.Listener.OnMessageReceived += OnMessageReceived;
foreach (ChatMessage chatMessage in ChatRoom.GetHistory(0))
{
AddMessage(chatMessage);
}
ChatRoom.MarkAsRead();
NavigationService.CurrentNavigationRoot.UpdateUnreadMessageCount();
NavigationService.CurrentChatspage.UpdateChatRooms();
PeerUsername.Text += ChatRoom.PeerAddress.Username;
YourUsername.Text += ChatRoom.LocalAddress.Username;
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
ChatRoom.Listener.OnMessageReceived -= OnMessageReceived;
}
private void OnMessageReceived(ChatRoom chatRoom, ChatMessage message)
{
if (ChatRoom != null)
{
AddMessage(message);
chatRoom.MarkAsRead();
}
}
private void AddMessage(ChatMessage chatMessage)
{
// Instead of simply display a TextBlock we now create a
// MessageDisplay object to show more informations about the message.
// See Controls/MessageDisplay.xaml(.cs)
MessageDisplay messageDisplay = new MessageDisplay(chatMessage);
MessagesList.Children.Add(messageDisplay);
ScrollToBottom();
}
private void ScrollToBottom()
{
MessagesScroll.UpdateLayout();
MessagesScroll.ChangeView(1, MessagesScroll.ExtentHeight, 1);
}
private void OutgoingMessageButton_Click(object sender, RoutedEventArgs e)
{
if (ChatRoom != null && OutgoingMessageText.Text != null && OutgoingMessageText.Text.Length > 0)
{
ChatMessage chatMessage = ChatRoom.CreateMessage(OutgoingMessageText.Text);
chatMessage.Send();
AddMessage(chatMessage);
}
OutgoingMessageText.Text = "";
}
/// <summary>
/// Method called when the "Send file" button is clicked
/// </summary>
private async void SendFileButton_Click(object sender, RoutedEventArgs e)
{
// Basic Windows code to let the user select a file and gain
// read access to a StorageFile object.
FileOpenPicker picker = new FileOpenPicker();
picker.ViewMode = PickerViewMode.List;
picker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
picker.FileTypeFilter.Add("*");
StorageFile file = await picker.PickSingleFileAsync();
if (file != null)
{
// We create a Linphone.Content object from the StorageFile object
// see CoreService.CreateContentFromFile(StorageFile file)
Content content = await CoreService.CreateContentFromFile(file);
// To create a text ChatMessage for a chat room we use ChatRoom.CreateMessage(string message);
// Here we want to create a file transfer message so we must use
// ChatRoom.CreateFileTransferMessage(Content initialContent) to create it.
ChatMessage fileMessage = ChatRoom.CreateFileTransferMessage(content);
// Then simply call ChatMessage.Send() to send the message to the remote.
fileMessage.Send();
AddMessage(fileMessage);
}
}
}
}

View File

@@ -0,0 +1,39 @@
<Page
x:Class="_05_FileTransfer.Views.ChatsPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:linphone="using:Linphone"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.3*" />
<ColumnDefinition Width="0.7*" />
</Grid.ColumnDefinitions>
<Border BorderBrush="{ThemeResource SystemAccentColorLight3}"
BorderThickness="2,2,1,2"
Padding="2">
<StackPanel Grid.Column="0" Orientation="Vertical">
<Button x:Name="NewChatRoom" Click="NewChatRoom_Click" Content="Create a new ChatRoom" HorizontalAlignment="Stretch" />
<ListView x:Name="ChatRoomsLV" SelectionMode="Single" IsItemClickEnabled="True" ItemClick="ChatRoomsLV_ItemClick">
<ListView.ItemTemplate>
<DataTemplate x:DataType="linphone:ChatRoom">
<StackPanel Orientation="Horizontal">
<Border BorderThickness="1" BorderBrush="{ThemeResource SystemAccentColorLight3}" CornerRadius="10" Margin="0,0,5,0" Padding="3,0,3,0">
<TextBlock Text="{x:Bind UnreadMessagesCount}" FontSize="14" />
</Border>
<TextBlock Text="{x:Bind PeerAddress.AsString()}" FontSize="14" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</Border>
<Border BorderBrush="{ThemeResource SystemAccentColorLight3}"
BorderThickness="1,2,2,2"
Padding="2"
Grid.Column="1">
<Frame x:Name="ChatRoomFrame" />
</Border>
</Grid>
</Page>

View File

@@ -0,0 +1,128 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of Linphone TutorialCS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using _05_FileTransfer.Service;
using Linphone;
using System;
using System.Threading.Tasks;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
namespace _05_FileTransfer.Views
{
public sealed partial class ChatsPage : Page
{
private CoreService CoreService { get; } = CoreService.Instance;
private NavigationService NavigationService { get; } = NavigationService.Instance;
public ChatsPage()
{
this.InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
NavigationService.CurrentChatspage = this;
UpdateChatRooms();
CoreService.AddOnOnMessageReceivedDelegate(OnMessageReceiveOrSent);
CoreService.AddOnMessageSentDelegate(OnMessageReceiveOrSent);
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
NavigationService.CurrentChatspage = null;
CoreService.RemoveOnOnMessageReceivedDelegate(OnMessageReceiveOrSent);
CoreService.RemoveOnMessageSentDelegate(OnMessageReceiveOrSent);
base.OnNavigatedFrom(e);
}
private void OnMessageReceiveOrSent(Core core, ChatRoom chatRoom, ChatMessage message) => UpdateChatRooms();
public void UpdateChatRooms()
{
ChatRoom selectedChatRoom = (ChatRoom)ChatRoomsLV.SelectedItem;
ChatRoomsLV.Items.Clear();
foreach (ChatRoom chatRoom in CoreService.Core.ChatRooms)
{
if (chatRoom.HistorySize > 0)
{
ChatRoomsLV.Items.Add(chatRoom);
if (selectedChatRoom == chatRoom)
{
ChatRoomsLV.SelectedItem = chatRoom;
}
}
}
}
private void ChatRoomsLV_ItemClick(object sender, ItemClickEventArgs e)
{
ChatRoomsLV.SelectedItem = e.ClickedItem;
ChatRoomFrame.Navigate(typeof(ChatPage), e.ClickedItem);
}
private async void NewChatRoom_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
string peerSipAddress = await InputTextDialogAsync("Enter peer sip address");
if (!String.IsNullOrWhiteSpace(peerSipAddress))
{
ChatRoom newChatRoom = CoreService.CreateOrGetChatRoom(peerSipAddress);
if (newChatRoom != null)
{
ChatRoomFrame.Navigate(typeof(ChatPage), newChatRoom);
}
else
{
ContentDialog noSettingsDialog = new ContentDialog
{
Title = "ChatRoom creation error",
Content = "An error occurred during ChatRoom creation, check sip address validity and try again.",
CloseButtonText = "OK"
};
await noSettingsDialog.ShowAsync();
}
}
}
private async Task<string> InputTextDialogAsync(string title)
{
TextBox inputTextBox = new TextBox
{
AcceptsReturn = false,
Height = 32
};
ContentDialog dialog = new ContentDialog
{
Content = inputTextBox,
Title = title,
IsSecondaryButtonEnabled = true,
PrimaryButtonText = "OK",
SecondaryButtonText = "Cancel"
};
if (await dialog.ShowAsync() == ContentDialogResult.Primary)
return inputTextBox.Text;
else
return "";
}
}
}

View File

@@ -0,0 +1,34 @@
<Page
x:Class="_05_FileTransfer.Views.LoginPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid KeyUp="GridKeyUp">
<Grid.RowDefinitions>
<RowDefinition Height="0.15*" />
<RowDefinition Height="0.85*" />
</Grid.RowDefinitions>
<Border Grid.Row="0" Background="{ThemeResource SystemAccentColorLight3}">
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Style="{ThemeResource HeaderTextBlockStyle}" Text="Login Form" />
</Border>
<StackPanel Grid.Row="1" VerticalAlignment="Center">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Orientation="Vertical">
<TextBlock Text="Identity :" />
<TextBox x:Name="Identity" Width="350" MinWidth="350" MaxWidth="350" Text="sip:" />
</StackPanel>
<StackPanel Margin="0,10,0,0" HorizontalAlignment="Center" VerticalAlignment="Center" Orientation="Vertical">
<TextBlock Text="Password :" />
<PasswordBox x:Name="Password" Width="350" MinWidth="350" MaxWidth="350" PlaceholderText="myPasswd" />
</StackPanel>
<Button x:Name="LogIn" Click="LogInClick" Content="Login" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,10,0,0" />
<TextBlock x:Name="RegistrationText" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,10,0,0" />
</StackPanel>
</Grid>
</Page>

View File

@@ -0,0 +1,112 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of Linphone TutorialCS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using _05_FileTransfer.Service;
using Linphone;
using Windows.System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Navigation;
namespace _05_FileTransfer.Views
{
/// <summary>
/// A really simple app for a first Login with LinphoneSDK x UWP
/// </summary>
public sealed partial class LoginPage : Page
{
private CoreService CoreService { get; } = CoreService.Instance;
public LoginPage()
{
this.InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
CoreService.AddOnAccountRegistrationStateChangedDelegate(OnAccountRegistrationStateChanged);
}
protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
CoreService.RemoveOnAccountRegistrationStateChangedDelegate(OnAccountRegistrationStateChanged);
base.OnNavigatingFrom(e);
}
/// <summary>
/// Called when you click on the "Login" button.
/// </summary>
private void LogInClick(object sender, RoutedEventArgs e)
{
if (LogIn.IsEnabled)
{
LogIn.IsEnabled = false;
CoreService.LogIn(Identity.Text, Password.Password);
}
}
/// <summary>
/// Called when a key is pressed and released on the login page.
/// If you pressed "Enter", simulate a login click.
/// </summary>
private void GridKeyUp(object sender, KeyRoutedEventArgs e)
{
if (VirtualKey.Enter.Equals(e.Key))
{
LogInClick(null, null);
}
}
/// <summary>
/// This method is called every time the RegistrationState is updated by background core's actions.
/// In this example we use this to update the GUI.
/// </summary>
private void OnAccountRegistrationStateChanged(Core core, Account account, RegistrationState state, string message)
{
RegistrationText.Text = "Your registration state is : " + state.ToString();
switch (state)
{
case RegistrationState.Cleared:
case RegistrationState.None:
CoreService.ClearCoreAfterLogOut();
LogIn.IsEnabled = true;
break;
case RegistrationState.Ok:
LogIn.IsEnabled = false;
this.Frame.Navigate(typeof(NavigationRoot));
break;
case RegistrationState.Progress:
LogIn.IsEnabled = false;
break;
case RegistrationState.Failed:
LogIn.IsEnabled = true;
break;
default:
break;
}
}
}
}

View File

@@ -0,0 +1,43 @@
<Page
x:Class="_05_FileTransfer.Views.NavigationRoot"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Loaded="Page_Loaded">
<Grid x:Name="NavRootGrid" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<NavigationView
x:Name="navview"
AlwaysShowHeader="False"
ItemInvoked="Navview_ItemInvoked"
PaneDisplayMode="Top"
IsBackButtonVisible="Collapsed">
<NavigationView.MenuItems>
<NavigationViewItem Content="Calls" IsSelected="True">
<NavigationViewItem.Icon>
<FontIcon FontFamily="Segoe MDL2 Assets" Glyph="&#xF715;" />
</NavigationViewItem.Icon>
</NavigationViewItem>
<NavigationViewItem>
<NavigationViewItem.Icon>
<FontIcon FontFamily="Segoe MDL2 Assets" Glyph="&#xE8F2;" />
</NavigationViewItem.Icon>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Chats" />
<Border x:Name="NewMessageCountBorder" BorderThickness="1" BorderBrush="Red" CornerRadius="10" Margin="5,0,0,0" Padding="3,0,3,0">
<TextBlock x:Name="NewMessageCount" Text="0" />
</Border>
</StackPanel>
</NavigationViewItem>
</NavigationView.MenuItems>
<NavigationView.PaneFooter>
<NavigationViewItem Content="Sign out" Tapped="SignOut_Tapped">
<NavigationViewItem.Icon>
<FontIcon FontFamily="Segoe MDL2 Assets" Glyph="&#xF3B1;" />
</NavigationViewItem.Icon>
</NavigationViewItem>
</NavigationView.PaneFooter>
<Frame x:Name="AppNavFrame" Navigated="AppNavFrame_Navigated" />
</NavigationView>
</Grid>
</Page>

View File

@@ -0,0 +1,154 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of Linphone TutorialCS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using _05_FileTransfer.Service;
using Linphone;
using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Navigation;
namespace _05_FileTransfer.Views
{
/// <summary>
/// A really simple app for a first Login with LinphoneSDK x UWP
/// </summary>
public sealed partial class NavigationRoot : Page
{
private CoreService CoreService { get; } = CoreService.Instance;
private NavigationService NavigationService { get; } = NavigationService.Instance;
private bool hasLoadedPreviously;
public NavigationRoot()
{
this.InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
this.CoreService.AddOnOnMessageReceivedDelegate(OnMessageReveive);
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
this.CoreService.RemoveOnOnMessageReceivedDelegate(OnMessageReveive);
base.OnNavigatedFrom(e);
}
private void Page_Loaded(object sender, RoutedEventArgs e)
{
// Only do an inital navigate the first time the page loads
// when we switch out of compactoverloadmode this will fire but we don't want to navigate because
// there is already a page loaded
if (!hasLoadedPreviously)
{
AppNavFrame.Navigate(typeof(CallsPage));
UpdateUnreadMessageCount();
hasLoadedPreviously = true;
NavigationService.CurrentNavigationRoot = this;
}
}
private void AppNavFrame_Navigated(object sender, NavigationEventArgs e)
{
switch (e.SourcePageType)
{
case Type c when e.SourcePageType == typeof(CallsPage):
((NavigationViewItem)navview.MenuItems[0]).IsSelected = true;
break;
case Type c when e.SourcePageType == typeof(ChatsPage):
((NavigationViewItem)navview.MenuItems[1]).IsSelected = true;
break;
}
}
private async void Navview_ItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args)
{
if (args.IsSettingsInvoked)
{
ContentDialog noSettingsDialog = new ContentDialog
{
Title = "No settings",
Content = "There is no settings in this little app",
CloseButtonText = "OK"
};
ContentDialogResult result = await noSettingsDialog.ShowAsync();
return;
}
string invokedItemValue = args.InvokedItem as string;
if (invokedItemValue != null && invokedItemValue.Contains("Calls"))
{
AppNavFrame.Navigate(typeof(CallsPage));
}
else
{
AppNavFrame.Navigate(typeof(ChatsPage));
}
}
private void SignOut_Tapped(object sender, TappedRoutedEventArgs e)
{
DisplaySignOutDialog();
}
private async void DisplaySignOutDialog()
{
ContentDialog signOutDialog = new ContentDialog
{
Title = "Sign out ?",
Content = "All your current calls and actions will be canceled, are you sure to continue ?",
PrimaryButtonText = "Sign out",
CloseButtonText = "Cancel"
};
ContentDialogResult result = await signOutDialog.ShowAsync();
if (result == ContentDialogResult.Primary)
{
CoreService.Core.TerminateAllCalls();
CoreService.LogOut();
this.Frame.Navigate(typeof(LoginPage));
}
}
private void OnMessageReveive(Core core, ChatRoom chatRoom, ChatMessage message)
{
UpdateUnreadMessageCount();
}
public void UpdateUnreadMessageCount()
{
if (CoreService.Core.UnreadChatMessageCountFromActiveLocals > 0)
{
NewMessageCount.Text = "" + CoreService.Core.UnreadChatMessageCountFromActiveLocals;
NewMessageCountBorder.Visibility = Visibility.Visible;
}
else
{
NewMessageCountBorder.Visibility = Visibility.Collapsed;
}
}
}
}