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,208 @@
<?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>{22034C73-FF8C-4D02-874D-15FED72C1A1D}</ProjectGuid>
<OutputType>AppContainerExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>_04_BasicChat</RootNamespace>
<AssemblyName>04_BasicChat</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="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="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="_04_BasicChat.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 _04_BasicChat.Service;
using _04_BasicChat.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 _04_BasicChat
{
/// <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,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>04_BasicChat</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="_04_BasicChat.App">
<uap:VisualElements
DisplayName="04_BasicChat"
Square150x150Logo="Assets\Square150x150Logo.png"
Square44x44Logo="Assets\Square44x44Logo.png"
Description="04_BasicChat"
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("04_BasicChat")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("04_BasicChat")]
[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,35 @@
Linphone X UWP tutorial 04_BasicChat
========================================
Second big step in this tutorial, we can now communicate in basic chat rooms.
In this part you are going to learn how to send and receive text messages over SIP using LinphoneSDK.
For our first step with ChatRoom we are going to create only one to one basic ChatRoom (no encryption, no ephemeral),
and for now the tutorial app will only support text message.
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 :
```
04_BasicChat
└───Service :
│ │ CoreService.cs : A singleton service which contains the Linphone.Core.
│ │ We added some code to create new chat rooms here.
│ │
│ │ NavigationService.cs : A small service used to keeps reference to current pages displayed.
└───Views :
│ │
│ │ ChatPage.xaml(.cs) : This is the frame displayed when you select a chat room.
│ │ For now it's a simple page where you can send message and see your
│ │ conversation history.
│ │
│ │ ChatsPage.xaml(.cs) : In this page we list all the existing chat rooms. If you select
│ │ one of them a ChatPage is render. You can also create new ChatRoom here.
│ │
│ │ NavigationRoot.xaml(.cs) : The navigation page, you can now navigate the ChatsPage !
```

View File

@@ -0,0 +1,282 @@
/*
* 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 _04_BasicChat.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.VideoDisplayFilter = "MSOGL";
core.VideoCaptureEnabled = true;
}
core.UsePreviewWindow(true);
}
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;
}
/// <summary>
/// Used to add a delegate for the OnMessageReceived callback this callback is triggered every time
/// a message is received in ANY ChatRoom.
/// </summary>
public void AddOnOnMessageReceivedDelegate(OnMessageReceivedDelegate myDelegate)
{
Core.Listener.OnMessageReceived += myDelegate;
}
public void RemoveOnOnMessageReceivedDelegate(OnMessageReceivedDelegate myDelegate)
{
Core.Listener.OnMessageReceived -= myDelegate;
}
/// <summary>
/// Used to add a delegate for the OnMessageReceived callback this callback is triggered every time
/// a message is sent in ANY ChatRoom.
/// </summary>
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;
}
/// <summary>
/// Method to create a one to one basic ChatRoom from a string sip address.
/// </summary>
public ChatRoom CreateOrGetChatRoom(string sipAddress)
{
// Construct an Address object from the string parameter if possible.
Address remoteAddress = Core.InterpretUrl(sipAddress);
// We get our current local Address.
Address localAdress = Core.DefaultProxyConfig.IdentityAddress;
// You need to create a ChatRoomParams object to configure
// your future ChatRoom, always use Core.CreateDefaultChatRoomParams().
// By default the default parameters are the ones we set just after, but we
// set them anyway to explain them.
ChatRoomParams chatRoomParams = Core.CreateDefaultChatRoomParams();
// Set the type of SIP server you want to use, if you are
// using a basic SIP backend you can't enable encryption or
// group chat.
chatRoomParams.Backend = ChatRoomBackend.Basic;
// Choose if you want to enable real time text.
chatRoomParams.RttEnabled = false;
// You can choose to enable encryption and choose the type of encryption
// want. We explain in a further step how to use LIME (Linphone
// Instant Message Encryption) with FlexisipChat backend.
chatRoomParams.EncryptionEnabled = false;
chatRoomParams.EncryptionBackend = ChatRoomEncryptionBackend.None;
// Enable this if you want to create a group ChatRoom. We explain in
// a further step how to handle group chat with FlexisipChat backend.
chatRoomParams.GroupEnabled = false;
// To create a ChatRoom always use :
// Core.CreateChatRoom(ChatRoomParams parameters, Address localAddr, IEnumerable<Address> participants);
// If all the parameters match an existing ChatRoom of yours,
// it is returned instead of creating a new one.
return Core.CreateChatRoom(chatRoomParams, localAdress, new[] { remoteAddress });
}
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 _04_BasicChat.Views;
namespace _04_BasicChat.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 _04_BasicChat.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="_04_BasicChat.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 _04_BasicChat.Service;
using Linphone;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
namespace _04_BasicChat.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,41 @@
<Page
x:Class="_04_BasicChat.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" />
<Button Grid.Column="1" x:Name="OutgoingMessageButton" Click="OutgoingMessageButton_Click" Content="Send" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</Grid>
</Grid>
</Page>

View File

@@ -0,0 +1,151 @@
/*
* 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 _04_BasicChat.Service;
using Linphone;
using System.Linq;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
namespace _04_BasicChat.Views
{
public sealed partial class ChatPage : Page
{
private NavigationService NavigationService { get; } = NavigationService.Instance;
private ChatRoom ChatRoom;
public ChatPage()
{
this.InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
ChatRoom = ((ChatRoom)e.Parameter);
// The ChatRoom also offers to register to some callbacks.
// One of them is OnMessageReceived, like the one we used
// on the core (Core.Listener.OnMessageReceived) but this one
// is triggered only when the message received is part of this
// ChatRoom.
ChatRoom.Listener.OnMessageReceived += OnMessageReceived;
// The method GetHistory get all the ChatMessage you have
// in your local database for this ChatRoom. GetHistory(0)
// means everything but you can specify a max number of messages.
foreach (ChatMessage chatMessage in ChatRoom.GetHistory(0))
{
// See AddMessage(ChatMessage chatMessage) to see how we display messages
AddMessage(chatMessage);
}
// Mark all the messages in th ChatRoom as read, if some messages
// weren't, this will trigger some read notifications to the remote.
ChatRoom.MarkAsRead();
// Only here to update display of unread message count on parent frames.
// See NavigationRoot.UpdateUnreadMessageCount() to see how to get a
// global unread message count.
NavigationService.CurrentNavigationRoot.UpdateUnreadMessageCount();
NavigationService.CurrentChatspage.UpdateChatRooms();
// We can find all the info from the peer in the PeerAddress attribute
// of a ChatRoom object.
ChatHeaderText.Text += ChatRoom.PeerAddress.Username;
PeerUsername.Text += ChatRoom.PeerAddress.Username;
// And yours in LocalAddress.
YourUsername.Text += ChatRoom.LocalAddress.Username;
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
// Don't forget to unregister delegate to avoid memory leak
ChatRoom.Listener = null;
}
/// <summary>
/// Delegate method called every time a message is received in this chat room.
/// </summary>
private void OnMessageReceived(ChatRoom chatRoom, ChatMessage message)
{
if (ChatRoom != null)
{
AddMessage(message);
chatRoom.MarkAsRead();
}
}
private void AddMessage(ChatMessage chatMessage)
{
TextBlock textBlock = new TextBlock();
// You can find a lot of information on a ChatMessage object.
// Here we used the IsOutgoing info to choose on each side
// of the frame the message should be displayed.
if (chatMessage.IsOutgoing)
{
textBlock.HorizontalAlignment = HorizontalAlignment.Right;
}
else
{
textBlock.HorizontalAlignment = HorizontalAlignment.Left;
}
// You can see we take the first element of the Contents list of our ChatMessage. To keep
// it simple we assume that we only send simple text message, we will talk more about multipart
// messages and other types of messagse in the next step.
// For now we only handle chat messages with one content, so we can find our text in
// chatMessage.Contents.First().Utf8Text.
// We used ["" +] because if the message is a file transfer for example the Utf8Text can be null.
textBlock.Text = "" + chatMessage.Contents.First().Utf8Text;
MessagesList.Children.Add(textBlock);
ScrollToBottom();
}
private void ScrollToBottom()
{
MessagesScroll.UpdateLayout();
MessagesScroll.ChangeView(1, MessagesScroll.ExtentHeight, 1);
}
/// <summary>
/// Method called when the "Send" button is clicked
/// </summary>
private void OutgoingMessageButton_Click(object sender, RoutedEventArgs e)
{
if (ChatRoom != null && OutgoingMessageText.Text != null && OutgoingMessageText.Text.Length > 0)
{
// We use the ChatRoom to create a new ChatMessage object. Here we used
// the method CreateMessage(string message) to create a text message.
ChatMessage chatMessage = ChatRoom.CreateMessage(OutgoingMessageText.Text);
// And simply call the Send() method to send the message.
chatMessage.Send();
AddMessage(chatMessage);
}
OutgoingMessageText.Text = "";
}
}
}

View File

@@ -0,0 +1,39 @@
<Page
x:Class="_04_BasicChat.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,165 @@
/*
* 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 _04_BasicChat.Service;
using Linphone;
using System;
using System.Threading.Tasks;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
namespace _04_BasicChat.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);
// We just do this so we can update the list from other pages.
NavigationService.CurrentChatspage = this;
// Find and update the chat rooms list, see UpdateChatRooms
UpdateChatRooms();
// You are now familiar with those kinds of callback register.
// Here we want to update the list every time a message is
// received (list order and unread message count, see ChatsPage.xaml)
// or sent (list order)
CoreService.AddOnOnMessageReceivedDelegate(OnMessageReceiveOrSent);
CoreService.AddOnMessageSentDelegate(OnMessageReceiveOrSent);
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
NavigationService.CurrentChatspage = null;
// You need to unregister delegate to allow the garbage collector to
// collect this instance when you navigate away.
CoreService.RemoveOnOnMessageReceivedDelegate(OnMessageReceiveOrSent);
CoreService.RemoveOnMessageSentDelegate(OnMessageReceiveOrSent);
base.OnNavigatedFrom(e);
}
/// <summary>
/// Method called too update the list every time a message is received or sent.
/// </summary>
private void OnMessageReceiveOrSent(Core core, ChatRoom chatRoom, ChatMessage message) => UpdateChatRooms();
public void UpdateChatRooms()
{
ChatRoom selectedChatRoom = (ChatRoom)ChatRoomsLV.SelectedItem;
ChatRoomsLV.Items.Clear();
// In the ChatRooms list attribute you can find every ChatRooms linked
// to your user. The list is ordered by ChatRoom last activity date
// (most recent first).
// You can see in Chats.xaml that we only use the properties
// UnreadMessagesCount and PeerAdress to display our chat rooms.
// In further steps we will do more.
foreach (ChatRoom chatRoom in CoreService.Core.ChatRooms)
{
// Here we use the HistorySize attribute to display only
// ChatRooms were at least one message was exchange.
if (chatRoom.HistorySize > 0)
{
ChatRoomsLV.Items.Add(chatRoom);
if (selectedChatRoom == chatRoom)
{
ChatRoomsLV.SelectedItem = chatRoom;
}
}
}
}
/// <summary>
/// Method called when any item of the chat rooms ListView is clicked.
/// </summary>
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))
{
// We create a new ChatRoom with the address the user gave us.
// See CoreService.CreateOrGetChatRoom(string sipAddress) for more info
ChatRoom newChatRoom = CoreService.CreateOrGetChatRoom(peerSipAddress);
if (newChatRoom != null)
{
// If the ChatRoom creation succeed render/navigate to a ChatPage in the inner
// frame of the ChatsPage.
// See ChatPage.xaml.cs to understand how to get message history and how to send/receive
// and display new messages.
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();
}
}
}
/// <summary>
/// Small utility method to display a dialog with an input text
/// </summary>
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="_04_BasicChat.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 _04_BasicChat.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 _04_BasicChat.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="_04_BasicChat.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,158 @@
/*
* 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 _04_BasicChat.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 _04_BasicChat.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()
{
// The property UnreadChatMessageCountFromActiveLocals return the total
// number of unread messages in all the chat rooms off all connected accounts
// on the device. In the tutorial we only allow one account at a time, so
// you get the global unread message count for your account.
if (CoreService.Core.UnreadChatMessageCountFromActiveLocals > 0)
{
NewMessageCount.Text = "" + CoreService.Core.UnreadChatMessageCountFromActiveLocals;
NewMessageCountBorder.Visibility = Visibility.Visible;
}
else
{
NewMessageCountBorder.Visibility = Visibility.Collapsed;
}
}
}
}