Skip to content
Snippets Groups Projects
Select Git revision
  • master
  • release/201811
  • release/201812
  • release/201901
  • release/201902
  • release/201903
  • release/201904
  • release/201905
  • release/201906
  • release/201908
  • release/201912
  • release/202001
  • release/202005
  • release/windows-test/201910
  • release/201808
  • wip/smartlist_refacto
  • wip/patches_poly_2017/JimmyHamel/MathieuGirouxHuppe
17 results

Account.cpp

Blame
  • 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;
        }
    }