diff --git a/App.xaml.cpp b/App.xaml.cpp index 3754cd7c6453c9fccdded7153cf201f13ca6257f..bf593b4ec862a8fb62bead7c3bd82a64204e3a05 100644 --- a/App.xaml.cpp +++ b/App.xaml.cpp @@ -38,6 +38,9 @@ App::App() { InitializeComponent(); // summon partial class, form generated files trough App.xaml + this->EnteredBackground += ref new EnteredBackgroundEventHandler(this, &App::App_EnteredBackground); + this->LeavingBackground += ref new LeavingBackgroundEventHandler(this, &App::App_LeavingBackground); + /* connect to delegate */ RingD::instance->summonWizard += ref new RingClientUWP::SummonWizard(this, &RingClientUWP::App::OnsummonWizard); } @@ -77,4 +80,26 @@ void App::OnsummonWizard() { ApplicationView::GetForCurrentView()->TryResizeView(Size(400, 600)); rootFrame->Navigate(Windows::UI::Xaml::Interop::TypeName(Views::Wizard::typeid)); +} + +void App::App_EnteredBackground(Platform::Object^ sender, EnteredBackgroundEventArgs^ e) +{ + MSG_("App_EnteredBackground"); + RingD::instance->isInBackground = true; +} + +void App::App_LeavingBackground(Platform::Object^ sender, LeavingBackgroundEventArgs^ e) +{ + MSG_("App_LeavingBackground"); + RingD::instance->isInBackground = false; +} + +void App::OnActivated(IActivatedEventArgs^ e) +{ + if (e->Kind == ActivationKind::ToastNotification) { + auto toastArgs = safe_cast<ToastNotificationActivatedEventArgs^>(e); + std::string args = Utils::toString(toastArgs->Argument); + if (!args.empty()) + RingD::instance->acceptIncommingCall(Utils::toPlatformString(args)); + } } \ No newline at end of file diff --git a/App.xaml.h b/App.xaml.h index 93b7bfe2ad4085e811b0c7d2c9c6e285f2a71b8e..0abdeddb9ecb5deb52ed848f6969f29a7c36f2c9 100644 --- a/App.xaml.h +++ b/App.xaml.h @@ -20,6 +20,8 @@ using namespace Windows::ApplicationModel::Activation; using namespace Windows::UI::Xaml::Controls; +using namespace Windows::ApplicationModel; +using namespace Windows::ApplicationModel::Background; namespace RingClientUWP { @@ -28,6 +30,10 @@ ref class App sealed { protected: virtual void OnLaunched(LaunchActivatedEventArgs^ e) override; + virtual void OnActivated(IActivatedEventArgs^ e) override; + + void App_EnteredBackground(Platform::Object^ sender, EnteredBackgroundEventArgs^ e); + void App_LeavingBackground(Platform::Object^ sender, LeavingBackgroundEventArgs^ e); internal: App(); @@ -36,4 +42,5 @@ private: Frame^ rootFrame; void OnsummonWizard(); }; + } \ No newline at end of file diff --git a/Assets/default.wav b/Assets/default.wav new file mode 100644 index 0000000000000000000000000000000000000000..f68346e5a2df863e1e6741532042db4c173014e0 Binary files /dev/null and b/Assets/default.wav differ diff --git a/Assets/message_notification_sound.wav b/Assets/message_notification_sound.wav new file mode 100644 index 0000000000000000000000000000000000000000..e2e531157d773e65ad6f6529d007aa67fbfb4fe5 Binary files /dev/null and b/Assets/message_notification_sound.wav differ diff --git a/MainPage.xaml.cpp b/MainPage.xaml.cpp index ae7bda2109e5bc8e78a1b572b0963b8e3d66cfdb..75e953559ccffc328dbb6590d5fca43a65989f3a 100644 --- a/MainPage.xaml.cpp +++ b/MainPage.xaml.cpp @@ -145,7 +145,23 @@ RingClientUWP::MainPage::showFrame(Windows::UI::Xaml::Controls::Frame^ frame) void RingClientUWP::MainPage::OnNavigatedTo(NavigationEventArgs ^ e) { + auto iter = BackgroundTaskRegistration::AllTasks->First(); + auto hascur = iter->HasCurrent; + while (hascur) + { + auto cur = iter->Current->Value; + cur->Unregister(true); + hascur = iter->MoveNext(); + } + BackgroundExecutionManager::RequestAccessAsync(); + BackgroundTaskBuilder^ builder = ref new BackgroundTaskBuilder(); + builder->Name = "CallRefusalBackgroundTask"; + //builder->TaskEntryPoint = "RingClientUWP.BackgroundActivity"; + builder->SetTrigger(ref new ToastNotificationActionTrigger()); + BackgroundTaskRegistration^ registration = builder->Register(); + RingD::instance->init(); + showLoadingOverlay(true, false); } diff --git a/Package.appxmanifest b/Package.appxmanifest index 4472030692b55e1d2041f188424a6446926db796..9030203152959ba533dfcba3bb469b7dc69020d0 100644 --- a/Package.appxmanifest +++ b/Package.appxmanifest @@ -1,5 +1,9 @@ <?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" IgnorableNamespaces="uap mp"> +<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:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3" + IgnorableNamespaces="uap uap3 mp"> <Identity Name="Savoir-faireLinux.GNURing" Publisher="CN=8121A5F7-3CA1-4CAA-92B2-4F595B011941" Version="1.1.12.0" /> <mp:PhoneIdentity PhoneProductId="2385953f-9019-423d-aa82-d1bbacfa258b" PhonePublisherId="00000000-0000-0000-0000-000000000000" /> <Properties> @@ -8,7 +12,7 @@ <Logo>Assets\StoreLogo.png</Logo> </Properties> <Dependencies> - <TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.0.0" MaxVersionTested="10.0.0.0" /> + <TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.14393.0" MaxVersionTested="10.0.14393.0" /> </Dependencies> <Resources> <Resource Language="x-generate" /> @@ -27,6 +31,7 @@ <Capability Name="internetClientServer" /> <Capability Name="internetClient" /> <Capability Name="privateNetworkClientServer" /> + <uap3:Capability Name="backgroundMediaPlayback"/> <uap:Capability Name="userAccountInformation" /> <DeviceCapability Name="microphone" /> <DeviceCapability Name="webcam" /> diff --git a/RingD.cpp b/RingD.cpp index 555d71a68bff34b1cd0b46b26c9e42a100dd5df9..b337db99c3bc2d27423dff635043b4a1ad50e927 100644 --- a/RingD.cpp +++ b/RingD.cpp @@ -47,6 +47,7 @@ using namespace RingClientUWP; using namespace RingClientUWP::Utils; using namespace RingClientUWP::ViewModel; + using namespace Windows::System; void @@ -365,6 +366,52 @@ void RingClientUWP::RingD::registerThisDevice(String ^ pin, String ^ archivePass pin = ""; } +void +ShowCallToast(String^ callId, String^ from = nullptr) +{ + String^ payload = + "<toast scenario='incomingCall'> " + "<visual> " + "<binding template='ToastGeneric'>" + "<text>GNU Ring - Incoming call"+ (from?(" from " + from):"") +"</text>" + "</binding>" + "</visual>" + /*"<actions>" + "<action arguments = '" + callId + "' content = 'Accept' />" + "</actions>"*/ + "<audio src='ms-appx:///Assets/default.wav' loop='true'/>" + "</toast>"; + + auto doc = ref new XmlDocument(); + doc->LoadXml(payload); + + auto toast = ref new ToastNotification(doc); + ToastNotificationManager::CreateToastNotifier()->Show(toast); +} + +void +ShowMsgToast(String^ from, String^ payload) +{ + String^ xml = + "<toast scenario='incomingMessage'> " + "<visual> " + "<binding template='ToastGeneric'>" + "<text>" + from + " : " + payload + "</text>" + "</binding>" + "</visual>" + "<actions>" + "<action arguments = '" + from + "'/>" + "</actions>" + "<audio src='ms-appx:///Assets/message_notification_sound.wav' loop='false'/>" + "</toast>"; + + auto doc = ref new XmlDocument(); + doc->LoadXml(xml); + + auto toast = ref new ToastNotification(doc); + ToastNotificationManager::CreateToastNotifier()->Show(toast); +} + void RingD::registerCallbacks() { @@ -469,15 +516,30 @@ RingD::registerCallbacks() if (state3 == CallStatus::OUTGOING_RINGING || state3 == CallStatus::INCOMING_RINGING) { try { - //Configuration::UserPreferences::instance->sendVCard(callId); + Configuration::UserPreferences::instance->sendVCard(callId); } catch (Exception^ e) { EXC_(e); } } - if (state3 == CallStatus::ENDED) + if (state3 == CallStatus::INCOMING_RINGING) { + if (isInBackground) { + ringtone_->Start(); + ShowCallToast(callId2); + } + else + ringtone_->Start(); + } + + if (state3 == CallStatus::IN_PROGRESS) { + ringtone_->Stop(); + } + + if (state3 == CallStatus::ENDED) { DRing::hangUp(callId); // solve a bug in the daemon API. + ringtone_->Stop(); + } CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync( CoreDispatcherPriority::High, ref new DispatchedHandler([=]() @@ -881,6 +943,7 @@ RingD::startDaemon() RingD::RingD() { + ringtone_ = ref new Ringtone("default.wav"); localFolder_ = Utils::toString(ApplicationData::Current->LocalFolder->Path); callIdsList_ = ref new Vector<String^>(); currentCallId = nullptr; diff --git a/RingD.h b/RingD.h index c7c727d6f8aa89eda92f37f490da3542a83aedf5..da5038f2f2b3c618b3c9da2ecdf1f3dddd9d8c85 100644 --- a/RingD.h +++ b/RingD.h @@ -18,10 +18,16 @@ **************************************************************************/ #include <dring.h> +#include "Ringtone.h" + using namespace concurrency; +using namespace Windows::UI::Notifications; +using namespace Windows::Data::Xml::Dom; + namespace RingClientUWP { + // its ok to keep this enum here and to use it with the wizard, because in pch.h headers are a-z sorted, // but it would be much more consistent to move this enum in globals.h when merged @@ -83,6 +89,7 @@ public: } } + property bool isInBackground; property StartingStatus _startingStatus; void cancelOutGoingCall2(String^ callId); // marche @@ -252,6 +259,7 @@ private: StartingStatus startingStatus_ = StartingStatus::NORMAL; bool editModeOn_ = false; bool debugModeOn_ = true; + Ringtone^ ringtone_; std::map<std::string, SharedCallback> callHandlers; std::map<std::string, SharedCallback> getAppPathHandler; diff --git a/Ringtone.cpp b/Ringtone.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d0685c9e6789b3184362e03b807cc1eba28a8c18 --- /dev/null +++ b/Ringtone.cpp @@ -0,0 +1,106 @@ +/************************************************************************** +* Copyright (C) 2016 by Savoir-faire Linux * +* Author: J�ger Nicolas <nicolas.jager@savoirfairelinux.com> * +* Author: Traczyk Andreas <traczyk.andreas@savoirfairelinux.com> * +* * +* 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/>. * +**************************************************************************/ +#include "pch.h" + +using namespace Windows::UI::Core; + +using namespace RingClientUWP; + +Ringtone::Ringtone(String^ fileName) +{ + fileName_ = fileName; + CreateGraph() + .then([this](task<void> t) { + t.get(); + CreateDefaultDeviceOutputNode() + .then([this](task<void> t) { + t.get(); + CreateFileInputNode() + .then([](task<void> t) { + t.get(); + }); + }); + }); +} + +void +Ringtone::Start() +{ + _graph->Start(); +} + +void +Ringtone::Stop() +{ + _graph->Stop(); + _graph->ResetAllNodes(); +} + +task<void> +Ringtone::CreateGraph() +{ + AudioGraphSettings^ settings = ref new AudioGraphSettings(Windows::Media::Render::AudioRenderCategory::Media); + return create_task(AudioGraph::CreateAsync(settings)) + .then([=](CreateAudioGraphResult^ result){ + if (result->Status != AudioGraphCreationStatus::Success) { + MSG_("CreateGraph failed"); + } + _graph = result->Graph; + }); +} + +task<void> +Ringtone::CreateDefaultDeviceOutputNode() +{ + return create_task(_graph->CreateDeviceOutputNodeAsync()) + .then([=](CreateAudioDeviceOutputNodeResult^ result){ + if (result->Status != AudioDeviceNodeCreationStatus::Success) { + MSG_("CreateDefaultDeviceOutputNode failed"); + } + _deviceOutputNode = result->DeviceOutputNode; + }); +} + +task<void> +Ringtone::CreateFileInputNode() +{ + Windows::ApplicationModel::Package^ package = Windows::ApplicationModel::Package::Current; + Windows::Storage::StorageFolder^ installedLocation = package->InstalledLocation; + return create_task(installedLocation->GetFolderAsync("Assets")) + .then([=](StorageFolder^ assetsFolder){ + create_task(assetsFolder->GetFileAsync(fileName_)) + .then([=](StorageFile^ ringtoneFile){ + // functions as a Background task but will force a SMTC panel into the thumbnail + //mp = ref new MediaPlayer(); + //mp->Source = MediaSource::CreateFromStorageFile(ringtoneFile); + ////mp->CommandManager->IsEnabled = false; + //mp->IsLoopingEnabled = true; + //mp->Play(); + create_task(_graph->CreateFileInputNodeAsync(ringtoneFile)) + .then([=](CreateAudioFileInputNodeResult^ result){ + if (result->Status != AudioFileNodeCreationStatus::Success) { + MSG_("CreateFileInputNode failed"); + } + _fileInputNode = result->FileInputNode; + _fileInputNode->LoopCount = nullptr; + _fileInputNode->AddOutgoingConnection(_deviceOutputNode); + }); + }); + }); +} \ No newline at end of file diff --git a/Ringtone.h b/Ringtone.h new file mode 100644 index 0000000000000000000000000000000000000000..bd26f78299a07eea2e893080f9463ae7e2eb33af --- /dev/null +++ b/Ringtone.h @@ -0,0 +1,50 @@ +/************************************************************************** +* Copyright (C) 2016 by Savoir-faire Linux * +* Author: J�ger Nicolas <nicolas.jager@savoirfairelinux.com> * +* Author: Traczyk Andreas <traczyk.andreas@savoirfairelinux.com> * +* * +* 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/>. * +**************************************************************************/ +#pragma once + +using namespace Platform; + +using namespace Windows::Media::Audio; +using namespace Windows::Media::Core; +using namespace Windows::Media::Playback; + +namespace RingClientUWP +{ + +public ref class Ringtone sealed +{ + +public: + Ringtone(String^ fileName); + void Start(); + void Stop(); + +private: + AudioFileInputNode^ _fileInputNode; + AudioGraph^ _graph; + AudioDeviceOutputNode^ _deviceOutputNode; + MediaPlayer^ mp; + String^ fileName_; + + task<void> CreateGraph(); + task<void> CreateDefaultDeviceOutputNode(); + task<void> CreateFileInputNode(); +}; + +} \ No newline at end of file diff --git a/UserPreferences.cpp b/UserPreferences.cpp index 2dd204ad79d9a0f62370c5645d4c6cbbe5c117c1..708e89d71fbea58a46173304412f1e400120394f 100644 --- a/UserPreferences.cpp +++ b/UserPreferences.cpp @@ -80,7 +80,7 @@ UserPreferences::Stringify() preferencesObject->SetNamedValue("PREF_ACCOUNT_INDEX", JsonValue::CreateNumberValue( PREF_ACCOUNT_INDEX )); preferencesObject->SetNamedValue("PREF_PROFILE_HASPHOTO", JsonValue::CreateBooleanValue( PREF_PROFILE_HASPHOTO)); - preferencesObject->SetNamedValue("PREF_PROFILE_UID", JsonValue::CreateNumberValue( PREF_PROFILE_UID )); + preferencesObject->SetNamedValue("PREF_PROFILE_UID", JsonValue::CreateNumberValue(static_cast<double>(PREF_PROFILE_UID))); return preferencesObject->Stringify(); } diff --git a/pch.h b/pch.h index 0a6b971699ea6127fdcf7923b257e2cb6e30dd02..f5b64fd23554a2d4bcc9d012740749a4426ece52 100644 --- a/pch.h +++ b/pch.h @@ -50,8 +50,11 @@ #include "UserPreferences.h" #include "VCardUtils.h" -/* video headers */ +/* video */ #include "Video.h" #include "VideoCaptureManager.h" #include "VideoManager.h" #include "VideoRendererManager.h" + +/* audio */ +#include "Ringtone.h" \ No newline at end of file diff --git a/ring-client-uwp.vcxproj b/ring-client-uwp.vcxproj index a068a20b25cadb386161fe5d4707f075dffd2415..451ad6f620ef81c4d3e136a20f63976f43350d8b 100644 --- a/ring-client-uwp.vcxproj +++ b/ring-client-uwp.vcxproj @@ -7,8 +7,8 @@ <MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion> <AppContainerApplication>true</AppContainerApplication> <ApplicationType>Windows Store</ApplicationType> - <WindowsTargetPlatformVersion>10.0.10586.0</WindowsTargetPlatformVersion> - <WindowsTargetPlatformMinVersion>10.0.10240.0</WindowsTargetPlatformMinVersion> + <WindowsTargetPlatformVersion>10.0.14393.0</WindowsTargetPlatformVersion> + <WindowsTargetPlatformMinVersion>10.0.14393.0</WindowsTargetPlatformMinVersion> <ApplicationTypeRevision>10.0</ApplicationTypeRevision> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> @@ -199,6 +199,9 @@ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild> </ClInclude> <ClInclude Include="RingDebug.h" /> + <ClInclude Include="Ringtone.h"> + <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild> + </ClInclude> <ClInclude Include="SmartPanel.xaml.h"> <DependentUpon>SmartPanel.xaml</DependentUpon> </ClInclude> @@ -333,6 +336,9 @@ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild> </ClCompile> <ClCompile Include="RingDebug.cpp" /> + <ClCompile Include="Ringtone.cpp"> + <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild> + </ClCompile> <ClCompile Include="SmartPanel.xaml.cpp"> <DependentUpon>SmartPanel.xaml</DependentUpon> </ClCompile> @@ -364,6 +370,10 @@ <ItemGroup> <None Include="Package.StoreAssociation.xml" /> </ItemGroup> + <ItemGroup> + <Media Include="Assets\default.wav" /> + <Media Include="Assets\message_notification_sound.wav" /> + </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> diff --git a/ring-client-uwp.vcxproj.filters b/ring-client-uwp.vcxproj.filters index 0f4042647f646195266d2120ec99961ec9b1acbf..b0b5545fb6f46c2cad7bed8b302f982658003d44 100644 --- a/ring-client-uwp.vcxproj.filters +++ b/ring-client-uwp.vcxproj.filters @@ -77,6 +77,9 @@ <ClCompile Include="VCardUtils.cpp"> <Filter>Common</Filter> </ClCompile> + <ClCompile Include="Ringtone.cpp"> + <Filter>Media\Audio</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="LoadingPage.xaml.h" /> @@ -150,6 +153,9 @@ <ClInclude Include="VCardUtils.h"> <Filter>Common</Filter> </ClInclude> + <ClInclude Include="Ringtone.h"> + <Filter>Media\Audio</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <Image Include="Assets\AccountTypeRING.png"> @@ -316,4 +322,12 @@ <UniqueIdentifier>{b81596e4-e16c-4537-9631-65655360cbf4}</UniqueIdentifier> </Filter> </ItemGroup> + <ItemGroup> + <Media Include="Assets\default.wav"> + <Filter>Assets</Filter> + </Media> + <Media Include="Assets\message_notification_sound.wav"> + <Filter>Assets</Filter> + </Media> + </ItemGroup> </Project> \ No newline at end of file