From 50f18a3480f3ae97c992d3d14c01365d7f615ee5 Mon Sep 17 00:00:00 2001 From: Duc Le <duchuule@users.noreply.github.com> Date: Mon, 3 Aug 2015 13:36:25 +0000 Subject: [PATCH] clean up code: move App.xaml.h out of pch.h --- App.xaml.cpp | 5 +- App.xaml.h | 5 + CheatPane.xaml.h | 2 + Common/DeviceResources.cpp | 6 +- Converter.h | 2 +- Definitions.h | 60 +++++ DirectXPage.xaml.cpp | 2 +- EmulatorSettings.h | 15 ++ ImportPage.xaml | 12 +- ImportPage.xaml.cpp | 23 ++ ImportPage.xaml.h | 1 + SelectFilePane.xaml.h | 1 + SelectROMPane.xaml.cpp | 5 +- SelectROMPane.xaml.h | 59 +---- VBA10.vcxproj | 9 + VBA10.vcxproj.filters | 2 + live_connect.h | 502 +++++++++++++++++++++++++++++++++++++ packages.config | 4 + pch.h | 5 +- 19 files changed, 654 insertions(+), 66 deletions(-) create mode 100644 live_connect.h create mode 100644 packages.config diff --git a/App.xaml.cpp b/App.xaml.cpp index dedf049..fb743f7 100644 --- a/App.xaml.cpp +++ b/App.xaml.cpp @@ -4,8 +4,10 @@ // #include "pch.h" +#include "App.xaml.h" #include "DirectXPage.xaml.h" #include "EmulatorSettings.h" +#include "live_connect.h" using namespace VBA10; @@ -26,7 +28,7 @@ using namespace Windows::UI::Xaml::Navigation; using namespace Windows::UI::ViewManagement; ROMDatabase^ VBA10::App::ROMDB = nullptr; - +//web::live::live_client* LiveClient = nullptr; /// <summary> /// Initializes the singleton application object. This is the first line of authored code @@ -40,6 +42,7 @@ App::App() Resuming += ref new EventHandler<Object^>(this, &App::OnResuming); ROMDB = ref new ROMDatabase(); + //LiveClient = new web::live::live_client(); } /// <summary> diff --git a/App.xaml.h b/App.xaml.h index d5b9d69..77981c0 100644 --- a/App.xaml.h +++ b/App.xaml.h @@ -9,6 +9,7 @@ #include "DirectXPage.xaml.h" #include "Database\ROMDatabase.h" #include "EmulatorSettings.h" +#include "live_connect.h" namespace VBA10 { @@ -23,6 +24,9 @@ namespace VBA10 internal: static ROMDatabase^ ROMDB; + static live::live_client* LiveClient; + + private: @@ -31,5 +35,6 @@ namespace VBA10 DirectXPage^ m_directXPage; + }; } diff --git a/CheatPane.xaml.h b/CheatPane.xaml.h index 89af441..7778484 100644 --- a/CheatPane.xaml.h +++ b/CheatPane.xaml.h @@ -8,6 +8,8 @@ #include "CheatPane.g.h" #include "CheatData.h" +using namespace Platform::Collections; + namespace VBA10 { [Windows::Foundation::Metadata::WebHostHidden] diff --git a/Common/DeviceResources.cpp b/Common/DeviceResources.cpp index cd71c13..8d9c280 100644 --- a/Common/DeviceResources.cpp +++ b/Common/DeviceResources.cpp @@ -2,6 +2,8 @@ #include "DeviceResources.h" #include "DirectXHelper.h" #include <windows.ui.xaml.media.dxinterop.h> +#include <algorithm> + using namespace D2D1; using namespace DirectX; @@ -220,8 +222,8 @@ void DX::DeviceResources::CreateWindowSizeDependentResources() m_outputSize.Height = m_logicalSize.Height * m_compositionScaleY; // Prevent zero size DirectX content from being created. - m_outputSize.Width = max(m_outputSize.Width, 1); - m_outputSize.Height = max(m_outputSize.Height, 1); + m_outputSize.Width = std::max(m_outputSize.Width, 1.0f); + m_outputSize.Height = std::max(m_outputSize.Height, 1.0f); // The width and height of the swap chain must be based on the window's // natively-oriented width and height. If the window is not in the native diff --git a/Converter.h b/Converter.h index f690e77..00b079f 100644 --- a/Converter.h +++ b/Converter.h @@ -1,5 +1,5 @@ #pragma once -#include "pch.h" + #include <ppltasks.h> //IMPORTANT NOTE: need to include this header file in both the .h and .cpp of an xaml page diff --git a/Definitions.h b/Definitions.h index 5f8aad2..b4f5351 100644 --- a/Definitions.h +++ b/Definitions.h @@ -1,3 +1,63 @@ #pragma once #define DEFAULT_SNAPSHOT L"Assets/no_snapshot.png" + +using namespace Windows::Storage; + +namespace VBA10 +{ + public delegate void ROMSelectedDelegate(StorageFile ^file, StorageFolder ^folder); + + [Windows::UI::Xaml::Data::BindableAttribute] + public ref class StorageFolderModel sealed + { + private: + public: + property StorageFolder ^Folder; + + property Platform::String ^Name + { + Platform::String ^get() + { + return this->Folder->DisplayName; + } + } + + property Platform::String ^Path + { + Platform::String ^get() + { + return this->Folder->Path; + } + } + + StorageFolderModel(StorageFolder ^folder); + }; + + [Windows::UI::Xaml::Data::BindableAttribute] + public ref class StorageFileModel sealed + { + private: + public: + property StorageFile ^File; + property StorageFolder ^Folder; + + property Platform::String ^Name + { + Platform::String ^get() + { + return this->File->Name; + } + } + + property Platform::String ^Path + { + Platform::String ^get() + { + return this->Folder->Path; + } + } + + StorageFileModel(StorageFile ^file, StorageFolder ^folder); + }; +} \ No newline at end of file diff --git a/DirectXPage.xaml.cpp b/DirectXPage.xaml.cpp index d34222b..3147c9c 100644 --- a/DirectXPage.xaml.cpp +++ b/DirectXPage.xaml.cpp @@ -12,7 +12,7 @@ #include "Database\ROMDatabase.h" #include "Definitions.h" #include "stringhelper.h" - +#include "App.xaml.h" #include "NavMenuItem.h" #include "NavMenuListView.h" diff --git a/EmulatorSettings.h b/EmulatorSettings.h index 81e9950..6bb04dd 100644 --- a/EmulatorSettings.h +++ b/EmulatorSettings.h @@ -119,6 +119,19 @@ namespace VBA10 } } + //keep track of whether the user successfully signed in before or not (so that we tried to silently resign in) + property bool SignedIn + { + bool get() + { + return GetValueOrDefault<bool>(SignedInKey, SignedInDefault); + } + void set(bool value) + { + AddOrUpdateValue(SignedInKey, value); + } + } + #pragma region Button positions @@ -556,6 +569,7 @@ namespace VBA10 Platform::String^ TurboFrameSkipKey = "TurboFrameSkipKey"; Platform::String^ EnableTurboKey = "EnableTurboKey"; Platform::String^ FullScreenKey = "FullScreenKey"; + Platform::String^ SignedInKey = "SignedInKey"; #pragma region button positions Platform::String^ PadLeftPKey = "PadLeftPKey"; @@ -605,6 +619,7 @@ namespace VBA10 const int TurboFrameSkipDefault = 2; const bool EnableTurboDefault = false; const bool FullScreenDefault = false; + const bool SignedInDefault = false; #pragma region button positions (in cm based on 6x10cm phone) diff --git a/ImportPage.xaml b/ImportPage.xaml index 9b0c651..b85c3be 100644 --- a/ImportPage.xaml +++ b/ImportPage.xaml @@ -6,7 +6,8 @@ xmlns:ctl="using:VBA10.Controls" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - mc:Ignorable="d"> + mc:Ignorable="d" + Loaded="Page_Loaded"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Grid.RowDefinitions> @@ -66,10 +67,15 @@ <TextBlock TextWrapping="Wrap" Text="Use this option to import rom through OneDrive. The rom will be copied to the app's private storage." Margin=" 0, 4, 0, 4" /> + + <Button x:Name="SignInbtn" Content="Sign in" + Click="chooseFolderbtn_Click" + Width="248" + Margin =" 0, 4, 0, 4"/> - <TextBlock TextWrapping="Wrap" + <!--<TextBlock TextWrapping="Wrap" Text="This function will be added in a future release. In the mean time, please store roms on your phone's SD Card or documents folder and use the Local Storage option." - Margin=" 0, 4, 0, 4" /> + Margin=" 0, 4, 0, 4" />--> </StackPanel> </ScrollViewer> diff --git a/ImportPage.xaml.cpp b/ImportPage.xaml.cpp index ad51d53..929521f 100644 --- a/ImportPage.xaml.cpp +++ b/ImportPage.xaml.cpp @@ -8,10 +8,12 @@ #include "Database\ROMDBEntry.h" #include "Database\ROMDatabase.h" #include "SelectFilePane.xaml.h" +#include "App.xaml.h" #include "stringhelper.h" + using namespace VBA10; using namespace Platform; @@ -252,3 +254,24 @@ void ImportPage::importSavbtn_Click(Platform::Object^ sender, Windows::UI::Xaml: }); } + + +void ImportPage::Page_Loaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) +{ + //try re-sign in silently because access token expires every 1 hour + if (!EmulatorSettings::Current->SignedIn) + { + live::live_client* LiveClient = new live::live_client(); + LiveClient->login(L"wl.skydrive_update wl.signin") + .then([this](bool isLoggedIn) + { + if (!isLoggedIn) + { + throw std::exception(); + } + // Request the "me" object from OneDrive + // and pass the return value to the next continuation + //return model->LiveClient.get(L"me"); + }); + } +} diff --git a/ImportPage.xaml.h b/ImportPage.xaml.h index f191389..b4f622e 100644 --- a/ImportPage.xaml.h +++ b/ImportPage.xaml.h @@ -26,5 +26,6 @@ namespace VBA10 StorageFolder^ tmpfolder; Platform::String^ tmptoken; void importSavbtn_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); + void Page_Loaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); }; } diff --git a/SelectFilePane.xaml.h b/SelectFilePane.xaml.h index 5a830f8..3a830eb 100644 --- a/SelectFilePane.xaml.h +++ b/SelectFilePane.xaml.h @@ -9,6 +9,7 @@ using namespace Windows::Foundation::Collections; using namespace Platform::Collections; +using namespace Windows::Storage; namespace VBA10 { diff --git a/SelectROMPane.xaml.cpp b/SelectROMPane.xaml.cpp index ff0a483..da5e07b 100644 --- a/SelectROMPane.xaml.cpp +++ b/SelectROMPane.xaml.cpp @@ -4,14 +4,17 @@ // #include "pch.h" + #include "SelectROMPane.xaml.h" -#include <Windows.h> + #include "DirectXPage.xaml.h"; #include "EmulatorSettings.h"; #include "EmulatorFileHandler.h" #include "SelectStatePane.xaml.h" #include "Database\ROMDBEntry.h" #include "Converter.h" +#include "App.xaml.h" + using namespace VBA10; diff --git a/SelectROMPane.xaml.h b/SelectROMPane.xaml.h index ad37077..3c05114 100644 --- a/SelectROMPane.xaml.h +++ b/SelectROMPane.xaml.h @@ -7,63 +7,11 @@ #include "SelectROMPane.g.h" #include "Converter.h" +#include "Definitions.h" +#include "Database\ROMDBEntry.h" namespace VBA10 { - public delegate void ROMSelectedDelegate(StorageFile ^file, StorageFolder ^folder); - - [Windows::UI::Xaml::Data::BindableAttribute] - public ref class StorageFolderModel sealed - { - private: - public: - property StorageFolder ^Folder; - - property Platform::String ^Name - { - Platform::String ^get() - { - return this->Folder->DisplayName; - } - } - - property Platform::String ^Path - { - Platform::String ^get() - { - return this->Folder->Path; - } - } - - StorageFolderModel(StorageFolder ^folder); - }; - - [Windows::UI::Xaml::Data::BindableAttribute] - public ref class StorageFileModel sealed - { - private: - public: - property StorageFile ^File; - property StorageFolder ^Folder; - - property Platform::String ^Name - { - Platform::String ^get() - { - return this->File->Name; - } - } - - property Platform::String ^Path - { - Platform::String ^get() - { - return this->Folder->Path; - } - } - - StorageFileModel(StorageFile ^file, StorageFolder ^folder); - }; [Windows::Foundation::Metadata::WebHostHidden] public ref class SelectROMPane sealed @@ -104,4 +52,7 @@ namespace VBA10 void ShowContextMenu(ROMDBEntry^ entry, Windows::Foundation::Rect rect); void maximizebtn_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); }; + + + } diff --git a/VBA10.vcxproj b/VBA10.vcxproj index bec2053..4df9c5a 100644 --- a/VBA10.vcxproj +++ b/VBA10.vcxproj @@ -373,6 +373,7 @@ <ClInclude Include="Input.h" /> <ClInclude Include="Converter.h" /> <ClInclude Include="KeyboardInput.h" /> + <ClInclude Include="live_connect.h" /> <ClInclude Include="NavMenuItem.h" /> <ClInclude Include="NavMenuListView.h" /> <ClInclude Include="OnResizeBuffer.h" /> @@ -1117,6 +1118,7 @@ <None Include="Assets\vba-over.ini"> <DeploymentContent>true</DeploymentContent> </None> + <None Include="packages.config" /> <None Include="SpriteBatch.hlsli" /> <None Include="VBA10_StoreKey.pfx" /> <None Include="VBA10_TemporaryKey.pfx" /> @@ -1217,5 +1219,12 @@ <Import Project="$(VSINSTALLDIR)\Common7\IDE\Extensions\Microsoft\VsGraphics\ImageContentTask.targets" /> <Import Project="$(VSINSTALLDIR)\Common7\IDE\Extensions\Microsoft\VsGraphics\MeshContentTask.targets" /> <Import Project="$(VSINSTALLDIR)\Common7\IDE\Extensions\Microsoft\VsGraphics\ShaderGraphContentTask.targets" /> + <Import Project="packages\cpprestsdk.v140.winapp.msvcstl.dyn.rt-dyn.2.6.0\build\native\cpprestsdk.v140.winapp.msvcstl.dyn.rt-dyn.targets" Condition="Exists('packages\cpprestsdk.v140.winapp.msvcstl.dyn.rt-dyn.2.6.0\build\native\cpprestsdk.v140.winapp.msvcstl.dyn.rt-dyn.targets')" /> </ImportGroup> + <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> + <PropertyGroup> + <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> + </PropertyGroup> + <Error Condition="!Exists('packages\cpprestsdk.v140.winapp.msvcstl.dyn.rt-dyn.2.6.0\build\native\cpprestsdk.v140.winapp.msvcstl.dyn.rt-dyn.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\cpprestsdk.v140.winapp.msvcstl.dyn.rt-dyn.2.6.0\build\native\cpprestsdk.v140.winapp.msvcstl.dyn.rt-dyn.targets'))" /> + </Target> </Project> \ No newline at end of file diff --git a/VBA10.vcxproj.filters b/VBA10.vcxproj.filters index 418db6f..bcd4e7e 100644 --- a/VBA10.vcxproj.filters +++ b/VBA10.vcxproj.filters @@ -531,6 +531,7 @@ <ClInclude Include="EmulatorSettings.h" /> <ClInclude Include="PurchasePage.xaml.h" /> <ClInclude Include="AdControl.xaml.h" /> + <ClInclude Include="live_connect.h" /> </ItemGroup> <ItemGroup> <AppxManifest Include="Package.appxmanifest" /> @@ -548,6 +549,7 @@ </None> <None Include="Package.StoreAssociation.xml" /> <None Include="VBA10_StoreKey.pfx" /> + <None Include="packages.config" /> </ItemGroup> <ItemGroup> <Page Include="DirectXPage.xaml" /> diff --git a/live_connect.h b/live_connect.h new file mode 100644 index 0000000..699a102 --- /dev/null +++ b/live_connect.h @@ -0,0 +1,502 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* live_connect.h +* +* Simple API for connecting to Windows Live services, such as OneDrive and Hotmail. +* Only supported for App Store apps. +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +#pragma once + +#include "cpprest/http_client.h" +#include "cpprest/streams.h" +#include "cpprest/filestream.h" + + +namespace VBA10 { + namespace live { + +#if defined(__cplusplus_winrt) + + /// <summary> + /// This namespace contains symbols for the standard Windows Live permission scopes, which + /// determine what privileges the application has to access data. What operations are allowed + /// and what details are returned will depend on privileges requested and granted. + /// + /// See MSDN documentation for Live Connect at http://msdn.microsoft.com/en-us/live/ for details + /// on the precise meaning of these scopes. + /// </summary> + namespace scopes + { + static const utility::string_t wl_birthday = U("wl.birthday"); + static const utility::string_t wl_basic = U("wl.basic"); + static const utility::string_t wl_calendars = U("wl.calendars"); + static const utility::string_t wl_calendars_update = U("wl.calendars_update"); + static const utility::string_t wl_contacts_birthday = U("wl.contacts_birthday"); + static const utility::string_t wl_contacts_create = U("wl.contacts_create"); + static const utility::string_t wl_contacts_calendars = U("wl.contacts_calendars"); + static const utility::string_t wl_contacts_photos = U("wl.contacts_photos"); + static const utility::string_t wl_contacts_skydrive = U("wl.contacts_skydrive"); + static const utility::string_t wl_emails = U("wl.emails"); + static const utility::string_t wl_events_create = U("wl.events_create"); + static const utility::string_t wl_messenger = U("wl.messenger"); + static const utility::string_t wl_offline_access = U("wl.offline_access"); + static const utility::string_t wl_phone_numbers = U("wl.phone_numbers"); + static const utility::string_t wl_photos = U("wl.photos"); + static const utility::string_t wl_postal_addresses = U("wl.postal_addresses"); + static const utility::string_t wl_share = U("wl.share"); + static const utility::string_t wl_signin = U("wl.signin"); + static const utility::string_t wl_skydrive = U("wl.skydrive"); + static const utility::string_t wl_skydrive_update = U("wl.skydrive_update"); + static const utility::string_t wl_work_profile = U("wl.work_profile"); + } + + /// <summary> + /// Represents a session connected to Windows Live services like Calendar, Contacts, OneDrive, and the + /// user profile, by using the Live Connect REST API. It is a thin layer on top of the Casablanca HTTP + /// library, tailored to the usage scenarios for Windows Live clients. + /// </summary> + /// <remarks> + /// See http://msdn.microsoft.com/onedrive/ for details on using the OneDrive REST API. + /// + /// Note: when passing resource paths into the functions in this class, it is not necessary to add the + /// leading '/' path character. It will be added automatically when constructing the HTTP request. + /// + /// Most Windows Live requests will return data in the form of plain text or JSON. The APIs in this + /// class will typically return data as JSON. The most significant exception to this is download(), + /// which will not return any data to be extracted, it will place the result in the stream or file + /// passed into it. Also, remove() will return data only to provide error information: for non-error + /// cases, the JSON value will be <c>null</c>. + /// + /// This class relies on PPL tasks to represent asynchrony: the examples contained within the comments + /// show synchronous forms of invoking these APIs. In real use, applications should be relying on + /// .then() instead of .get() or .wait(). + /// </remarks> + class live_client + { + public: + /// <summary> + /// Constructs a new Windows Live client + /// </summary> + live_client() : + m_client(L"https://apis.live.net/v5.0/"), + m_authenticator(ref new Windows::Security::Authentication::OnlineId::OnlineIdAuthenticator) + { + } + + /// <summary> + /// Indicates whether the user is able to sign out of the app. + /// </summary> + bool can_sign_out() + { + return m_authenticator->CanSignOut; + } + + /// <summary> + /// Authenticates the current user and requests permission to access a listed Live services. + /// </summary> + /// <param name="services">A string containing a space-separated list of access scopes to request</param> + /// <returns><c>true</c> if authentication succeeded, <c>false</c> otherwise.</returns> + pplx::task<bool> login(utility::string_t scopes) + { + this->m_token.clear(); + + auto request = ref new Windows::Security::Authentication::OnlineId::OnlineIdServiceTicketRequest(ref new Platform::String(scopes.c_str()), "DELEGATION"); + + return pplx::create_task(m_authenticator->AuthenticateUserAsync(request)) + .then([this](concurrency::task<Windows::Security::Authentication::OnlineId::UserIdentity^> idtask) + { + try + { + auto ident = idtask.get(); + if (ident->Tickets->Size > 0) + { + auto ticket = ident->Tickets->GetAt(0); + + m_token = std::wstring(ticket->Value->Data()); + return true; + } + } + catch (const concurrency::task_canceled &) + { + + } + catch (const std::exception &) + { + + } + catch (Platform::Exception ^ex) + { + //std::wstring s = std::wstring(ex->Message->Data()); + + + } + return false; + }); + } + + /// <summary> + /// Authenticates the current user and requests permission to access a listed Live services. + /// </summary> + /// <param name="begin"> + /// The starting point of an iterator over a std::string collection, (for example a vector of + /// strings), holding the names of scopes to get permission for. + /// </param> + /// <param name="end">The end point for the same collection.</param> + /// <returns><c>true</c> if authentication succeeded, <c>false</c> otherwise.</returns> + template<typename Iter> + pplx::task<bool> login(Iter begin, Iter end) + { + utility::string_t services; + + for (Iter iter = begin; iter != end; ++iter) + { + services.append(*iter); + services.append(utility::string_t(L" ")); + } + + return login(services); + } + + /// <summary> + /// Dismisses any and all permissions that have been granted to the user. + /// </summary> + /// <returns><c>true</c> if logout succeeded, <c>false</c> otherwise.</returns> + /// <remarks> + /// Whether the logout attempt was successful or not, the application will + /// be required to log in again, since the access token will be cleared. + /// </remarks> + pplx::task<bool> logout() + { + this->m_token.clear(); + + if (!m_authenticator->CanSignOut) + return pplx::task_from_result(false); + + return pplx::create_task(m_authenticator->SignOutUserAsync()).then([this] + { + return true; + }); + } + + /// <summary> + /// Retrieves the access token in use by this client instance. + /// </summary> + /// <returns>The current token: a string. An invalid token is indicated by an empty string.</returns> + const utility::string_t& access_token() const + { + return m_token; + } + + /// <summary> + /// Retrieves data from Windows Live. + /// </summary> + /// <param name="resource">The Windows Live resource to retrieve.</param> + /// <returns>The JSON value resulting from the request.</returns> + /// <example>To get the current user profile: <code>json::value profile = live_clnt.get(L"me").get().extract_json().get();</code></example> + pplx::task<web::json::value> get(const utility::string_t& resource) + { + return _make_request(web::http::methods::GET, resource).then([](web::http::http_response response) { return _json_extract(response); }); + } + + /// <summary> + /// Deletes data from Windows Live. + /// </summary> + /// <param name="resource">The Windows Live resource to delete.</param> + /// <returns>The JSON value resulting from the request.</returns> + /// <example>To delete a file: <code>live_clnt.remove(L"file.NNNNNNNNNNNN").wait();</code>, where file.NNNNNNNNNNNN is a file identifier.</example> + pplx::task<web::json::value> remove(const utility::string_t& resource) + { + return _make_request(web::http::methods::DEL, resource).then([](web::http::http_response response) { return _json_extract(response); }); + } + + /// <summary> + /// Modifies data in Windows Live. + /// </summary> + /// <param name="resource">The Windows Live resource to modify.</param> + /// <returns>The JSON value resulting from the request.</returns> + /// <remarks>In most cases, the response will contain data about the modified resource.</remarks> + pplx::task<web::json::value> put(const utility::string_t& resource, const web::json::value& data) + { + return _make_request(web::http::methods::PUT, resource, data).then([](web::http::http_response response) { return _json_extract(response); }); + } + + /// <summary> + /// Adds data to Windows Live. + /// </summary> + /// <param name="resource">The Windows Live resource (such as a folder) where data should be added.</param> + /// <returns>The JSON value resulting from the request.</returns> + /// <remarks>In most cases, the response will contain data about the added resource.</remarks> + /// <example>To add a contact: <code>json::value contact = ...; live_clnt.post(L"me/contacts", contact).wait();</code></example> + pplx::task<web::json::value> post(const utility::string_t& resource, const web::json::value& data) + { + return _make_request(web::http::methods::POST, resource, data).then([](web::http::http_response response) { return _json_extract(response); }); + } + + /// <summary> + /// Copies data in Windows Live. + /// </summary> + /// <param name="resource">The Windows Live resource to copy.</param> + /// <param name="destination">The location where the resource copy should be placed.</param> + /// <returns>The JSON value resulting from the request.</returns> + /// <remarks>In most cases, the response will contain data about the added resource.</remarks> + /// <example>To copy a file: <code>live_clnt.copy(L"file.NNNNNNNNNNNN", L"folder.MMMMMMMMMMMM").wait();</code></example> + pplx::task<web::json::value> copy(const utility::string_t& resource, const utility::string_t& destination) + { + return _make_request(U("COPY"), resource, destination).then([](web::http::http_response response) { return _json_extract(response); }); + } + + /// <summary> + /// Moves data in Windows Live. + /// </summary> + /// <param name="resource">The Windows Live resource to move.</param> + /// <param name="destination">The location where the resource should be placed.</param> + /// <returns>The JSON value resulting from the request.</returns> + /// <remarks>In most cases, the response will contain data about the resource in its new location.</remarks> + /// <example>To copy a file: <code>live_clnt.copy(L"file.NNNNNNNNNNNN", L"folder.MMMMMMMMMMMM").wait();</code></example> + pplx::task<web::json::value> move(const utility::string_t& resource, const utility::string_t& destination) + { + return _make_request(U("MOVE"), resource, destination).then([](web::http::http_response response) { return _json_extract(response); }); + } + + /// <summary> + /// Download a file from OneDrive. + /// </summary> + /// <param name="file_id">The OneDrive file id to download.</param> + /// <param name="stream">A stream into which the contents of the file should be placed.</param> + /// <returns>The size of the downloaded resource.</returns> + /// <remarks></remarks> + /// <example>To download a file: <code>ostream stream = ...; live_clnt.download(L"file.NNNNNNNNNNNN", ostream).get().content_ready().wait();</code></example> + pplx::task<size_t> download(const utility::string_t& file_id, concurrency::streams::ostream stream) + { + web::http::uri_builder bldr; + bldr.append(file_id); + bldr.append_path(U("content")); + + web::http::http_request req(web::http::methods::GET); + req.set_request_uri(bldr.to_string()); + + return _make_request(req) + .then([stream](web::http::http_response response) -> pplx::task<size_t> + { + if (response.status_code() >= 400) + { + return response.extract_string().then( + [](utility::string_t message) -> pplx::task<size_t> + { + return pplx::task_from_exception<size_t>(std::exception(utility::conversions::to_utf8string(message).c_str())); + }); + } + return response.body().read_to_end(stream.streambuf()); + }); + } + + /// <summary> + /// Download a file from OneDrive. + /// </summary> + /// <param name="file_id">The OneDrive file id to download.</param> + /// <param name="file">A StorageFile reference identifying the target for the downloaded data.</param> + /// <returns>The size of the downloaded resource.</returns> + /// <remarks></remarks> + pplx::task<size_t> download(const utility::string_t& file_id, Windows::Storage::StorageFile^ file) + { + if (file == nullptr) + throw std::invalid_argument("file reference cannot be null"); + + return concurrency::streams::file_stream<uint8_t>::open_ostream(file).then( + [file_id, this](concurrency::streams::ostream stream) + { + web::http::uri_builder bldr; + bldr.append(file_id); + bldr.append_path(U("content")); + + web::http::http_request req(web::http::methods::GET); + req.set_request_uri(bldr.to_string()); + + return _make_request(req) + .then([stream](web::http::http_response response) -> pplx::task<size_t> + { + if (response.status_code() >= 400) + { + return response.extract_string().then( + [](utility::string_t message) -> pplx::task<size_t> + { + return pplx::task_from_exception<size_t>(std::exception(utility::conversions::to_utf8string(message).c_str())); + }); + } + return response.body().read_to_end(stream.streambuf()); + }) + .then([stream](pplx::task<size_t> ret_task) + { + return stream.flush().then([stream, ret_task]() + { + return stream.close(); + }).then([ret_task]() + { + return ret_task; + }); + }); + }); + } + + /// <summary> + /// Upload a file to OneDrive. + /// </summary> + /// <param name="path">The path of the file location in OneDrive. It should be of the form "folder.NNNNNNNNNN/files/file_name.ext"</param> + /// <param name="stream">A stream from which data for the file will be read.</param> + /// <param name="content_length">The size of the data to upload.</param> + /// <returns>The JSON value resulting from the request, containing metadata about the uploaded file.</returns> + /// <remarks> + /// The stream must contain at least as many bytes as indicated by 'content_length', starting from its current read position. + /// </remarks> + pplx::task<web::json::value> upload(const utility::string_t& path, concurrency::streams::istream stream, size_t content_length) + { + web::http::uri_builder bldr; + bldr.append(path); + + web::http::http_request req(web::http::methods::PUT); + req.set_request_uri(bldr.to_string()); + req.set_body(stream, content_length, U("")); + + return _make_request(req) + .then([](web::http::http_response response) + { + return _json_extract(response); + }); + } + + /// <summary> + /// Upload a file to OneDrive. + /// </summary> + /// <param name="path">The path of the file location in OneDrive. It should be of the form "folder.NNNNNNNNNN/files/file_name.ext"</param> + /// <param name="file">A StorageFile reference identifying the source of the uploaded data.</param> + /// <param name="content_length">The size of the data to upload.</param> + /// <returns>The JSON value resulting from the request, containing metadata about the uploaded file.</returns> + /// <remarks>The entire file will be uploaded.</remarks> + pplx::task<web::json::value> upload(const utility::string_t& path, Windows::Storage::StorageFile^ file) + { + if (file == nullptr) + throw std::invalid_argument("file reference cannot be null"); + + return pplx::create_task(file->GetBasicPropertiesAsync()).then( + [path, this, file](Windows::Storage::FileProperties::BasicProperties^ props) + { + if (props == nullptr) + throw std::exception("failed to retrieve file properties; cannot determine its size"); + size_t size = (size_t)props->Size; + + return concurrency::streams::file_stream<uint8_t>::open_istream(file).then( + [path, this, size](concurrency::streams::istream stream) + { + web::http::uri_builder bldr; + bldr.append(path); + + web::http::http_request req(web::http::methods::PUT); + req.set_request_uri(bldr.to_string()); + req.set_body(stream, size, U("")); + + return _make_request(req) + .then([](web::http::http_response response) + { + return response.content_ready(); + }) + .then([stream](pplx::task<web::http::http_response> response) + { + return stream.close().then([response]() + { + return _json_extract(response.get()); + }); + }); + }); + }); + } + + private: + + static pplx::task<web::json::value> _json_extract(web::http::http_response response) + { + switch (response.status_code()) + { + case web::http::status_codes::NoContent: + return pplx::task_from_result(web::json::value::null()); + default: + if (response.status_code() >= 400) + { + return response.extract_string().then( + [](utility::string_t message) -> pplx::task<web::json::value> + { + return pplx::task_from_exception<web::json::value>(std::exception(utility::conversions::to_utf8string(message).c_str())); + }); + } + return response.extract_json(); + } + } + + pplx::task<web::http::http_response> _make_request(web::http::method method, const utility::string_t& path) + { + web::http::uri_builder bldr; + bldr.append(path); + + web::http::http_request req(method); + req.set_request_uri(bldr.to_string()); + return _make_request(req); + } + + + pplx::task<web::http::http_response> _make_request(web::http::method method, const utility::string_t& path, const web::json::value& data) + { + web::http::uri_builder bldr; + bldr.append(path); + + web::http::http_request req(method); + req.set_request_uri(bldr.to_string()); + req.set_body(data); + return _make_request(req); + } + + pplx::task<web::http::http_response> _make_request(web::http::method method, const utility::string_t& path, const utility::string_t& destination) + { + web::http::uri_builder bldr; + bldr.append(path); + + web::json::value data; + data[U("destination")] = web::json::value::string(destination); + + web::http::http_request req(method); + req.set_request_uri(bldr.to_string()); + req.set_body(data); + return _make_request(req); + } + + pplx::task<web::http::http_response> _make_request(web::http::http_request req) + { + if (!m_token.empty()) + req.headers().add(U("Authorization"), U("Bearer ") + m_token); + return m_client.request(req); + } + + Windows::Security::Authentication::OnlineId::OnlineIdAuthenticator^ m_authenticator; + web::http::client::http_client m_client; + utility::string_t m_token; + }; +#endif + } +} \ No newline at end of file diff --git a/packages.config b/packages.config new file mode 100644 index 0000000..dd3634e --- /dev/null +++ b/packages.config @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<packages> + <package id="cpprestsdk.v140.winapp.msvcstl.dyn.rt-dyn" version="2.6.0" targetFramework="native" /> +</packages> \ No newline at end of file diff --git a/pch.h b/pch.h index 2aa4e98..683e27d 100644 --- a/pch.h +++ b/pch.h @@ -1,5 +1,5 @@ #pragma once - +#define NOMINMAX #include <wrl.h> #include <wrl/client.h> #include <d3d11_2.h> @@ -13,5 +13,4 @@ #include <agile.h> #include <concrt.h> #include <collection.h> -#include <ppltasks.h> -#include "App.xaml.h" \ No newline at end of file +#include <ppltasks.h> \ No newline at end of file