Project 'savoirfairelinux/ring-client-uwp' was moved to 'savoirfairelinux/jami-client-uwp'. Please update any links and bookmarks that may still have the old path.
Select Git revision
-
- adds the ability to select device, resolution, and frame rate - modifies the initialization of the daemon by seperating the registration of the callbacks from the init function and places the start and run loop in an IAsyncAction worker thread with forced high priority - uses std::ofstream for debug log file instead of Platform functions Change-Id: I32439088fe58513c46d11297db4898ca237174e7 Tuleap: #790
- adds the ability to select device, resolution, and frame rate - modifies the initialization of the daemon by seperating the registration of the callbacks from the init function and places the start and run loop in an IAsyncAction worker thread with forced high priority - uses std::ofstream for debug log file instead of Platform functions Change-Id: I32439088fe58513c46d11297db4898ca237174e7 Tuleap: #790
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
VideoPage.xaml.cpp 38.60 KiB
/**************************************************************************
* Copyright (C) 2016 by Savoir-faire Linux *
* Author: Jger Nicolas <nicolas.jager@savoirfairelinux.com> *
* Author: Traczyk Andreas <andreas.traczyk@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"
#include "VideoPage.xaml.h"
#include <MemoryBuffer.h> // IMemoryBufferByteAccess
using namespace RingClientUWP::Views;
using namespace ViewModel;
using namespace Video;
using namespace Concurrency;
using namespace Platform;
using namespace Windows::Devices::Enumeration;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Controls::Primitives;
using namespace Windows::UI::Xaml::Data;
using namespace Windows::UI::Xaml::Input;
using namespace Windows::UI::Xaml::Media;
using namespace Windows::UI::Xaml::Navigation;
using namespace Windows::Media::Capture;
using namespace Windows::ApplicationModel::Core;
using namespace Windows::UI::Core;
using namespace Windows::UI;
using namespace Windows::Graphics::Display;
using namespace Windows::Graphics::Imaging;
using namespace Windows::Media;
using namespace Windows::UI::Xaml::Media::Imaging;
using namespace Windows::UI::Xaml::Media::Animation;
using namespace Windows::UI::Xaml::Shapes;
using namespace Windows::Devices::Sensors;
using namespace Windows::UI::Input;
VideoPage::VideoPage()
{
InitializeComponent();
barFading = true;
Page::NavigationCacheMode = Navigation::NavigationCacheMode::Required;
VideoManager::instance->rendererManager()->writeVideoFrame +=
ref new WriteVideoFrame([this](String^ id, uint8_t* buf, int width, int height)
{
CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(CoreDispatcherPriority::High,
ref new DispatchedHandler([=]() {
try {
currentRendererWrapper = VideoManager::instance->rendererManager()->renderer(id);
if (!currentRendererWrapper) {
return;
}
else {
currentRendererWrapper->isRendering = true;
create_task(WriteFrameAsSoftwareBitmapAsync(id, buf, width, height))
.then([=](task<void> previousTask) {
try {
previousTask.get();
}
catch (Platform::Exception^ e) {
EXC_(e);
}
});
}
}
catch(Platform::COMException^ e) {
EXC_(e);
}
}));
});
VideoManager::instance->captureManager()->startPreviewing +=
ref new StartPreviewing([this]()
{
_PreviewImage_->Visibility = Windows::UI::Xaml::Visibility::Visible;
_PreviewImageResizer_->Visibility = Windows::UI::Xaml::Visibility::Visible;
_PreviewImage_->FlowDirection = VideoManager::instance->captureManager()->mirroringPreview ?
Windows::UI::Xaml::FlowDirection::RightToLeft :
Windows::UI::Xaml::FlowDirection::LeftToRight;
double aspectRatio = VideoManager::instance->captureManager()->aspectRatio();
_PreviewImage_->Height = ( _videoContent_->ActualHeight / 4 );
_PreviewImage_->Width = _PreviewImage_->Height * aspectRatio;
_PreviewImageRect_->Width = _PreviewImage_->Width;
_PreviewImageRect_->Height = _PreviewImage_->Height;
});
VideoManager::instance->captureManager()->stopPreviewing +=
ref new StopPreviewing([this]()
{
_PreviewImage_->Source = nullptr;
_PreviewImage_->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
_PreviewImageResizer_->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
});
VideoManager::instance->captureManager()->getSink +=
ref new GetSink([this]()
{
return _PreviewImage_;
});
VideoManager::instance->rendererManager()->clearRenderTarget +=
ref new ClearRenderTarget([this]()
{
_IncomingVideoImage_->Source = nullptr;
});
RingD::instance->windowResized +=
ref new WindowResized([=](float width, float height)
{
});
RingD::instance->incomingAccountMessage +=
ref new IncomingAccountMessage([&](String^ accountId, String^ from, String^ payload)
{
scrollDown();
});
RingD::instance->vCardUpdated += ref new VCardUpdated([&](Contact^ contact)
{
Utils::runOnUIThread([this, contact]() {
SmartPanelItemsViewModel::instance->update({ "_bestName2", "_avatarImage" });
updatePageContent();
});
});
RingD::instance->stateChange +=
ref new StateChange([&](String^ callId, CallStatus state, int code)
{
switch (state) {
case CallStatus::IN_PROGRESS:
{
for (auto it : SmartPanelItemsViewModel::instance->itemsList)
if (it->_callStatus != CallStatus::IN_PROGRESS && it->_callId != callId)
RingD::instance->pauseCall(Utils::toString(it->_callId));
_callPaused_->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
_IncomingVideoImage_->Visibility = Windows::UI::Xaml::Visibility::Visible;
callTimerUpdater->Start();
updateCallTimer(nullptr, nullptr);
RingD::instance->startSmartInfo(500);
videoPage_InputHandlerToken = Window::Current->CoreWindow->KeyDown +=
ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(this, &VideoPage::_corewindow__KeyDown);
break;
}
case CallStatus::ENDED:
{
Video::VideoManager::instance->rendererManager()->raiseClearRenderTarget();
if (RingD::instance->isFullScreen)
RingD::instance->setWindowedMode();
/* "close" the chat panel */
closeChatPanel();
callTimerUpdater->Stop();
//RingD::instance->stopSmartInfo();
Window::Current->CoreWindow->KeyDown -= videoPage_InputHandlerToken;
_smartInfoBorder_->Visibility = VIS::Collapsed;
break;
}
case CallStatus::PEER_PAUSED:
case CallStatus::PAUSED:
_callPaused_->Visibility = Windows::UI::Xaml::Visibility::Visible;
_IncomingVideoImage_->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
break;
}
});
RingD::instance->messageDataLoaded += ref new MessageDataLoaded([&]() { scrollDown(); });
RingD::instance->updateSmartInfo += ref new RingClientUWP::UpdateSmartInfo(this, &RingClientUWP::Views::VideoPage::OnsmartInfoUpdated);
RingD::instance->incomingMessage += ref new RingClientUWP::IncomingMessage(this, &RingClientUWP::Views::VideoPage::OnincomingMessage);
RingD::instance->incomingVideoMuted += ref new RingClientUWP::IncomingVideoMuted(this, &RingClientUWP::Views::VideoPage::OnincomingVideoMuted);
VideoManager::instance->captureManager()->startPreviewing += ref new RingClientUWP::StartPreviewing(this, &RingClientUWP::Views::VideoPage::OnstartPreviewing);
VideoManager::instance->captureManager()->stopPreviewing += ref new RingClientUWP::StopPreviewing(this, &RingClientUWP::Views::VideoPage::OnstopPreviewing);
RingD::instance->audioMuted += ref new RingClientUWP::AudioMuted(this, &RingClientUWP::Views::VideoPage::OnaudioMuted);
RingD::instance->videoMuted += ref new RingClientUWP::VideoMuted(this, &RingClientUWP::Views::VideoPage::OnvideoMuted);
InitManipulationTransforms();
_PreviewImage_->ManipulationDelta += ref new ManipulationDeltaEventHandler(this, &VideoPage::PreviewImage_ManipulationDelta);
_PreviewImage_->ManipulationCompleted += ref new ManipulationCompletedEventHandler(this, &VideoPage::PreviewImage_ManipulationCompleted);
_PreviewImage_->ManipulationMode =
ManipulationModes::TranslateX |
ManipulationModes::TranslateY;
_PreviewImageResizer_->ManipulationDelta += ref new ManipulationDeltaEventHandler(this, &VideoPage::PreviewImageResizer_ManipulationDelta);
_PreviewImageResizer_->ManipulationCompleted += ref new ManipulationCompletedEventHandler(this, &VideoPage::PreviewImageResizer_ManipulationCompleted);
_PreviewImageResizer_->ManipulationMode = ManipulationModes::TranslateY;
_chatPanelResizeBarGrid_->ManipulationDelta += ref new ManipulationDeltaEventHandler(this, &VideoPage::_chatPanelResizeBarGrid__ManipulationDelta);
_chatPanelResizeBarGrid_->ManipulationCompleted += ref new ManipulationCompletedEventHandler(this, &VideoPage::_chatPanelResizeBarGrid__ManipulationCompleted);
_chatPanelResizeBarGrid_->ManipulationMode =
ManipulationModes::TranslateX |
ManipulationModes::TranslateY;
TimeSpan timeSpan;
timeSpan.Duration = static_cast<long long>(1e7);
callTimerUpdater = ref new DispatcherTimer;
callTimerUpdater->Interval = timeSpan;
callTimerUpdater->Tick += ref new Windows::Foundation::EventHandler<Object^>(this, &VideoPage::updateCallTimer);
showSmartInfo = false;
}
void
VideoPage::OnsmartInfoUpdated(const std::map<std::string, std::string>& info)
{
auto smartInfo = Utils::convertMap(info);
if (auto selectedItem = SmartPanelItemsViewModel::instance->_selectedItem)
_si_CallId_->Text = "CallID: " + selectedItem->_callId;
if (smartInfo->HasKey("local FPS"))
_si_fps1_->Text = smartInfo->Lookup("local FPS");
if (smartInfo->HasKey("local video codec"))
_si_vc1_->Text = smartInfo->Lookup("local video codec");
if (smartInfo->HasKey("local audio codec"))
_si_ac1_->Text = smartInfo->Lookup("local audio codec");
auto localResolution = VideoManager::instance->captureManager()->activeDevice->currentResolution();
_si_res1_->Text = localResolution->getFriendlyName();
if (smartInfo->HasKey("remote FPS"))
_si_fps2_->Text = smartInfo->Lookup("remote FPS") ;
if (smartInfo->HasKey("remote video codec"))
_si_vc2_->Text = smartInfo->Lookup("remote video codec");
if (smartInfo->HasKey("remote audio codec"))
_si_ac2_->Text = smartInfo->Lookup("remote audio codec");
if (currentRendererWrapper) {
auto remoteResolution = currentRendererWrapper->renderer->width.ToString() +
"x" +
currentRendererWrapper->renderer->height.ToString();
_si_res2_->Text = remoteResolution;
}
}
void
VideoPage::updateCallTimer(Object^ sender, Object^ e)
{
if(auto item = SmartPanelItemsViewModel::instance->_selectedItem)
_callTime_->Text = item->_callTime;
}
void
VideoPage::OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs^ e)
{
updatePageContent();
closeChatPanel();
}
void RingClientUWP::Views::VideoPage::updatePageContent()
{
auto item = SmartPanelItemsViewModel::instance->_selectedItem;
auto contact = (item) ? item->_contact : nullptr;
if (!contact)
return;
_contactName_->Text = contact->_bestName;
//_contactBarAvatar_->ImageSource = RingClientUWP::ResourceMananger::instance->imageFromRelativePath(contact->_avatarImage);
if (contact->_avatarImage != " ") {
auto avatarImageUri = ref new Windows::Foundation::Uri(contact->_avatarImage);
_contactBarAvatar_->ImageSource = ref new BitmapImage(avatarImageUri);
_defaultContactBarAvatarGrid_->Visibility = VIS::Collapsed;
_contactBarAvatarGrid_->Visibility = VIS::Visible;
}
else {
_defaultContactBarAvatarGrid_->Visibility = VIS::Visible;
_contactBarAvatarGrid_->Visibility = VIS::Collapsed;
_defaultAvatar_->Fill = contact->_avatarColorBrush;
_defaultAvatarInitial_->Text = Utils::getUpperInitial(contact->_bestName2);
}
_messagesList_->ItemsSource = contact->_conversation->_messages;
scrollDown();
}
void
VideoPage::updatePreviewFrameDimensions()
{
double aspectRatio = VideoManager::instance->captureManager()->aspectRatio();
TransformGroup^ transforms = ref new TransformGroup();
double scaleValue = 1 + userPreviewHeightModifier / _PreviewImage_->Height;
scaleValue = std::max(std::min(1.75, scaleValue), 0.5);
userPreviewHeightModifier = _PreviewImage_->Height * (scaleValue - 1);
ScaleTransform^ scale = ref new ScaleTransform();
scale->ScaleX = scaleValue;
scale->ScaleY = scaleValue;
TranslateTransform^ translate = ref new TranslateTransform();
switch (quadrant)
{
case Quadrant::SE:
translate->Y = -userPreviewHeightModifier;
translate->X = translate->Y * aspectRatio;
break;
case Quadrant::SW:
translate->Y = -userPreviewHeightModifier;
translate->X = 0;
break;
case Quadrant::NW:
translate->Y = 0;
translate->X = 0;
break;
case Quadrant::NE:
translate->Y = 0;
translate->X = -userPreviewHeightModifier * aspectRatio;
break;
default:
break;
}
transforms->Children->Append(scale);
transforms->Children->Append(translate);
_PreviewImage_->RenderTransform = transforms;
_PreviewImageResizer_->RenderTransform = translate;
arrangeResizer();
}
void RingClientUWP::Views::VideoPage::scrollDown()
{
_scrollView_->UpdateLayout();
_scrollView_->ScrollToVerticalOffset(_scrollView_->ScrollableHeight);
}
void RingClientUWP::Views::VideoPage::screenVideo(bool state)
{
if (state) {
Video::VideoManager::instance->rendererManager()->raiseClearRenderTarget();
_callPaused_->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
_IncomingVideoImage_->Visibility = Windows::UI::Xaml::Visibility::Visible;
_PreviewImage_->Visibility = Windows::UI::Xaml::Visibility::Visible;
_PreviewImageResizer_->Visibility = Windows::UI::Xaml::Visibility::Visible;
} else {
_callPaused_->Visibility = Windows::UI::Xaml::Visibility::Visible;
_IncomingVideoImage_->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
_PreviewImage_->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
_PreviewImageResizer_->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
}
}
void
RingClientUWP::Views::VideoPage::_sendBtn__Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
sendMessage();
}
void
RingClientUWP::Views::VideoPage::sendMessage()
{
if (auto item = SmartPanelItemsViewModel::instance->_selectedItem) {
auto contact = item->_contact;
auto txt = _messageTextBox_->Text;
/* empty the textbox */
_messageTextBox_->Text = "";
if (!contact || txt->IsEmpty())
return;
RingD::instance->sendSIPTextMessage(txt);
scrollDown();
}
}
void RingClientUWP::Views::VideoPage::Button_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
}
void RingClientUWP::Views::VideoPage::_btnCancel__Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
}
void RingClientUWP::Views::VideoPage::_btnHangUp__Tapped(Platform::Object^ sender, Windows::UI::Xaml::Input::TappedRoutedEventArgs^ e)
{
if (auto item = SmartPanelItemsViewModel::instance->_selectedItem) {
_PreviewImage_->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
_PreviewImageResizer_->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
RingD::instance->hangUpCall2(item->_callId);
pressHangUpCall();
}
else
WNG_("item not found, cannot hang up");
}
void RingClientUWP::Views::VideoPage::_btnPause__Tapped(Platform::Object^ sender, Windows::UI::Xaml::Input::TappedRoutedEventArgs^ e)
{
auto item = SmartPanelItemsViewModel::instance->_selectedItem;
if (item->_callStatus == CallStatus::IN_PROGRESS)
RingD::instance->pauseCall(Utils::toString(item->_callId));
else if (item->_callStatus == CallStatus::PAUSED)
RingD::instance->unPauseCall(Utils::toString(item->_callId));
pauseCall();
}
void RingClientUWP::Views::VideoPage::_btnChat__Tapped(Platform::Object^ sender, Windows::UI::Xaml::Input::TappedRoutedEventArgs^ e)
{
if (!isChatPanelOpen) {
openChatPanel();
}
else {
closeChatPanel();
}
}
void RingClientUWP::Views::VideoPage::_btnAddFriend__Tapped(Platform::Object^ sender, Windows::UI::Xaml::Input::TappedRoutedEventArgs^ e)
{
addContactCall();
}
void RingClientUWP::Views::VideoPage::_btnSwitch__Tapped(Platform::Object^ sender, Windows::UI::Xaml::Input::TappedRoutedEventArgs^ e)
{
transferCall();
}
void RingClientUWP::Views::VideoPage::_btnMicrophone__Tapped(Platform::Object^ sender, Windows::UI::Xaml::Input::TappedRoutedEventArgs^ e)
{
switchMicrophoneStateCall();
auto item = SmartPanelItemsViewModel::instance->_selectedItem;
auto state = !item->_audioMuted;
item->_audioMuted = state;
// refacto : compare how video and audios are muted, then decide which solution is best.
RingD::instance->muteAudio(Utils::toString(item->_callId), state); // nb : muteAudio == setMuteAudio
}
void RingClientUWP::Views::VideoPage::_btnMemo__Tapped(Platform::Object^ sender, Windows::UI::Xaml::Input::TappedRoutedEventArgs^ e)
{
reccordVideoCall();
}
void RingClientUWP::Views::VideoPage::_btnHQ__Tapped(Platform::Object^ sender, Windows::UI::Xaml::Input::TappedRoutedEventArgs^ e)
{
qualityVideoLevelCall();
}
void RingClientUWP::Views::VideoPage::_btnVideo__Tapped(Platform::Object^ sender, Windows::UI::Xaml::Input::TappedRoutedEventArgs^ e)
{
switchVideoStateCall();
}
void RingClientUWP::Views::VideoPage::_videoControl__PointerMoved(Platform::Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ e)
{
if (barFading)
fadeVideoControlsStoryboard->Begin();
}
void RingClientUWP::Views::VideoPage::btnAny_entered(Platform::Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ e)
{
barFading = false;
fadeVideoControlsStoryboard->Stop();
}
void RingClientUWP::Views::VideoPage::btnAny_exited(Platform::Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ e)
{
barFading = true;
}
void
VideoPage::SetBuffer(uint8_t* buf, int width, int height)
{
videoFrame = ref new VideoFrame(BitmapPixelFormat::Bgra8, width, height);
softwareBitmap = videoFrame->SoftwareBitmap;
BitmapBuffer^ bitmapBuffer = softwareBitmap->LockBuffer(BitmapBufferAccessMode::Write);
IMemoryBufferReference^ memoryBufferReference = bitmapBuffer->CreateReference();
Microsoft::WRL::ComPtr<IMemoryBufferByteAccess> byteAccess;
if (SUCCEEDED(reinterpret_cast<IUnknown*>(memoryBufferReference)->QueryInterface(IID_PPV_ARGS(&byteAccess))))
{
byte* data;
unsigned capacity;
byteAccess->GetBuffer(&data, &capacity);
std::memcpy(data, buf, static_cast<size_t>(capacity));
}
delete memoryBufferReference;
delete bitmapBuffer;
}
task<void>
VideoPage::WriteFrameAsSoftwareBitmapAsync(String^ id, uint8_t* buf, int width, int height)
{
SetBuffer(buf, width, height);
VideoManager::instance->rendererManager()->renderer(id)->isRendering = false;
auto sbSource = ref new Media::Imaging::SoftwareBitmapSource();
return create_task(sbSource->SetBitmapAsync(softwareBitmap))
.then([this, sbSource]()
{
try {
_IncomingVideoImage_->Source = sbSource;
}
catch (Exception^ e) {
EXC_(e);
}
});
}
void RingClientUWP::Views::VideoPage::OnincomingMessage(Platform::String ^callId, Platform::String ^payload)
{
openChatPanel();
scrollDown();
}
void RingClientUWP::Views::VideoPage::_btnVideo__Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
auto item = SmartPanelItemsViewModel::instance->_selectedItem;
item->muteVideo(!item->_videoMuted);
}
void RingClientUWP::Views::VideoPage::OnincomingVideoMuted(Platform::String ^callId, bool state)
{
/*_callPaused_->Visibility = (state)
? Windows::UI::Xaml::Visibility::Visible
: Windows::UI::Xaml::Visibility::Collapsed;*/
_IncomingVideoImage_->Visibility = (state)
? Windows::UI::Xaml::Visibility::Collapsed
: Windows::UI::Xaml::Visibility::Visible;
}
void RingClientUWP::Views::VideoPage::OnstartPreviewing()
{
_PreviewImage_->Visibility = Windows::UI::Xaml::Visibility::Visible;
_PreviewImageResizer_->Visibility = Windows::UI::Xaml::Visibility::Visible;
}
void RingClientUWP::Views::VideoPage::OnstopPreviewing()
{
_PreviewImage_->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
_PreviewImageResizer_->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
}
void RingClientUWP::Views::VideoPage::_btnMicrophone__Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
switchMicrophoneStateCall();
auto item = SmartPanelItemsViewModel::instance->_selectedItem;
auto state = !item->_audioMuted;
item->_audioMuted = state;
// refacto : compare how video and audios are muted, then decide which solution is best.
RingD::instance->muteAudio(Utils::toString(item->_callId), state); // nb : muteAudio == setMuteAudio
}
void RingClientUWP::Views::VideoPage::OnaudioMuted(const std::string &callId, bool state)
{
_txbkMicrophoneMuted_->Visibility = (state) ? Windows::UI::Xaml::Visibility::Visible
: Windows::UI::Xaml::Visibility::Collapsed;
}
void RingClientUWP::Views::VideoPage::OnvideoMuted(const std::string &callId, bool state)
{
_txbkVideoMuted_->Visibility = (state) ? Windows::UI::Xaml::Visibility::Visible
: Windows::UI::Xaml::Visibility::Collapsed;
}
void RingClientUWP::Views::VideoPage::IncomingVideoImage_DoubleTapped(Platform::Object^ sender, Windows::UI::Xaml::Input::DoubleTappedRoutedEventArgs^ e)
{
RingD::instance->toggleFullScreen();
anchorPreview();
}
void RingClientUWP::Views::VideoPage::InitManipulationTransforms()
{
PreviewImage_transforms = ref new TransformGroup();
PreviewImage_previousTransform = ref new MatrixTransform();
PreviewImage_previousTransform->Matrix = Matrix::Identity;
PreviewImage_deltaTransform = ref new CompositeTransform();
PreviewImage_transforms->Children->Append(PreviewImage_previousTransform);
PreviewImage_transforms->Children->Append(PreviewImage_deltaTransform);
_PreviewImageRect_->RenderTransform = PreviewImage_transforms;
}
void RingClientUWP::Views::VideoPage::PreviewImage_ManipulationDelta(Platform::Object^ sender, ManipulationDeltaRoutedEventArgs^ e)
{
if (!isMovingPreview)
isMovingPreview = true;
_PreviewImageRect_->RenderTransform = PreviewImage_transforms;
PreviewImage_previousTransform->Matrix = PreviewImage_transforms->Value;
PreviewImage_deltaTransform->TranslateX = e->Delta.Translation.X;
PreviewImage_deltaTransform->TranslateY = e->Delta.Translation.Y;
computeQuadrant();
}
void
RingClientUWP::Views::VideoPage::computeQuadrant()
{
// Compute center coordinate of _videoContent_
Point centerOfVideoFrame = Point( static_cast<float>(_videoContent_->ActualWidth - _colChatBx_->ActualWidth) / 2,
static_cast<float>(_videoContent_->ActualHeight - _rowChatBx_->ActualHeight) / 2 );
// Compute the center coordinate of _PreviewImage_ relative to _videoContent_
Point centerOfPreview = Point( static_cast<float>(_PreviewImage_->ActualWidth) / 2,
static_cast<float>(_PreviewImage_->ActualHeight) / 2 );
UIElement^ container = dynamic_cast<UIElement^>(VisualTreeHelper::GetParent(_videoContent_));
GeneralTransform^ transform = _PreviewImage_->TransformToVisual(container);
Point relativeCenterOfPreview = transform->TransformPoint(centerOfPreview);
// Compute the difference between the center of _videoContent_
// and the relative scaled center of _PreviewImageRect_
Point diff = Point( centerOfVideoFrame.X - relativeCenterOfPreview.X,
centerOfVideoFrame.Y - relativeCenterOfPreview.Y );
lastQuadrant = quadrant;
if (diff.X > 0)
quadrant = diff.Y > 0 ? Quadrant::NW : Quadrant::SW;
else
quadrant = diff.Y > 0 ? Quadrant::NE : Quadrant::SE;
if (lastQuadrant != quadrant) {
arrangeResizer();
}
}
void
RingClientUWP::Views::VideoPage::arrangeResizer()
{
double scaleValue = (userPreviewHeightModifier + _PreviewImage_->Height) / _PreviewImage_->Height;
float scaledWidth = static_cast<float>(scaleValue * _PreviewImage_->ActualWidth);
float scaledHeight = static_cast<float>(scaleValue * _PreviewImage_->ActualHeight);
float rSize = 20; // the size of the square UIElement used to resize the preview
float xOffset, yOffset;
PointCollection^ resizeTrianglePoints = ref new PointCollection();
switch (quadrant)
{
case Quadrant::SE:
xOffset = 0;
yOffset = 0;
resizeTrianglePoints->Append(Point(xOffset, yOffset));
resizeTrianglePoints->Append(Point(xOffset + rSize, yOffset));
resizeTrianglePoints->Append(Point(xOffset, yOffset + rSize));
break;
case Quadrant::SW:
xOffset = scaledWidth - rSize;
yOffset = 0;
resizeTrianglePoints->Append(Point(xOffset, yOffset));
resizeTrianglePoints->Append(Point(xOffset + rSize, yOffset));
resizeTrianglePoints->Append(Point(xOffset + rSize, yOffset + rSize));
break;
case Quadrant::NW:
xOffset = scaledWidth - rSize;
yOffset = scaledHeight - rSize;
resizeTrianglePoints->Append(Point(xOffset + rSize, yOffset));
resizeTrianglePoints->Append(Point(xOffset + rSize, yOffset + rSize));
resizeTrianglePoints->Append(Point(xOffset, yOffset + rSize));
break;
case Quadrant::NE:
xOffset = 0;
yOffset = scaledHeight - rSize;
resizeTrianglePoints->Append(Point(xOffset, yOffset + rSize));
resizeTrianglePoints->Append(Point(xOffset + rSize, yOffset + rSize));
resizeTrianglePoints->Append(Point(xOffset, yOffset));
break;
default:
break;
}
_PreviewImageResizer_->Points = resizeTrianglePoints;
}
void RingClientUWP::Views::VideoPage::PreviewImage_ManipulationCompleted(Platform::Object^ sender, ManipulationCompletedRoutedEventArgs^ e)
{
isMovingPreview = false;
anchorPreview();
updatePreviewFrameDimensions();
}
void
VideoPage::PreviewImageResizer_Pressed(Object^ sender, PointerRoutedEventArgs^ e)
{
isResizingPreview = true;
lastUserPreviewHeightModifier = userPreviewHeightModifier;
}
void RingClientUWP::Views::VideoPage::anchorPreview()
{
PreviewImage_previousTransform->Matrix = Matrix::Identity;
_PreviewImageRect_->RenderTransform = nullptr;
switch (quadrant)
{
case Quadrant::SE:
_PreviewImageRect_->HorizontalAlignment = Windows::UI::Xaml::HorizontalAlignment::Right;
_PreviewImageRect_->VerticalAlignment = Windows::UI::Xaml::VerticalAlignment::Bottom;
break;
case Quadrant::SW:
_PreviewImageRect_->HorizontalAlignment = Windows::UI::Xaml::HorizontalAlignment::Left;
_PreviewImageRect_->VerticalAlignment = Windows::UI::Xaml::VerticalAlignment::Bottom;
break;
case Quadrant::NW:
_PreviewImageRect_->HorizontalAlignment = Windows::UI::Xaml::HorizontalAlignment::Left;
_PreviewImageRect_->VerticalAlignment = Windows::UI::Xaml::VerticalAlignment::Top;
break;
case Quadrant::NE:
_PreviewImageRect_->HorizontalAlignment = Windows::UI::Xaml::HorizontalAlignment::Right;
_PreviewImageRect_->VerticalAlignment = Windows::UI::Xaml::VerticalAlignment::Top;
break;
default:
break;
};
}
void
VideoPage::PreviewImageResizer_ManipulationDelta(Platform::Object^ sender, ManipulationDeltaRoutedEventArgs^ e)
{
if (!isResizingPreview)
isResizingPreview = true;
if (quadrant == Quadrant::NW || quadrant == Quadrant::NE) {
userPreviewHeightModifier = lastUserPreviewHeightModifier + e->Cumulative.Translation.Y;
}
else {
userPreviewHeightModifier = lastUserPreviewHeightModifier - (e->Cumulative.Translation.Y);
}
updatePreviewFrameDimensions();
}
void
VideoPage::PreviewImageResizer_ManipulationCompleted(Platform::Object^ sender, ManipulationCompletedRoutedEventArgs^ e)
{
isResizingPreview = false;
if (!isHoveringOnResizer) {
CoreApplication::MainView->CoreWindow->PointerCursor = ref new Windows::UI::Core::CoreCursor(Windows::UI::Core::CoreCursorType::Arrow, 0);
}
}
void
VideoPage::PreviewImage_PointerReleased(Platform::Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ e)
{
// For some reason, PreviewImage_ManipulationCompleted doesn't always fired when the mouse is released
anchorPreview();
updatePreviewFrameDimensions();
}
void
VideoPage::PreviewImageResizer_PointerEntered(Platform::Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ e)
{
isHoveringOnResizer = true;
if (!isMovingPreview) {
switch (quadrant)
{
case Quadrant::SE:
case Quadrant::NW:
CoreApplication::MainView->CoreWindow->PointerCursor = ref new Windows::UI::Core::CoreCursor(Windows::UI::Core::CoreCursorType::SizeNorthwestSoutheast, 0);
break;
case Quadrant::SW:
case Quadrant::NE:
CoreApplication::MainView->CoreWindow->PointerCursor = ref new Windows::UI::Core::CoreCursor(Windows::UI::Core::CoreCursorType::SizeNortheastSouthwest, 0);
break;
default:
break;
}
}
}
void
VideoPage::PreviewImageResizer_PointerExited(Platform::Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ e)
{
isHoveringOnResizer = false;
if (!isResizingPreview) {
CoreApplication::MainView->CoreWindow->PointerCursor = ref new Windows::UI::Core::CoreCursor(Windows::UI::Core::CoreCursorType::Arrow, 0);
}
}
void
VideoPage::PreviewImageResizer_PointerReleased(Platform::Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ e)
{
if (!isHoveringOnResizer) {
CoreApplication::MainView->CoreWindow->PointerCursor = ref new Windows::UI::Core::CoreCursor(Windows::UI::Core::CoreCursorType::Arrow, 0);
}
}
void
VideoPage::_chatPanelResizeBarGrid__PointerEntered(Platform::Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ e)
{
if (chtBoxOrientation == Orientation::Horizontal) {
CoreApplication::MainView->CoreWindow->PointerCursor = ref new Windows::UI::Core::CoreCursor(Windows::UI::Core::CoreCursorType::SizeWestEast, 0);
}
else {
CoreApplication::MainView->CoreWindow->PointerCursor = ref new Windows::UI::Core::CoreCursor(Windows::UI::Core::CoreCursorType::SizeNorthSouth, 0);
}
}
void
VideoPage::_chatPanelResizeBarGrid__PointerExited(Platform::Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ e)
{
if (!isResizingChatPanel) {
CoreApplication::MainView->CoreWindow->PointerCursor = ref new Windows::UI::Core::CoreCursor(Windows::UI::Core::CoreCursorType::Arrow, 0);
}
}
void
VideoPage::_chatPanelResizeBarGrid__PointerPressed(Platform::Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ e)
{
isResizingChatPanel = true;
lastchatPanelSize = chatPanelSize;
}
void
VideoPage::_chatPanelResizeBarGrid__PointerReleased(Platform::Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ e)
{
isResizingChatPanel = false;
}
void
VideoPage::_chatPanelResizeBarGrid__ManipulationDelta(Platform::Object^ sender, ManipulationDeltaRoutedEventArgs^ e)
{
if (chtBoxOrientation == Orientation::Horizontal) {
chatPanelSize = lastchatPanelSize - e->Cumulative.Translation.X;
}
else {
chatPanelSize = lastchatPanelSize - e->Cumulative.Translation.Y;
}
resizeChatPanel();
}
void
VideoPage::_chatPanelResizeBarGrid__ManipulationCompleted(Platform::Object^ sender, ManipulationCompletedRoutedEventArgs^ e)
{
isResizingChatPanel = false;
CoreApplication::MainView->CoreWindow->PointerCursor = ref new Windows::UI::Core::CoreCursor(Windows::UI::Core::CoreCursorType::Arrow, 0);
}
void
VideoPage::openChatPanel()
{
resizeChatPanel();
isChatPanelOpen = true;
SmartPanelItemsViewModel::instance->_selectedItem->_contact->_unreadMessages = 0;
}
void
VideoPage::closeChatPanel()
{
_colChatBx_->Width = 0;
_rowChatBx_->Height = 0;
isChatPanelOpen = false;
}
void
VideoPage::resizeChatPanel()
{
// clamp chatPanelSize
double minChatPanelSize = 176;
double maxChatPanelSize = chtBoxOrientation == Orientation::Horizontal ?
_videoContent_->ActualWidth * .5 :
_videoContent_->ActualHeight * .5;
chatPanelSize = std::max(minChatPanelSize, std::min(chatPanelSize, maxChatPanelSize));
if (chtBoxOrientation == Orientation::Horizontal) {
_colChatBx_->Width = GridLength(chatPanelSize, GridUnitType::Pixel);
_rowChatBx_->Height = 0;
}
else {
_colChatBx_->Width = 0;
_rowChatBx_->Height = GridLength(chatPanelSize, GridUnitType::Pixel);
}
}
void
VideoPage::_btnToggleOrientation__Tapped(Platform::Object^ sender, Windows::UI::Xaml::Input::TappedRoutedEventArgs^ e)
{
bool wasChatPanelOpen = isChatPanelOpen;
closeChatPanel();
if (chtBoxOrientation == Orientation::Horizontal) {
chtBoxOrientation = Orientation::Vertical;
_btnToggleOrientation_->Content = L"\uE90D";
_chatPanelResizeBarGrid_->HorizontalAlignment = Windows::UI::Xaml::HorizontalAlignment::Stretch;
_chatPanelResizeBarGrid_->VerticalAlignment = Windows::UI::Xaml::VerticalAlignment::Top;
_chatPanelResizeBarGrid_->Width = std::numeric_limits<double>::quiet_NaN();
_chatPanelResizeBarGrid_->Height = 4;
Grid::SetColumn(_chatPanelGrid_, 0);
Grid::SetRow(_chatPanelGrid_, 2);
}
else {
chtBoxOrientation = Orientation::Horizontal;
_btnToggleOrientation_->Content = L"\uE90E";
_chatPanelResizeBarGrid_->HorizontalAlignment = Windows::UI::Xaml::HorizontalAlignment::Left;
_chatPanelResizeBarGrid_->VerticalAlignment = Windows::UI::Xaml::VerticalAlignment::Stretch;
_chatPanelResizeBarGrid_->Width = 4;
_chatPanelResizeBarGrid_->Height = std::numeric_limits<double>::quiet_NaN();
Grid::SetColumn(_chatPanelGrid_, 2);
Grid::SetRow(_chatPanelGrid_, 0);
}
if (wasChatPanelOpen)
openChatPanel();
}
void
VideoPage::_videoContent__SizeChanged(Platform::Object^ sender, Windows::UI::Xaml::SizeChangedEventArgs^ e)
{
if (isChatPanelOpen)
resizeChatPanel();
}
void
VideoPage::_messageTextBox__TextChanged(Platform::Object^ sender, TextChangedEventArgs^ e)
{
bool carriageReturnPressed = false;
if (_messageTextBox_->Text->Length() == (lastMessageText->Length() + 1) &&
_messageTextBox_->Text != "\r") {
unsigned cursorPos = 0;
auto strMessage = Utils::toString(_messageTextBox_->Text);
auto strLastMessage = Utils::toString(lastMessageText);
for (std::string::size_type i = 0; i < strLastMessage.size(); ++i) {
if (strMessage[i] != strLastMessage[i]) {
auto changed = strMessage.substr(i, 1);
if (changed == "\r") {
carriageReturnPressed = true;
MSG_("CR inside");
}
break;
}
}
if (strMessage.substr(strMessage.length() - 1) == "\r") {
if (lastMessageText->Length() != 0) {
carriageReturnPressed = true;
MSG_("CR at end");
}
}
}
if (carriageReturnPressed && !(RingD::instance->isCtrlPressed || RingD::instance->isShiftPressed)) {
_messageTextBox_->Text = lastMessageText;
sendMessage();
lastMessageText = "";
}
DependencyObject^ child = VisualTreeHelper::GetChild(_messageTextBox_, 0);
auto sendBtnElement = Utils::xaml::FindVisualChildByName(child, "_sendBtn_");
auto sendBtn = dynamic_cast<Button^>(sendBtnElement);
if (_messageTextBox_->Text != "") {
sendBtn->IsEnabled = true;
}
else {
sendBtn->IsEnabled = false;
}
lastMessageText = _messageTextBox_->Text;
}
void
VideoPage::_corewindow__KeyDown(CoreWindow^ sender, KeyEventArgs^ e)
{
auto ctrlState = CoreWindow::GetForCurrentThread()->GetKeyState(VirtualKey::Control);
auto isCtrlDown = ctrlState != CoreVirtualKeyStates::None;
if (isCtrlDown && e->VirtualKey == Windows::System::VirtualKey::I) {
_smartInfoBorder_->Visibility = _smartInfoBorder_->Visibility == VIS::Collapsed ? VIS::Visible : VIS::Collapsed;
}
}