Windows 10 Version 1803 - September 2018 Update
This commit is contained in:
Коммит
dca302ef95
|
@ -543,6 +543,7 @@ For additional Windows samples, see [Windows on GitHub](http://microsoft.github.
|
|||
<td><a href="Samples/WiFiDirectServices">Wi-Fi Direct services</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="Samples/HotspotAuthentication">Wi-Fi hotspot authentication</a></td>
|
||||
<td><a href="Samples/WiFiScan">Wi-Fi scanning</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
|
@ -136,7 +136,6 @@
|
|||
<Link>Shared\Logging\Extensions\MediaPlaybackListStringExtensions.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="Helpers\CommonLicenseRequest.cs" />
|
||||
<Compile Include="Helpers\MediaPlayerExtensions.cs" />
|
||||
<Compile Include="Helpers\PlayReadyHelper.cs" />
|
||||
<Compile Include="Controls\ContentSelector.xaml.cs">
|
||||
<DependentUpon>ContentSelector.xaml</DependentUpon>
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
|
||||
using Windows.Media.Core;
|
||||
using Windows.Media.Playback;
|
||||
|
||||
namespace SDKTemplate.Helpers
|
||||
{
|
||||
|
||||
public static class MediaPlayerExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows for disposal of the underlying MediaSources attached to a MediaPlayer, regardless
|
||||
/// of if a MediaSource or MediaPlaybackItem was passed to the MediaPlayer.
|
||||
///
|
||||
/// It is left to the app to implement a clean-up of the other possible IMediaPlaybackSource
|
||||
/// type, which is a MediaPlaybackList.
|
||||
///
|
||||
/// </summary>
|
||||
public static void DisposeSource(this MediaPlayer mediaPlayer)
|
||||
{
|
||||
if (mediaPlayer == null)
|
||||
return;
|
||||
|
||||
if (mediaPlayer.Source != null)
|
||||
{
|
||||
var source = mediaPlayer.Source as MediaSource;
|
||||
if (source != null)
|
||||
source.Dispose();
|
||||
|
||||
var item = mediaPlayer.Source as MediaPlaybackItem;
|
||||
if (item != null && item.Source != null)
|
||||
{
|
||||
if (item.BreakSchedule.PrerollBreak != null)
|
||||
{
|
||||
foreach (var mpItem in item.BreakSchedule.PrerollBreak.PlaybackList.Items)
|
||||
{
|
||||
mpItem.Source?.Dispose();
|
||||
}
|
||||
}
|
||||
if (item.BreakSchedule.MidrollBreaks.Count != 0)
|
||||
{
|
||||
foreach (var mrBreak in item.BreakSchedule.MidrollBreaks)
|
||||
{
|
||||
foreach (var mpItem in mrBreak.PlaybackList.Items)
|
||||
{
|
||||
mpItem.Source?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (item.BreakSchedule.PostrollBreak != null)
|
||||
{
|
||||
foreach (var mpItem in item.BreakSchedule.PostrollBreak.PlaybackList.Items)
|
||||
{
|
||||
mpItem.Source?.Dispose();
|
||||
}
|
||||
}
|
||||
item.Source.Dispose();
|
||||
}
|
||||
};
|
||||
mediaPlayer.Source = null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -33,9 +33,6 @@ namespace SDKTemplate
|
|||
var mp = mediaPlayerElement.MediaPlayer;
|
||||
if (mp != null)
|
||||
{
|
||||
// Explicitly disposing sources facilitates faster memory reclamation.
|
||||
mp.DisposeSource(); // From SDKTemplate.Helpers.MediaPlayerExtensions
|
||||
|
||||
// Ensure MediaPlayerElement drops its reference to MediaPlayer.
|
||||
mediaPlayerElement.SetMediaPlayer(null);
|
||||
|
||||
|
|
|
@ -48,7 +48,6 @@ namespace SDKTemplate
|
|||
|
||||
UnregisterHandlers(mediaPlayer);
|
||||
|
||||
mediaPlayer.DisposeSource();
|
||||
mediaPlayerElement.SetMediaPlayer(null);
|
||||
mediaPlayer.Dispose();
|
||||
}
|
||||
|
@ -133,7 +132,6 @@ namespace SDKTemplate
|
|||
private async Task<MediaPlaybackItem> LoadSourceFromUriAsync(Uri uri, HttpClient httpClient = null)
|
||||
{
|
||||
UnregisterHandlers(mediaPlayerElement.MediaPlayer);
|
||||
mediaPlayerElement.MediaPlayer?.DisposeSource();
|
||||
|
||||
AdaptiveMediaSourceCreationResult result = null;
|
||||
if (httpClient != null)
|
||||
|
|
|
@ -61,7 +61,6 @@ namespace SDKTemplate
|
|||
|
||||
UnregisterHandlers(mediaPlayer);
|
||||
|
||||
mediaPlayer.DisposeSource();
|
||||
mediaPlayerElement.SetMediaPlayer(null);
|
||||
mediaPlayer.Dispose();
|
||||
}
|
||||
|
@ -149,7 +148,6 @@ namespace SDKTemplate
|
|||
private async Task<MediaPlaybackItem> LoadSourceFromUriAsync(Uri uri, HttpClient httpClient = null)
|
||||
{
|
||||
UnregisterHandlers(mediaPlayerElement.MediaPlayer);
|
||||
mediaPlayerElement.MediaPlayer?.DisposeSource();
|
||||
|
||||
if (tokenMethod == AzureKeyAcquisitionMethod.AuthorizationHeader)
|
||||
{
|
||||
|
|
|
@ -55,7 +55,6 @@ namespace SDKTemplate
|
|||
|
||||
UnregisterHandlers(mediaPlayer);
|
||||
|
||||
mediaPlayer.DisposeSource();
|
||||
mediaPlayerElement.SetMediaPlayer(null);
|
||||
mediaPlayer.Dispose();
|
||||
}
|
||||
|
@ -134,7 +133,6 @@ namespace SDKTemplate
|
|||
private async Task<MediaPlaybackItem> LoadSourceFromUriAsync(Uri uri, HttpClient httpClient = null)
|
||||
{
|
||||
UnregisterHandlers(mediaPlayerElement.MediaPlayer);
|
||||
mediaPlayerElement.MediaPlayer?.DisposeSource();
|
||||
|
||||
AdaptiveMediaSourceCreationResult result = null;
|
||||
if (httpClient != null)
|
||||
|
|
|
@ -50,7 +50,6 @@ namespace SDKTemplate
|
|||
|
||||
UnregisterHandlers(mediaPlayer);
|
||||
|
||||
mediaPlayer.DisposeSource();
|
||||
mediaPlayerElement.SetMediaPlayer(null);
|
||||
mediaPlayer.Dispose();
|
||||
}
|
||||
|
@ -112,7 +111,6 @@ namespace SDKTemplate
|
|||
private async Task<MediaPlaybackItem> LoadSourceFromUriAsync(Uri uri, HttpClient httpClient = null)
|
||||
{
|
||||
UnregisterHandlers(mediaPlayerElement.MediaPlayer);
|
||||
mediaPlayerElement.MediaPlayer?.DisposeSource();
|
||||
|
||||
AdaptiveMediaSourceCreationResult result = null;
|
||||
if (httpClient != null)
|
||||
|
|
|
@ -48,7 +48,6 @@ namespace SDKTemplate
|
|||
|
||||
UnregisterHandlers(mediaPlayer);
|
||||
|
||||
mediaPlayer.DisposeSource();
|
||||
mediaPlayerElement.SetMediaPlayer(null);
|
||||
mediaPlayer.Dispose();
|
||||
}
|
||||
|
@ -108,7 +107,6 @@ namespace SDKTemplate
|
|||
private async Task<MediaPlaybackItem> LoadSourceFromUriAsync(Uri uri, HttpClient httpClient = null)
|
||||
{
|
||||
UnregisterHandlers(mediaPlayerElement.MediaPlayer);
|
||||
mediaPlayerElement.MediaPlayer?.DisposeSource();
|
||||
|
||||
AdaptiveMediaSourceCreationResult result = null;
|
||||
if (httpClient != null)
|
||||
|
|
|
@ -52,7 +52,6 @@ namespace SDKTemplate
|
|||
|
||||
UnregisterHandlers(mediaPlayer);
|
||||
|
||||
mediaPlayer.DisposeSource();
|
||||
mediaPlayerElement.SetMediaPlayer(null);
|
||||
mediaPlayer.Dispose();
|
||||
}
|
||||
|
@ -104,7 +103,6 @@ namespace SDKTemplate
|
|||
private async Task<MediaPlaybackItem> LoadSourceFromUriAsync(Uri uri, HttpClient httpClient = null)
|
||||
{
|
||||
UnregisterHandlers(mediaPlayerElement.MediaPlayer);
|
||||
mediaPlayerElement.MediaPlayer?.DisposeSource();
|
||||
|
||||
AdaptiveMediaSourceCreationResult result = null;
|
||||
if (httpClient != null)
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
</Properties>
|
||||
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.14393.0" MaxVersionTested="10.0.17134.0" />
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.15063.0" MaxVersionTested="10.0.17134.0" />
|
||||
</Dependencies>
|
||||
|
||||
<Resources>
|
||||
|
|
|
@ -55,7 +55,7 @@ namespace SDKTemplate
|
|||
[DllImport("api-ms-win-core-sysinfo-l1-2-1.dll")]
|
||||
static extern void GetSystemTimeAsFileTime(ref FILETIME lpSystemTimeAsFileTime);
|
||||
|
||||
[DllImport("api-ms-win-core-libraryloader-l2-1.dll")]
|
||||
[DllImport("api-ms-win-core-libraryloader-l2-1-0.dll", CharSet = CharSet.Unicode)]
|
||||
static extern IntPtr LoadPackagedLibrary(string filename, uint reserved);
|
||||
|
||||
public static ServiceViewModel Current { get; private set; }
|
||||
|
|
|
@ -31,6 +31,7 @@ using namespace Windows::UI::Xaml;
|
|||
|
||||
static const float sc_MaxZoom = 1.0f; // Restrict max zoom to 1:1 scale.
|
||||
static const unsigned int sc_MaxBytesPerPixel = 16; // Covers all supported image formats.
|
||||
static const float sc_nominalRefWhite = 80.0f; // Nominal white nits for sRGB and scRGB.
|
||||
|
||||
// 400 bins with gamma of 10 lets us measure luminance to within 10% error for any
|
||||
// luminance above ~1.5 nits, up to 1 million nits.
|
||||
|
@ -43,13 +44,13 @@ D2DAdvancedColorImagesRenderer::D2DAdvancedColorImagesRenderer(
|
|||
) :
|
||||
m_deviceResources(deviceResources),
|
||||
m_renderEffectKind(RenderEffectKind::None),
|
||||
m_imageInfo{},
|
||||
m_zoom(1.0f),
|
||||
m_minZoom(1.0f), // Dynamically calculated on window size.
|
||||
m_imageOffset(),
|
||||
m_pointerPos(),
|
||||
m_maxCLL(-1.0f),
|
||||
m_brightnessAdjust(1.0f),
|
||||
m_imageInfo{},
|
||||
m_isComputeSupported(false)
|
||||
{
|
||||
// Register to be notified if the GPU device is lost or recreated.
|
||||
|
@ -116,7 +117,9 @@ void D2DAdvancedColorImagesRenderer::SetRenderOptions(
|
|||
m_renderEffectKind = effect;
|
||||
m_brightnessAdjust = brightnessAdjustment;
|
||||
|
||||
UpdateWhiteLevelScale(m_brightnessAdjust, m_dispInfo->SdrWhiteLevelInNits);
|
||||
auto sdrWhite = m_dispInfo ? m_dispInfo->SdrWhiteLevelInNits : sc_nominalRefWhite;
|
||||
|
||||
UpdateWhiteLevelScale(m_brightnessAdjust, sdrWhite);
|
||||
|
||||
// Adjust the Direct2D effect graph based on RenderEffectKind.
|
||||
// Some RenderEffectKind values require us to apply brightness adjustment
|
||||
|
@ -338,7 +341,7 @@ void D2DAdvancedColorImagesRenderer::CreateHistogramResources()
|
|||
|
||||
histogramMatrix->SetInputEffect(0, m_histogramPrescale.Get());
|
||||
|
||||
float scale = sc_histMaxNits / 80.0f; // scRGB 1.0 is defined as 80 nits.
|
||||
float scale = sc_histMaxNits / sc_nominalRefWhite;
|
||||
|
||||
D2D1_MATRIX_5X4_F rgbtoYnorm = D2D1::Matrix5x4F(
|
||||
0.2126f / scale, 0, 0, 0,
|
||||
|
@ -652,8 +655,7 @@ void D2DAdvancedColorImagesRenderer::UpdateWhiteLevelScale(float brightnessAdjus
|
|||
case AdvancedColorKind::StandardDynamicRange:
|
||||
case AdvancedColorKind::WideColorGamut:
|
||||
default:
|
||||
// Nominal reference white of sRGB is 80 nits.
|
||||
scale = sdrWhiteLevel / 80.0f;
|
||||
scale = sdrWhiteLevel / sc_nominalRefWhite;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -710,7 +712,9 @@ void D2DAdvancedColorImagesRenderer::ComputeHdrMetadata()
|
|||
// Initialize with a sentinel value.
|
||||
m_maxCLL = -1.0f;
|
||||
|
||||
if (!m_isComputeSupported)
|
||||
// MaxCLL is not meaningful for SDR or WCG images.
|
||||
if ((!m_isComputeSupported) ||
|
||||
(m_imageInfo.imageKind != AdvancedColorKind::HighDynamicRange))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -722,8 +726,6 @@ void D2DAdvancedColorImagesRenderer::ComputeHdrMetadata()
|
|||
|
||||
auto ctx = m_deviceResources->GetD2DDeviceContext();
|
||||
|
||||
if (m_imageInfo.imageKind == AdvancedColorKind::HighDynamicRange)
|
||||
{
|
||||
ctx->BeginDraw();
|
||||
|
||||
ctx->DrawImage(m_histogramEffect.Get());
|
||||
|
@ -757,19 +759,19 @@ void D2DAdvancedColorImagesRenderer::ComputeHdrMetadata()
|
|||
}
|
||||
}
|
||||
|
||||
float binNorm = static_cast<float>(maxCLLbin + 1) / static_cast<float>(sc_histNumBins);
|
||||
float binNorm = static_cast<float>(maxCLLbin) / static_cast<float>(sc_histNumBins);
|
||||
m_maxCLL = powf(binNorm, 1 / sc_histGamma) * sc_histMaxNits;
|
||||
}
|
||||
else
|
||||
{
|
||||
// MaxCLL is not meaningful for SDR or WCG images.
|
||||
}
|
||||
|
||||
// Some drivers have a bug where histogram will always return 0. Treat this as unknown.
|
||||
m_maxCLL = (m_maxCLL == 0.0f) ? -1.0f : m_maxCLL;
|
||||
}
|
||||
|
||||
// Set HDR10 metadata to allow HDR displays to optimize behavior based on our content.
|
||||
void D2DAdvancedColorImagesRenderer::EmitHdrMetadata()
|
||||
{
|
||||
if (m_dispInfo->CurrentAdvancedColorKind == AdvancedColorKind::HighDynamicRange)
|
||||
auto acKind = m_dispInfo ? m_dispInfo->CurrentAdvancedColorKind : AdvancedColorKind::StandardDynamicRange;
|
||||
|
||||
if (acKind == AdvancedColorKind::HighDynamicRange)
|
||||
{
|
||||
DXGI_HDR_METADATA_HDR10 metadata = {};
|
||||
|
||||
|
|
|
@ -67,9 +67,24 @@ DirectXPage::DirectXPage() :
|
|||
DisplayInformation::DisplayContentsInvalidated +=
|
||||
ref new TypedEventHandler<DisplayInformation^, Object^>(this, &DirectXPage::OnDisplayContentsInvalidated);
|
||||
|
||||
AdvancedColorInfo^ acInfo = nullptr;
|
||||
try
|
||||
{
|
||||
currentDisplayInformation->AdvancedColorInfoChanged +=
|
||||
ref new TypedEventHandler<DisplayInformation^, Object^>(this, &DirectXPage::OnAdvancedColorInfoChanged);
|
||||
|
||||
acInfo = currentDisplayInformation->GetAdvancedColorInfo();
|
||||
}
|
||||
catch (COMException^ e)
|
||||
{
|
||||
// In Windows 10 1803, accessing AdvancedColorInfo or registering the event handler while connected over
|
||||
// remote desktop will throw E_FAIL. This is fixed in future versions of Windows.
|
||||
if (e->HResult != E_FAIL)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
swapChainPanel->CompositionScaleChanged +=
|
||||
ref new TypedEventHandler<SwapChainPanel^, Object^>(this, &DirectXPage::OnCompositionScaleChanged);
|
||||
|
||||
|
@ -107,7 +122,8 @@ DirectXPage::DirectXPage() :
|
|||
|
||||
m_renderer = std::unique_ptr<D2DAdvancedColorImagesRenderer>(new D2DAdvancedColorImagesRenderer(m_deviceResources));
|
||||
|
||||
UpdateDisplayACState(currentDisplayInformation->GetAdvancedColorInfo());
|
||||
// Even if AdvancedColorInfo is not available, run the change handler anyway to set default values.
|
||||
UpdateDisplayACState(acInfo);
|
||||
}
|
||||
|
||||
DirectXPage::~DirectXPage()
|
||||
|
@ -175,18 +191,17 @@ void DirectXPage::LoadImage(_In_ StorageFile^ imageFile)
|
|||
});
|
||||
}
|
||||
|
||||
void DirectXPage::UpdateDisplayACState(_In_ AdvancedColorInfo^ info)
|
||||
void DirectXPage::UpdateDisplayACState(_In_opt_ AdvancedColorInfo^ info)
|
||||
{
|
||||
// Render options are meaningless when this method is first called, as no image has been loaded yet.
|
||||
// Therefore it doesn't matter what value we use for oldDispKind in this case.
|
||||
// Fill in default display info values if AdvancedColorInfo is not available yet.
|
||||
// For example, if the image hasn't been loaded.
|
||||
auto oldDispKind = m_dispInfo ? m_dispInfo->CurrentAdvancedColorKind : AdvancedColorKind::StandardDynamicRange;
|
||||
auto newDispKind = info->CurrentAdvancedColorKind;
|
||||
m_dispInfo = info;
|
||||
auto newDispKind = info ? info->CurrentAdvancedColorKind : AdvancedColorKind::StandardDynamicRange;
|
||||
m_dispInfo = info ? info : m_dispInfo;
|
||||
auto maxcll = info ? static_cast<int>(info->MaxLuminanceInNits) : 0;
|
||||
|
||||
DisplayACState->Text = L"Kind: " + ConvertACKindToString(newDispKind);
|
||||
|
||||
unsigned int maxcll = static_cast<unsigned int>(info->MaxLuminanceInNits);
|
||||
|
||||
if (maxcll == 0)
|
||||
{
|
||||
// Luminance value of 0 means that no valid data was provided by the display.
|
||||
|
@ -207,7 +222,6 @@ void DirectXPage::UpdateDisplayACState(_In_ AdvancedColorInfo^ info)
|
|||
// If display has changed kind between SDR/HDR/WCG, we must reset all rendering options.
|
||||
UpdateDefaultRenderOptions();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// UI element event handlers.
|
||||
|
@ -269,8 +283,9 @@ void DirectXPage::UpdateDefaultRenderOptions()
|
|||
break;
|
||||
|
||||
case AdvancedColorKind::HighDynamicRange:
|
||||
auto acKind = m_dispInfo ? m_dispInfo->CurrentAdvancedColorKind : AdvancedColorKind::StandardDynamicRange;
|
||||
|
||||
switch (m_dispInfo->CurrentAdvancedColorKind)
|
||||
switch (acKind)
|
||||
{
|
||||
case AdvancedColorKind::StandardDynamicRange:
|
||||
case AdvancedColorKind::WideColorGamut:
|
||||
|
@ -345,7 +360,19 @@ void DirectXPage::OnDisplayContentsInvalidated(_In_ DisplayInformation^ sender,
|
|||
|
||||
void DirectXPage::OnAdvancedColorInfoChanged(_In_ DisplayInformation ^sender, _In_ Object ^args)
|
||||
{
|
||||
try
|
||||
{
|
||||
UpdateDisplayACState(sender->GetAdvancedColorInfo());
|
||||
}
|
||||
catch (COMException^ e)
|
||||
{
|
||||
// In Windows 10 1803, accessing AdvancedColorInfo or registering the event handler while connected over
|
||||
// remote desktop will throw E_FAIL. This is fixed in future versions of Windows.
|
||||
if (e->HResult != E_FAIL)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ namespace D2DAdvancedColorImages
|
|||
void SliderChanged(_In_ Platform::Object^ sender, _In_ Windows::UI::Xaml::Controls::Primitives::RangeBaseValueChangedEventArgs^ e);
|
||||
void ComboChanged(_In_ Platform::Object^ sender, _In_ Windows::UI::Xaml::Controls::SelectionChangedEventArgs^ e);
|
||||
|
||||
void UpdateDisplayACState(_In_ Windows::Graphics::Display::AdvancedColorInfo^ info);
|
||||
void UpdateDisplayACState(_In_opt_ Windows::Graphics::Display::AdvancedColorInfo^ info);
|
||||
void UpdateDefaultRenderOptions();
|
||||
void UpdateRenderOptions();
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
|
||||
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
|
||||
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
|
||||
xmlns:desktop4="http://schemas.microsoft.com/appx/manifest/desktop/windows10/4"
|
||||
IgnorableNamespaces="uap mp"
|
||||
>
|
||||
<Identity Name="Microsoft.SDKSamples.D2DAdvancedColorImages.CPP" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" Version="1.0.0.0"/>
|
||||
|
@ -19,7 +20,7 @@
|
|||
<Resource Language="x-generate"/>
|
||||
</Resources>
|
||||
<Applications>
|
||||
<Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="D2DAdvancedColorImages.App">
|
||||
<Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="D2DAdvancedColorImages.App" desktop4:SupportsMultipleInstances="true">
|
||||
<uap:VisualElements
|
||||
DisplayName="Direct2D Advanced Color Images C++ Sample"
|
||||
Square150x150Logo="Assets\squaretile-sdk.png"
|
||||
|
|
|
@ -157,6 +157,9 @@
|
|||
<ClInclude Include="Scenario4_SaveFile.xaml.h">
|
||||
<DependentUpon>..\shared\Scenario4_SaveFile.xaml</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Scenario5_TriggerCFU.xaml.h">
|
||||
<DependentUpon>..\shared\Scenario5_TriggerCFU.xaml</DependentUpon>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ApplicationDefinition Include="..\..\..\SharedContent\xaml\App.xaml">
|
||||
|
@ -172,6 +175,7 @@
|
|||
<Page Include="..\..\..\SharedContent\xaml\Styles.xaml">
|
||||
<Link>Styles\Styles.xaml</Link>
|
||||
</Page>
|
||||
<Page Include="..\shared\Scenario5_TriggerCFU.xaml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AppxManifest Include="Package.appxmanifest">
|
||||
|
@ -206,6 +210,9 @@
|
|||
<ClCompile Include="Scenario4_SaveFile.xaml.cpp">
|
||||
<DependentUpon>..\shared\Scenario4_SaveFile.xaml</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Scenario5_TriggerCFU.xaml.cpp">
|
||||
<DependentUpon>..\shared\Scenario5_TriggerCFU.xaml</DependentUpon>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="..\..\..\SharedContent\media\microsoft-sdk.png">
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
<ClCompile Include="Scenario2_MultiFile.xaml.cpp" />
|
||||
<ClCompile Include="Scenario3_SingleFolder.xaml.cpp" />
|
||||
<ClCompile Include="Scenario4_SaveFile.xaml.cpp" />
|
||||
<ClCompile Include="Scenario5_TriggerCFU.xaml.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
|
@ -31,6 +32,7 @@
|
|||
<ClInclude Include="Scenario2_MultiFile.xaml.h" />
|
||||
<ClInclude Include="Scenario3_SingleFolder.xaml.h" />
|
||||
<ClInclude Include="Scenario4_SaveFile.xaml.h" />
|
||||
<ClInclude Include="Scenario5_TriggerCFU.xaml.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AppxManifest Include="Package.appxmanifest" />
|
||||
|
@ -44,6 +46,7 @@
|
|||
<Page Include="..\shared\Scenario2_MultiFile.xaml" />
|
||||
<Page Include="..\shared\Scenario3_SingleFolder.xaml" />
|
||||
<Page Include="..\shared\Scenario4_SaveFile.xaml" />
|
||||
<Page Include="..\shared\Scenario5_TriggerCFU.xaml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="..\..\..\SharedContent\media\microsoft-sdk.png">
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
</Properties>
|
||||
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.10240.0" MaxVersionTested="10.0.17134.0" />
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17134.0" MaxVersionTested="10.0.17134.0" />
|
||||
</Dependencies>
|
||||
|
||||
<Resources>
|
||||
|
|
|
@ -12,4 +12,5 @@ Platform::Array<Scenario>^ MainPage::scenariosInner = ref new Platform::Array<Sc
|
|||
{ "Pick multiple files", "SDKTemplate.Scenario2" },
|
||||
{ "Pick a folder", "SDKTemplate.Scenario3" },
|
||||
{ "Save a file", "SDKTemplate.Scenario4" },
|
||||
{ "Trigger CFU", "SDKTemplate.Scenario5" }
|
||||
};
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
//
|
||||
// Scenario5_TriggerCFU.xaml.cpp
|
||||
// Implementation of the Scenario5_TriggerCFU class
|
||||
//
|
||||
|
||||
#include "pch.h"
|
||||
#include "Scenario5_TriggerCFU.xaml.h"
|
||||
|
||||
using namespace SDKTemplate;
|
||||
|
||||
using namespace concurrency;
|
||||
using namespace Platform;
|
||||
using namespace Platform::Collections;
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::Storage;
|
||||
using namespace Windows::Storage::AccessCache;
|
||||
using namespace Windows::Storage::Pickers;
|
||||
using namespace Windows::Storage::Provider;
|
||||
using namespace Windows::UI::Xaml;
|
||||
|
||||
Scenario5::Scenario5()
|
||||
{
|
||||
InitializeComponent();
|
||||
UpdateButtons();
|
||||
}
|
||||
|
||||
void Scenario5::UpdateButtons()
|
||||
{
|
||||
WriteToFileButton->IsEnabled = (m_afterWriteFile != nullptr);
|
||||
WriteToFileWithCFUButton->IsEnabled = (m_afterWriteFile != nullptr);
|
||||
|
||||
SaveToFutureAccessListButton->IsEnabled = (m_beforeReadFile != nullptr);
|
||||
GetFileFromFutureAccessListButton->IsEnabled = !m_faToken->IsEmpty();
|
||||
}
|
||||
|
||||
void Scenario5::CreateFileButton_Click(Object^ sender, RoutedEventArgs^ e)
|
||||
{
|
||||
rootPage->NotifyUser("", NotifyType::StatusMessage);
|
||||
|
||||
FileSavePicker^ savePicker = ref new FileSavePicker();
|
||||
savePicker->SuggestedStartLocation = PickerLocationId::DocumentsLibrary;
|
||||
|
||||
savePicker->FileTypeChoices->Insert("Plain Text", ref new Vector<String^>{ ".txt" });
|
||||
savePicker->SuggestedFileName = "New Document";
|
||||
|
||||
create_task(savePicker->PickSaveFileAsync()).then([this](StorageFile^ file)
|
||||
{
|
||||
m_afterWriteFile = file;
|
||||
if (m_afterWriteFile)
|
||||
{
|
||||
rootPage->NotifyUser("File created.", NotifyType::StatusMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
rootPage->NotifyUser("Operation cancelled.", NotifyType::ErrorMessage);
|
||||
}
|
||||
UpdateButtons();
|
||||
});
|
||||
}
|
||||
|
||||
void Scenario5::WriteToFileButton_Click(Object^ sender, RoutedEventArgs^ e)
|
||||
{
|
||||
create_task(FileIO::WriteTextAsync(m_afterWriteFile, "The File Picker App just wrote to the file!")).then([this]()
|
||||
{
|
||||
rootPage->NotifyUser("File write complete. If applicable, a WriteActivationMode.AfterWrite activation will occur in approximately 60 seconds on desktop.", NotifyType::StatusMessage);
|
||||
});
|
||||
}
|
||||
|
||||
void Scenario5::WriteToFileWithExplicitCFUButton_Click(Object^ sender, RoutedEventArgs^ e)
|
||||
{
|
||||
StorageFile^ file = m_afterWriteFile;
|
||||
CachedFileManager::DeferUpdates(file);
|
||||
create_task(FileIO::WriteTextAsync(file, "The File Picker App just wrote to the file!")).then([this, file]()
|
||||
{
|
||||
rootPage->NotifyUser("File write complete. Explicitly completing updates.", NotifyType::StatusMessage);
|
||||
return create_task(CachedFileManager::CompleteUpdatesAsync(file));
|
||||
}).then([this, file](FileUpdateStatus status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case FileUpdateStatus::Complete:
|
||||
rootPage->NotifyUser("File " + file->Name + " was saved.", NotifyType::StatusMessage);
|
||||
break;
|
||||
|
||||
case FileUpdateStatus::CompleteAndRenamed:
|
||||
rootPage->NotifyUser("File " + file->Name + " was renamed and saved.", NotifyType::StatusMessage);
|
||||
break;
|
||||
|
||||
default:
|
||||
rootPage->NotifyUser("File " + file->Name + " couldn't be saved.", NotifyType::ErrorMessage);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Scenario5::PickAFileButton_Click(Object^ sender, RoutedEventArgs^ e)
|
||||
{
|
||||
rootPage->NotifyUser("", NotifyType::StatusMessage);
|
||||
|
||||
FileOpenPicker^ openPicker = ref new FileOpenPicker();
|
||||
openPicker->ViewMode = PickerViewMode::Thumbnail;
|
||||
openPicker->FileTypeFilter->Append(".txt");
|
||||
|
||||
create_task(openPicker->PickSingleFileAsync()).then([this](StorageFile^ file)
|
||||
{
|
||||
m_beforeReadFile = file;
|
||||
if (file)
|
||||
{
|
||||
rootPage->NotifyUser("Picked file: " + file->Name, NotifyType::StatusMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
rootPage->NotifyUser("Operation cancelled.", NotifyType::ErrorMessage);
|
||||
}
|
||||
UpdateButtons();
|
||||
});
|
||||
}
|
||||
|
||||
void Scenario5::SaveToFutureAccessListButton_Click(Object^ sender, RoutedEventArgs^ e)
|
||||
{
|
||||
m_faToken = StorageApplicationPermissions::FutureAccessList->Add(m_beforeReadFile);
|
||||
rootPage->NotifyUser("Saved to Future Access List", NotifyType::StatusMessage);
|
||||
m_beforeReadFile = nullptr;
|
||||
UpdateButtons();
|
||||
}
|
||||
|
||||
void Scenario5::GetFileFromFutureAccessListButton_Click(Object^ sender, RoutedEventArgs^ e)
|
||||
{
|
||||
rootPage->NotifyUser("Getting the file from the Future Access List. If applicable, a ReadActivationMode.BeforeAccess activation will occur in approximately 60 seconds on desktop.", NotifyType::StatusMessage);
|
||||
create_task(StorageApplicationPermissions::FutureAccessList->GetFileAsync(m_faToken)).then([this](StorageFile^ file)
|
||||
{
|
||||
if (file)
|
||||
{
|
||||
rootPage->NotifyUser("Retrieved file from Future Access List: " + file->Name, NotifyType::StatusMessage);
|
||||
m_beforeReadFile = file;
|
||||
}
|
||||
else
|
||||
{
|
||||
rootPage->NotifyUser("Unable to retrieve file from Future Access List.", NotifyType::ErrorMessage);
|
||||
}
|
||||
UpdateButtons();
|
||||
});
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
//
|
||||
// Scenario5_TriggerCFU.xaml.h
|
||||
// Declaration of the Scenario5_TriggerCFU class
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "pch.h"
|
||||
#include "Scenario5_TriggerCFU.g.h"
|
||||
#include "MainPage.xaml.h"
|
||||
|
||||
namespace SDKTemplate
|
||||
{
|
||||
public ref class Scenario5 sealed
|
||||
{
|
||||
public:
|
||||
Scenario5();
|
||||
|
||||
private:
|
||||
MainPage^ rootPage = MainPage::Current;
|
||||
Windows::Storage::StorageFile^ m_afterWriteFile = nullptr;
|
||||
Windows::Storage::StorageFile^ m_beforeReadFile = nullptr;
|
||||
Platform::String^ m_faToken;
|
||||
|
||||
void UpdateButtons();
|
||||
void CreateFileButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
|
||||
void WriteToFileButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
|
||||
void WriteToFileWithExplicitCFUButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
|
||||
void SaveToFutureAccessListButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
|
||||
void GetFileFromFutureAccessListButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
|
||||
void PickAFileButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
|
||||
};
|
||||
}
|
|
@ -115,6 +115,9 @@
|
|||
<Compile Include="Scenario4_SaveFile.xaml.cs">
|
||||
<DependentUpon>Scenario4_SaveFile.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Scenario5_TriggerCFU.xaml.cs">
|
||||
<DependentUpon>Scenario5_TriggerCFU.xaml</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AppxManifest Include="Package.appxmanifest">
|
||||
|
@ -152,6 +155,11 @@
|
|||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="..\shared\Scenario5_TriggerCFU.xaml">
|
||||
<Link>Scenario5_TriggerCFU.xaml</Link>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="..\..\..\SharedContent\xaml\Styles.xaml">
|
||||
<Link>Styles\Styles.xaml</Link>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
</Properties>
|
||||
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.10240.0" MaxVersionTested="10.0.17134.0" />
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17134.0" MaxVersionTested="10.0.17134.0" />
|
||||
</Dependencies>
|
||||
|
||||
<Resources>
|
||||
|
|
|
@ -25,6 +25,7 @@ namespace SDKTemplate
|
|||
new Scenario() { Title = "Pick multiple files", ClassType = typeof(Scenario2) },
|
||||
new Scenario() { Title = "Pick a folder", ClassType = typeof(Scenario3) },
|
||||
new Scenario() { Title = "Save a file", ClassType = typeof(Scenario4) },
|
||||
new Scenario() { Title = "Trigger CFU", ClassType = typeof(Scenario5) },
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
|
||||
using System;
|
||||
using Windows.Storage;
|
||||
using Windows.Storage.AccessCache;
|
||||
using Windows.Storage.Pickers;
|
||||
using Windows.Storage.Provider;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace SDKTemplate
|
||||
{
|
||||
public sealed partial class Scenario5 : Page
|
||||
{
|
||||
MainPage rootPage = MainPage.Current;
|
||||
|
||||
StorageFile m_afterWriteFile = null;
|
||||
StorageFile m_beforeReadFile = null;
|
||||
string m_faToken;
|
||||
|
||||
public Scenario5()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
UpdateButtons();
|
||||
}
|
||||
|
||||
private void UpdateButtons()
|
||||
{
|
||||
WriteToFileButton.IsEnabled = (m_afterWriteFile != null);
|
||||
WriteToFileWithCFUButton.IsEnabled = (m_afterWriteFile != null);
|
||||
|
||||
SaveToFutureAccessListButton.IsEnabled = (m_beforeReadFile != null);
|
||||
GetFileFromFutureAccessListButton.IsEnabled = !string.IsNullOrEmpty(m_faToken);
|
||||
}
|
||||
|
||||
private async void CreateFileButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
rootPage.NotifyUser("", NotifyType.StatusMessage);
|
||||
|
||||
FileSavePicker savePicker = new FileSavePicker();
|
||||
savePicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
|
||||
|
||||
savePicker.FileTypeChoices.Add("Plain Text", new string[] { ".txt" });
|
||||
savePicker.SuggestedFileName = "New Document";
|
||||
|
||||
m_afterWriteFile = await savePicker.PickSaveFileAsync();
|
||||
if (m_afterWriteFile != null)
|
||||
{
|
||||
rootPage.NotifyUser("File created.", NotifyType.StatusMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
rootPage.NotifyUser("Operation cancelled.", NotifyType.ErrorMessage);
|
||||
}
|
||||
UpdateButtons();
|
||||
}
|
||||
|
||||
private async void WriteToFileButton_Click(Object sender, RoutedEventArgs e)
|
||||
{
|
||||
await FileIO.WriteTextAsync(m_afterWriteFile, "The File Picker App just wrote to the file!");
|
||||
rootPage.NotifyUser("File write complete. If applicable, a WriteActivationMode.AfterWrite activation will occur in approximately 60 seconds on desktop.", NotifyType.StatusMessage);
|
||||
}
|
||||
|
||||
private async void WriteToFileWithExplicitCFUButton_Click(Object sender, RoutedEventArgs e)
|
||||
{
|
||||
StorageFile file = m_afterWriteFile;
|
||||
CachedFileManager.DeferUpdates(file);
|
||||
await FileIO.WriteTextAsync(file, "The File Picker App just wrote to the file!");
|
||||
rootPage.NotifyUser("File write complete. Explicitly completing updates.", NotifyType.StatusMessage);
|
||||
FileUpdateStatus status = await CachedFileManager.CompleteUpdatesAsync(file);
|
||||
switch (status)
|
||||
{
|
||||
case FileUpdateStatus.Complete:
|
||||
rootPage.NotifyUser($"File {file.Name} was saved.", NotifyType.StatusMessage);
|
||||
break;
|
||||
|
||||
case FileUpdateStatus.CompleteAndRenamed:
|
||||
rootPage.NotifyUser($"File ${file.Name} was renamed and saved.", NotifyType.StatusMessage);
|
||||
break;
|
||||
|
||||
default:
|
||||
rootPage.NotifyUser($"File ${file.Name} couldn't be saved.", NotifyType.ErrorMessage);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private async void PickAFileButton_Click(Object sender, RoutedEventArgs e)
|
||||
{
|
||||
rootPage.NotifyUser("", NotifyType.StatusMessage);
|
||||
|
||||
FileOpenPicker openPicker = new FileOpenPicker();
|
||||
openPicker.ViewMode = PickerViewMode.Thumbnail;
|
||||
openPicker.FileTypeFilter.Add(".txt");
|
||||
|
||||
StorageFile file = await openPicker.PickSingleFileAsync();
|
||||
if (file != null)
|
||||
{
|
||||
m_beforeReadFile = file;
|
||||
rootPage.NotifyUser($"Picked file: {file.Name}", NotifyType.StatusMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_beforeReadFile = null;
|
||||
rootPage.NotifyUser("Operation cancelled.", NotifyType.ErrorMessage);
|
||||
}
|
||||
UpdateButtons();
|
||||
}
|
||||
|
||||
private void SaveToFutureAccessListButton_Click(Object sender, RoutedEventArgs e)
|
||||
{
|
||||
m_faToken = StorageApplicationPermissions.FutureAccessList.Add(m_beforeReadFile);
|
||||
rootPage.NotifyUser("Saved to Future Access List", NotifyType.StatusMessage);
|
||||
m_beforeReadFile = null;
|
||||
UpdateButtons();
|
||||
}
|
||||
|
||||
private async void GetFileFromFutureAccessListButton_Click(Object sender, RoutedEventArgs e)
|
||||
{
|
||||
rootPage.NotifyUser("Getting the file from the Future Access List. If applicable, a ReadActivationMode.BeforeAccess activation will occur in approximately 60 seconds on desktop.", NotifyType.StatusMessage);
|
||||
StorageFile file = await StorageApplicationPermissions.FutureAccessList.GetFileAsync(m_faToken);
|
||||
if (file != null)
|
||||
{
|
||||
rootPage.NotifyUser($"Retrieved file from Future Access List: {file.Name}", NotifyType.StatusMessage);
|
||||
m_beforeReadFile = file;
|
||||
}
|
||||
else
|
||||
{
|
||||
rootPage.NotifyUser("Unable to retrieve file from Future Access List.", NotifyType.ErrorMessage);
|
||||
}
|
||||
UpdateButtons();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -61,6 +61,7 @@
|
|||
<Content Include="html\scenario2_MultiFile.html" />
|
||||
<Content Include="html\scenario3_SingleFolder.html" />
|
||||
<Content Include="html\scenario4_SaveFile.html" />
|
||||
<Content Include="html\scenario5_TriggerCFU.html" />
|
||||
<Content Include="..\..\..\SharedContent\media\microsoft-sdk.png">
|
||||
<Link>images\microsoft-sdk.png</Link>
|
||||
</Content>
|
||||
|
@ -93,6 +94,7 @@
|
|||
<Content Include="js\scenario2_MultiFile.js" />
|
||||
<Content Include="js\scenario3_SingleFolder.js" />
|
||||
<Content Include="js\scenario4_SaveFile.js" />
|
||||
<Content Include="js\scenario5_TriggerCFU.js" />
|
||||
<Content Include="..\..\..\SharedContent\js\Microsoft.WinJS\css\ui-dark.css">
|
||||
<Link>Microsoft.WinJS.4.0\css\ui-dark.css</Link>
|
||||
</Content>
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
</Properties>
|
||||
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.10240.0" MaxVersionTested="10.0.17134.0" />
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17134.0" MaxVersionTested="10.0.17134.0" />
|
||||
</Dependencies>
|
||||
|
||||
<Resources>
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
<!--
|
||||
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
||||
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
||||
PARTICULAR PURPOSE.
|
||||
|
||||
Copyright (c) Microsoft Corporation. All rights reserved
|
||||
-->
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
|
||||
<head>
|
||||
<title></title>
|
||||
<script src="/js/scenario5_TriggerCFU.js"></script>
|
||||
</head>
|
||||
|
||||
<body class="win-type-body">
|
||||
<h2 id="sampleHeader" class="win-type-subheader">Description:</h2>
|
||||
<div id="scenarioDescription">
|
||||
Writes to a file and optionally notifies the CachedFileManager.
|
||||
</div>
|
||||
<p>
|
||||
<button id="CreateFileButton" class="win-button">Create a file</button>
|
||||
</p>
|
||||
<p>
|
||||
<button id="WriteToFileButton" class="win-button">Write to file</button>
|
||||
</p>
|
||||
<p>
|
||||
<button id="WriteToFileWithCFUButton" class="win-button">Write to file and notify CachedFileManager</button>
|
||||
</p>
|
||||
|
||||
<h2 id="sampleHeader" class="win-type-subheader">Description:</h2>
|
||||
<div id="scenarioDescription">
|
||||
Opens a file and saves it to the Future Access list, then gets it from the Future Access List.
|
||||
</div>
|
||||
<p>
|
||||
<button id="PickAFileButton" class="win-button">Open a file</button>
|
||||
</p>
|
||||
<p>
|
||||
<button id="SaveToFutureAccessListButton" class="win-button">Save file to Future Access list</button>
|
||||
</p>
|
||||
<p>
|
||||
<button id="GetFileFromFutureAccessListButton" class="win-button">Get file from Future Access List</button>
|
||||
</p>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -13,6 +13,9 @@
|
|||
}, {
|
||||
url: "/html/scenario4_SaveFile.html",
|
||||
title: "Save a file"
|
||||
}, {
|
||||
url: "/html/scenario5_TriggerCFU.html",
|
||||
title: "Trigger CFU"
|
||||
}];
|
||||
WinJS.Namespace.define("SdkSample", {
|
||||
sampleTitle: sampleTitle,
|
||||
|
|
|
@ -30,17 +30,17 @@
|
|||
// Prevent updates to the remote version of the file until we finish making changes and call CompleteUpdatesAsync.
|
||||
Windows.Storage.CachedFileManager.deferUpdates(file);
|
||||
// write to file
|
||||
Windows.Storage.FileIO.writeTextAsync(file, file.name).done(function () {
|
||||
Windows.Storage.FileIO.writeTextAsync(file, file.name).then(function () {
|
||||
// Let Windows know that we're finished changing the file so the other app can update the remote version of the file.
|
||||
// Completing updates may require Windows to ask for user input.
|
||||
Windows.Storage.CachedFileManager.completeUpdatesAsync(file).done(function (updateStatus) {
|
||||
return Windows.Storage.CachedFileManager.completeUpdatesAsync(file);
|
||||
}).done(function (updateStatus) {
|
||||
if (updateStatus === Windows.Storage.Provider.FileUpdateStatus.complete) {
|
||||
WinJS.log && WinJS.log("File " + file.name + " was saved.", "sample", "status");
|
||||
} else {
|
||||
WinJS.log && WinJS.log("File " + file.name + " couldn't be saved.", "sample", "status");
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
WinJS.log && WinJS.log("Operation cancelled.", "sample", "status");
|
||||
}
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
//// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
||||
//// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||
//// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
||||
//// PARTICULAR PURPOSE.
|
||||
////
|
||||
//// Copyright (c) Microsoft Corporation. All rights reserved
|
||||
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
// Elements
|
||||
var writeToFileButton;
|
||||
var writeToFileWithCFUButton;
|
||||
var saveToFutureAccessListButton;
|
||||
var getFileFromFutureAccessListButton;
|
||||
|
||||
// Scenario state variables
|
||||
var beforeReadFile;
|
||||
var afterWriteFile;
|
||||
var faToken;
|
||||
|
||||
var page = WinJS.UI.Pages.define("/html/scenario5_TriggerCFU.html", {
|
||||
ready: function (element, options) {
|
||||
writeToFileButton = document.getElementById("WriteToFileButton");
|
||||
writeToFileWithCFUButton = document.getElementById("WriteToFileWithCFUButton");
|
||||
saveToFutureAccessListButton = document.getElementById("SaveToFutureAccessListButton");
|
||||
getFileFromFutureAccessListButton = document.getElementById("GetFileFromFutureAccessListButton");
|
||||
|
||||
document.getElementById("CreateFileButton").addEventListener("click", createFile);
|
||||
writeToFileButton.addEventListener("click", writeToFile);
|
||||
writeToFileWithCFUButton.addEventListener("click", writeToFileWithExplicitCFU);
|
||||
document.getElementById("PickAFileButton").addEventListener("click", pickAFile);
|
||||
saveToFutureAccessListButton.addEventListener("click", saveToFutureAccessList);
|
||||
getFileFromFutureAccessListButton.addEventListener("click", getFileFromFutureAccessList);
|
||||
|
||||
updateButtons();
|
||||
}
|
||||
});
|
||||
|
||||
function updateButtons() {
|
||||
writeToFileButton.disabled = !afterWriteFile;
|
||||
writeToFileWithCFUButton.disabled = !afterWriteFile;
|
||||
|
||||
saveToFutureAccessListButton.disabled = !beforeReadFile;
|
||||
getFileFromFutureAccessListButton.disabled = !faToken;
|
||||
}
|
||||
|
||||
function createFile() {
|
||||
WinJS.log && WinJS.log("", "sample", "status");
|
||||
|
||||
// Create the picker object and set options
|
||||
var savePicker = new Windows.Storage.Pickers.FileSavePicker();
|
||||
savePicker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.documentsLibrary;
|
||||
// Dropdown of file types the user can save the file as
|
||||
savePicker.fileTypeChoices.insert("Plain Text", [".txt"]);
|
||||
// Default file name if the user does not type one in or select a file to replace
|
||||
savePicker.suggestedFileName = "New Document";
|
||||
|
||||
savePicker.pickSaveFileAsync().done(function (file) {
|
||||
afterWriteFile = file;
|
||||
if (afterWriteFile) {
|
||||
WinJS.log && WinJS.log("File created.", "sample", "status");
|
||||
} else {
|
||||
WinJS.log && WinJS.log("Operation cancelled.", "sample", "status");
|
||||
}
|
||||
updateButtons();
|
||||
});
|
||||
}
|
||||
|
||||
function writeToFile() {
|
||||
Windows.Storage.FileIO.writeTextAsync(afterWriteFile, "The File Picker App just wrote to the file!").done(function () {
|
||||
WinJS.log && WinJS.log("File write complete. If applicable, a WriteActivationMode.AfterWrite activation will occur in approximately 60 seconds on desktop.", "sample", "status");
|
||||
});
|
||||
}
|
||||
|
||||
function writeToFileWithExplicitCFU() {
|
||||
var file = afterWriteFile;
|
||||
Windows.Storage.CachedFileManager.deferUpdates(file);
|
||||
Windows.Storage.FileIO.writeTextAsync(file, "The File Picker App just wrote to the file!").then(function () {
|
||||
WinJS.log && WinJS.log("File write complete. Explicitly completing updates.", "sample", "status");
|
||||
return Windows.Storage.CachedFileManager.completeUpdatesAsync(file);
|
||||
}).done(function (status) {
|
||||
switch (status) {
|
||||
case Windows.Storage.Provider.FileUpdateStatus.complete:
|
||||
WinJS.log && WinJS.log(`File ${file.name} was saved.`, "sample", "status");
|
||||
break;
|
||||
|
||||
case Windows.Storage.Provider.FileUpdateStatus.completeAndRenamed:
|
||||
WinJS.log && WinJS.log(`File ${file.name} was renamed and saved.`, "sample", "status");
|
||||
break;
|
||||
|
||||
default:
|
||||
WinJS.log && WinJS.log(`File ${file.name} couldn't be saved.`, "sample", "error");
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function pickAFile() {
|
||||
WinJS.log && WinJS.log("", "sample", "status");
|
||||
|
||||
var openPicker = new Windows.Storage.Pickers.FileOpenPicker();
|
||||
openPicker.viewMode = Windows.Storage.Pickers.PickerViewMode.thumbnail;
|
||||
openPicker.fileTypeFilter.push(".txt");
|
||||
|
||||
openPicker.pickSingleFileAsync().done(function (file) {
|
||||
beforeReadFile = file;
|
||||
if (file) {
|
||||
WinJS.log && WinJS.log(`Picked file: ${file.name}`, "sample", "status");
|
||||
} else {
|
||||
WinJS.log && WinJS.log("Operation cancelled.", "sample", "error");
|
||||
}
|
||||
updateButtons();
|
||||
});
|
||||
}
|
||||
|
||||
function saveToFutureAccessList() {
|
||||
faToken = Windows.Storage.AccessCache.StorageApplicationPermissions.futureAccessList.add(beforeReadFile);
|
||||
WinJS.log && WinJS.log("Saved to Future Access List", "sample", "status");
|
||||
beforeReadFile = null;
|
||||
updateButtons();
|
||||
}
|
||||
|
||||
function getFileFromFutureAccessList() {
|
||||
WinJS.log && WinJS.log("Getting the file from the Future Access List. If applicable, a ReadActivationMode.BeforeAccess activation will occur in approximately 60 seconds on desktop.", "sample", "status");
|
||||
Windows.Storage.AccessCache.StorageApplicationPermissions.futureAccessList.getFileAsync(faToken).done(function (file) {
|
||||
if (file) {
|
||||
WinJS.log && WinJS.log(`Retrieved file from Future Access List: ${file.name}`, "sample", "status");
|
||||
beforeReadFile = file;
|
||||
} else {
|
||||
WinJS.log && WinJS.log("Unable to retrieve file from Future Access List.", "sample", "error");
|
||||
}
|
||||
updateButtons();
|
||||
});
|
||||
}
|
||||
})();
|
|
@ -0,0 +1,41 @@
|
|||
<!--
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
-->
|
||||
<Page
|
||||
x:Class="SDKTemplate.Scenario5"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
<ScrollViewer Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Padding="12,10,12,12">
|
||||
<StackPanel>
|
||||
<TextBlock Text="Description:" Style="{StaticResource SampleHeaderTextStyle}"/>
|
||||
<TextBlock Style="{StaticResource ScenarioDescriptionTextStyle}" TextWrapping="Wrap">
|
||||
Writes to a file and optionally notifies the CachedFileManager.
|
||||
</TextBlock>
|
||||
|
||||
<Button Content="Create a file" Click="CreateFileButton_Click" Margin="0,10,0,0"/>
|
||||
<Button x:Name="WriteToFileButton" Content="Write to file" Click="WriteToFileButton_Click" Margin="0,10,0,0" />
|
||||
<Button x:Name="WriteToFileWithCFUButton" Content="Write to file and notify CachedFileManager" Click="WriteToFileWithExplicitCFUButton_Click" Margin="0,10,0,0"/>
|
||||
|
||||
<TextBlock Text="Description:" Style="{StaticResource SampleHeaderTextStyle}" Margin="0,20,0,0"/>
|
||||
<TextBlock Style="{StaticResource ScenarioDescriptionTextStyle}" TextWrapping="Wrap">
|
||||
Opens a file and saves it to the Future Access list, then gets it from the Future Access List.
|
||||
</TextBlock>
|
||||
|
||||
<Button Content="Open a file" Click="PickAFileButton_Click" Margin="0,10,0,0" />
|
||||
<Button x:Name="SaveToFutureAccessListButton" Content="Save file to Future Access List" Click="SaveToFutureAccessListButton_Click" Margin="0,10,0,0" />
|
||||
<Button x:Name="GetFileFromFutureAccessListButton" Content="Get file from Future Access List" Click="GetFileFromFutureAccessListButton_Click" Margin="0,10,0,0" />
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</Page>
|
|
@ -52,8 +52,9 @@ namespace FilePickerContracts
|
|||
|
||||
private async void AddFileButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
StorageFile file = await Windows.Storage.ApplicationData.Current.LocalFolder.CreateFileAsync(@"CachedFile.txt", CreationCollisionOption.ReplaceExisting);
|
||||
StorageFile file = await Windows.Storage.ApplicationData.Current.LocalFolder.CreateFileAsync(@"BeforeAccessConflictingFile.txt", CreationCollisionOption.ReplaceExisting);
|
||||
await Windows.Storage.FileIO.WriteTextAsync(file, @"Cached file created...");
|
||||
// Note: BeforeAccess triggers CFU activations only for files accessed from the Most Recently Used list and Future Access list.
|
||||
CachedFileUpdater.SetUpdateInformation(file, "CachedFile", ReadActivationMode.BeforeAccess, WriteActivationMode.NotNeeded, CachedFileOptions.RequireUpdateOnAccess);
|
||||
|
||||
bool inBasket;
|
||||
|
|
|
@ -47,6 +47,7 @@ void Scenario1_Basic::ToggleFullScreenModeButton_Click(Platform::Object^ sender,
|
|||
{
|
||||
view->ExitFullScreenMode();
|
||||
rootPage->NotifyUser("Exiting full screen mode", NotifyType::StatusMessage);
|
||||
isLastKnownFullScreen = false;
|
||||
// The SizeChanged event will be raised when the exit from full screen mode is complete.
|
||||
}
|
||||
else
|
||||
|
@ -54,6 +55,7 @@ void Scenario1_Basic::ToggleFullScreenModeButton_Click(Platform::Object^ sender,
|
|||
if (view->TryEnterFullScreenMode())
|
||||
{
|
||||
rootPage->NotifyUser("Entering full screen mode", NotifyType::StatusMessage);
|
||||
isLastKnownFullScreen = true;
|
||||
// The SizeChanged event will be raised when the entry to full screen mode is complete.
|
||||
}
|
||||
else
|
||||
|
@ -87,6 +89,14 @@ void Scenario1_Basic::UpdateContent()
|
|||
ToggleFullScreenModeSymbol->Symbol = isFullScreenMode ? Symbol::BackToWindow : Symbol::FullScreen;
|
||||
ReportFullScreenMode->Text = isFullScreenMode ? "is in" : "is not in";
|
||||
FullScreenOptionsPanel->Visibility = isFullScreenMode ? Windows::UI::Xaml::Visibility::Visible : Windows::UI::Xaml::Visibility::Collapsed;
|
||||
|
||||
// Did the system force a change in full screen mode?
|
||||
if (isLastKnownFullScreen != isFullScreenMode)
|
||||
{
|
||||
isLastKnownFullScreen = isFullScreenMode;
|
||||
// Clear any stray messages that talked about the mode we are no longer in.
|
||||
rootPage->NotifyUser("", NotifyType::StatusMessage);
|
||||
}
|
||||
}
|
||||
|
||||
void Scenario1_Basic::OnKeyDown(Platform::Object^ sender, Windows::UI::Xaml::Input::KeyRoutedEventArgs^ e)
|
||||
|
@ -98,6 +108,7 @@ void Scenario1_Basic::OnKeyDown(Platform::Object^ sender, Windows::UI::Xaml::Inp
|
|||
{
|
||||
view->ExitFullScreenMode();
|
||||
rootPage->NotifyUser("Exited full screen mode due to keypress", NotifyType::StatusMessage);
|
||||
isLastKnownFullScreen = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,10 @@ namespace SDKTemplate
|
|||
private:
|
||||
MainPage^ rootPage;
|
||||
|
||||
// What is the program's last known full-screen state?
|
||||
// We use this to detect when the system forced us out of full-screen mode.
|
||||
bool isLastKnownFullScreen = Windows::UI::ViewManagement::ApplicationView::GetForCurrentView()->IsFullScreenMode;
|
||||
|
||||
void ToggleFullScreenModeButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
|
||||
void ShowStandardSystemOverlaysButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
|
||||
void UseMinimalOverlaysCheckBox_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
|
||||
|
|
|
@ -22,6 +22,10 @@ namespace SDKTemplate
|
|||
{
|
||||
private MainPage rootPage;
|
||||
|
||||
// What is the program's last known full-screen state?
|
||||
// We use this to detect when the system forced us out of full-screen mode.
|
||||
private bool isLastKnownFullScreen = ApplicationView.GetForCurrentView().IsFullScreenMode;
|
||||
|
||||
public Scenario1_Basic()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
@ -49,6 +53,7 @@ namespace SDKTemplate
|
|||
{
|
||||
view.ExitFullScreenMode();
|
||||
rootPage.NotifyUser("Exiting full screen mode", NotifyType.StatusMessage);
|
||||
isLastKnownFullScreen = false;
|
||||
// The SizeChanged event will be raised when the exit from full screen mode is complete.
|
||||
}
|
||||
else
|
||||
|
@ -56,6 +61,7 @@ namespace SDKTemplate
|
|||
if (view.TryEnterFullScreenMode())
|
||||
{
|
||||
rootPage.NotifyUser("Entering full screen mode", NotifyType.StatusMessage);
|
||||
isLastKnownFullScreen = true;
|
||||
// The SizeChanged event will be raised when the entry to full screen mode is complete.
|
||||
}
|
||||
else
|
||||
|
@ -89,6 +95,14 @@ namespace SDKTemplate
|
|||
ToggleFullScreenModeSymbol.Symbol = isFullScreenMode ? Symbol.BackToWindow : Symbol.FullScreen;
|
||||
ReportFullScreenMode.Text = isFullScreenMode ? "is in" : "is not in";
|
||||
FullScreenOptionsPanel.Visibility = isFullScreenMode ? Visibility.Visible : Visibility.Collapsed;
|
||||
|
||||
// Did the system force a change in full screen mode?
|
||||
if (isLastKnownFullScreen != isFullScreenMode)
|
||||
{
|
||||
isLastKnownFullScreen = isFullScreenMode;
|
||||
// Clear any stray messages that talked about the mode we are no longer in.
|
||||
rootPage.NotifyUser("", NotifyType.StatusMessage);
|
||||
}
|
||||
}
|
||||
|
||||
void OnKeyDown(object sender, KeyRoutedEventArgs e)
|
||||
|
@ -100,6 +114,7 @@ namespace SDKTemplate
|
|||
{
|
||||
view.ExitFullScreenMode();
|
||||
rootPage.NotifyUser("Exited full screen mode due to keypress", NotifyType.StatusMessage);
|
||||
isLastKnownFullScreen = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,10 @@
|
|||
var FullScreenSystemOverlayMode = ViewManagement.FullScreenSystemOverlayMode;
|
||||
var ApplicationView = ViewManagement.ApplicationView;
|
||||
|
||||
// What is the program's last known full-screen state?
|
||||
// We use this to detect when the system forced us out of full-screen mode.
|
||||
var isLastKnownFullScreen = ApplicationView.getForCurrentView().isFullScreenMode;
|
||||
|
||||
var page = WinJS.UI.Pages.define("/html/scenario1-basic.html", {
|
||||
ready: function (element, options) {
|
||||
toggleFullScreenMode.addEventListener("click", onToggleFullScreenMode);
|
||||
|
@ -29,10 +33,12 @@
|
|||
if (view.isFullScreenMode) {
|
||||
view.exitFullScreenMode();
|
||||
WinJS.log && WinJS.log("Exiting full screen mode", "samples", "status");
|
||||
isLastKnownFullScreen = false;
|
||||
// The resize event will be raised when the exit from full screen mode is complete.
|
||||
} else {
|
||||
if (view.tryEnterFullScreenMode()) {
|
||||
WinJS.log && WinJS.log("Entering full screen mode", "samples", "status");
|
||||
isLastKnownFullScreen = true;
|
||||
// The resize event will be raised when the entry to full screen mode is complete.
|
||||
} else {
|
||||
WinJS.log && WinJS.log("Failed to enter full screen mode", "samples", "error");
|
||||
|
@ -56,14 +62,23 @@
|
|||
toggleFullScreenMode.innerText = WinJS.UI.AppBarIcon[isFullScreenMode ? "backtowindow" : "fullscreen"];
|
||||
reportFullScreenMode.innerText = isFullScreenMode ? "is in" : "is not in";
|
||||
fullScreenOptions.style.display = isFullScreenMode ? "block" : "none";
|
||||
|
||||
// Did the system force a change in full screen mode?
|
||||
if (isLastKnownFullScreen !== isFullScreenMode) {
|
||||
isLastKnownFullScreen = isFullScreenMode;
|
||||
// Clear any stray messages that talked about the mode we are no longer in.
|
||||
WinJS.log && WinJS.log("", "samples", "status");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function onkeydown(e) {
|
||||
if (e.key === "Esc") {
|
||||
if (e.key === "Escape") {
|
||||
var view = ApplicationView.getForCurrentView();
|
||||
if (view.isFullScreenMode) {
|
||||
view.exitFullScreenMode();
|
||||
WinJS.log && WinJS.log("Exited full screen mode due to keypress", "samples", "status");
|
||||
isLastKnownFullScreen = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
<!---
|
||||
category: NetworkingAndWebServices
|
||||
samplefwlink: http://go.microsoft.com/fwlink/p/?LinkId=2019043
|
||||
--->
|
||||
|
||||
# Hotspot authentication sample
|
||||
|
||||
Shows how to provision and register a hotspot profile and perform either WISPr or custom authentication.
|
||||
|
||||
> **Note:** This sample is part of a large collection of UWP feature samples.
|
||||
> If you are unfamiliar with Git and GitHub, you can download the entire collection as a
|
||||
> [ZIP file](https://github.com/Microsoft/Windows-universal-samples/archive/master.zip), but be
|
||||
> sure to unzip everything to access shared dependencies. For more info on working with the ZIP file,
|
||||
> the samples collection, and GitHub, see [Get the UWP samples from GitHub](https://aka.ms/ovu2uq).
|
||||
> For more samples, see the [Samples portal](https://aka.ms/winsamples) on the Windows Dev Center.
|
||||
|
||||
This sample demonstrates the following operations:
|
||||
|
||||
- Provision a hotspot profile and register the corresponding background task.
|
||||
- Authenticate as a foreground app with default WISPr authentication or custom authentication.
|
||||
- Authenticate in a background task
|
||||
|
||||
The background task requires that the profile be provisioned and registered.
|
||||
The task is triggered once the Wi-Fi is connected to the hotspot as a part of post Wi-Fi connection.
|
||||
When a device initially connects, the hotspot usually has a limited connection,
|
||||
and post-connection authentication is done by a background task to enable the full Internet connection.
|
||||
|
||||
In order to invoke the hotspot authentication flow (background task),
|
||||
the access point being connected to must provide a captive portal response
|
||||
with WISPr support claimed in the XML blob being returned to the client.
|
||||
|
||||
Authentication can be completed in one of two ways.
|
||||
- Issuing WISPr credentials using IssueCredentialsAsync API, which uses the default WISPr implementation.
|
||||
- Performing a custom WISPr authentication using the information obtained through HotspotAuthenticationContext.TryGetAuthenticationContext. In this case, you must call HotspotAuthenticationContext.SkipAuthentication API to skip the default WISPr authentication process once the custom authentication is complete.
|
||||
|
||||
## Related topics
|
||||
|
||||
### Conceptual
|
||||
|
||||
[Windows 8 hotspot authentication sample](https://code.msdn.microsoft.com/windowsapps/Wi-Fi-hotspot-authenticatio-943569eb)
|
||||
|
||||
### Reference
|
||||
|
||||
[Mobile Broadband on the Windows Hardware Dev Center](https://docs.microsoft.com/en-us/windows-hardware/drivers/mobilebroadband/index)
|
||||
|
||||
[HotspotAuthenticationContext](https://docs.microsoft.com/en-us/uwp/api/Windows.Networking.NetworkOperators.HotspotAuthenticationContext)
|
||||
|
||||
[HotspotAuthenticationEventDetails ](https://docs.microsoft.com/en-us/uwp/api/Windows.Networking.NetworkOperators.HotspotAuthenticationEventDetails)
|
||||
|
||||
[IBackgroundTask](https://docs.microsoft.com/en-us/uwp/api/Windows.ApplicationModel.Background.IBackgroundTask)
|
||||
|
||||
[IBackgroundTaskInstance](https://docs.microsoft.com/en-us/uwp/api/Windows.ApplicationModel.Background.IBackgroundTaskInstance)
|
||||
|
||||
## System requirements
|
||||
|
||||
The system must have a Wi-Fi adapter for this sample to be meaningful.
|
||||
|
||||
**Client:** Windows 10
|
||||
|
||||
**Server:** Windows Server 2016 Technical Preview
|
||||
|
||||
**Phone:** Windows 10
|
||||
|
||||
## Build the sample
|
||||
|
||||
1. If you download the samples ZIP, be sure to unzip the entire archive, not just the folder with the sample you want to build.
|
||||
2. Start Microsoft Visual Studio 2017 and select **File** \> **Open** \> **Project/Solution**.
|
||||
3. Starting in the folder where you unzipped the samples, go to the Samples subfolder, then the subfolder for this specific sample, then the subfolder for the programming language. Double-click the Visual Studio Solution (.sln) file.
|
||||
4. Press Ctrl+Shift+B, or select **Build** \> **Build Solution**.
|
||||
|
||||
## Run the sample
|
||||
|
||||
The Wi-Fi hotspot authentication sample has following requirements:
|
||||
|
||||
Windows must trigger the application when authenticating with a certain hotspot. This is achieved by provisioning a WLAN profile with a corresponding configuration.
|
||||
|
||||
1. The app must declare background task and corresponding entry point must match with the actual background task.
|
||||
2. The app must declare networkConnectionManagerProvisioning capability to provision hotspot profile xml. The capability belongs to restrictedcapabilities namespace and needs to be manually added to the Package.appxmanifest file since Visaul Studio UI doesn't support it.
|
||||
3. The provisioning XML built into this application must be modified to match the hotspot's SSID.
|
||||
|
||||
The steps for completing these requirements and running the sample are described below:
|
||||
|
||||
1. Modify the Provisioning Metadata XML file to match your hotspot by opening ProvisioningData.xml and adjusting the following fields:
|
||||
|
||||
* CarrierProvisioning\Global\CarrierID: If writing a mobile broadband operator app, use the same GUID that you specified as the Service Number in the metadata authoring wizard.
|
||||
If writing a hotspot-only app, generate a new GUID with Visual Studio. On the Tools menu, click Create GUID. Click Copy to transfer the new GUID to the Clipboard.
|
||||
|
||||
* CarrierProvisioning\Global\SubscriberID: If writing a mobile broadband operator app, use the International Mobile Subscriber Identity (IMSI) of the mobile broadband SIM.
|
||||
If writing a hotspot-only app, use any unique identifier appropriate to your service, such as a username or account number.
|
||||
|
||||
* CarrierProvisioning\WLANProfiles\WLANProfile\name: Name of your service or test Access Point (AP).
|
||||
|
||||
* CarrierProvisioning\WLANProfiles\WLANProfile\SSIDConfig\SSID\name: Configured SSID of your test hotspot.
|
||||
|
||||
* CarrierProvisioning\WLANProfiles\WLANProfile\MSM\security\HotspotProfile\ExtAuth\ExtensionId:
|
||||
Package family name of the application. To retrieve this name, open package.appmanifest in Visual Studio. Click on the Packaging tab. Copy the Package Family Name.
|
||||
|
||||
2. Provide appropriate credentials for your test AP
|
||||
|
||||
* If the test AP uses fixed credentials: Modify HotspotAuthenticationTask\ConfigStore.cs to return a valid username and password for your test AP.
|
||||
* If the test AP uses dynamic credentials: In HotspotAuthenticationTask\BackgroundTask.cs, replace the reference to ConfigStore with your own business logic to generate/retrieve appropriate credentials for the network.
|
||||
|
||||
The next steps depend on whether you just want to deploy the sample or you want to both deploy and run it.
|
||||
|
||||
**Deploying the sample**
|
||||
1. Select **Build** \> **Deploy Solution**.
|
||||
|
||||
**Deploying and running the sample**
|
||||
1. To debug the sample and then run it, press F5 or select **Debug** \> **Start Debugging**. To run the sample without debugging, press Ctrl+F5 or select **Debug** \> **Start Without Debugging**.
|
||||
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.27130.2027
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotspotAuthenticationApp", "HotspotAuthenticationApp.csproj", "{87C48C1D-7C6C-5C7B-A524-9DC375917356}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotspotAuthenticationTask", "HotspotAuthenticationTask\HotspotAuthenticationTask.csproj", "{3F0A97C0-48FC-529A-8986-9BEA6AA5B1B4}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|ARM = Debug|ARM
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|ARM = Release|ARM
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{87C48C1D-7C6C-5C7B-A524-9DC375917356}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||
{87C48C1D-7C6C-5C7B-A524-9DC375917356}.Debug|ARM.Build.0 = Debug|ARM
|
||||
{87C48C1D-7C6C-5C7B-A524-9DC375917356}.Debug|ARM.Deploy.0 = Debug|ARM
|
||||
{87C48C1D-7C6C-5C7B-A524-9DC375917356}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{87C48C1D-7C6C-5C7B-A524-9DC375917356}.Debug|x64.Build.0 = Debug|x64
|
||||
{87C48C1D-7C6C-5C7B-A524-9DC375917356}.Debug|x64.Deploy.0 = Debug|x64
|
||||
{87C48C1D-7C6C-5C7B-A524-9DC375917356}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{87C48C1D-7C6C-5C7B-A524-9DC375917356}.Debug|x86.Build.0 = Debug|x86
|
||||
{87C48C1D-7C6C-5C7B-A524-9DC375917356}.Debug|x86.Deploy.0 = Debug|x86
|
||||
{87C48C1D-7C6C-5C7B-A524-9DC375917356}.Release|ARM.ActiveCfg = Release|ARM
|
||||
{87C48C1D-7C6C-5C7B-A524-9DC375917356}.Release|ARM.Build.0 = Release|ARM
|
||||
{87C48C1D-7C6C-5C7B-A524-9DC375917356}.Release|ARM.Deploy.0 = Release|ARM
|
||||
{87C48C1D-7C6C-5C7B-A524-9DC375917356}.Release|x64.ActiveCfg = Release|x64
|
||||
{87C48C1D-7C6C-5C7B-A524-9DC375917356}.Release|x64.Build.0 = Release|x64
|
||||
{87C48C1D-7C6C-5C7B-A524-9DC375917356}.Release|x64.Deploy.0 = Release|x64
|
||||
{87C48C1D-7C6C-5C7B-A524-9DC375917356}.Release|x86.ActiveCfg = Release|x86
|
||||
{87C48C1D-7C6C-5C7B-A524-9DC375917356}.Release|x86.Build.0 = Release|x86
|
||||
{87C48C1D-7C6C-5C7B-A524-9DC375917356}.Release|x86.Deploy.0 = Release|x86
|
||||
{3F0A97C0-48FC-529A-8986-9BEA6AA5B1B4}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||
{3F0A97C0-48FC-529A-8986-9BEA6AA5B1B4}.Debug|ARM.Build.0 = Debug|ARM
|
||||
{3F0A97C0-48FC-529A-8986-9BEA6AA5B1B4}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{3F0A97C0-48FC-529A-8986-9BEA6AA5B1B4}.Debug|x64.Build.0 = Debug|x64
|
||||
{3F0A97C0-48FC-529A-8986-9BEA6AA5B1B4}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{3F0A97C0-48FC-529A-8986-9BEA6AA5B1B4}.Debug|x86.Build.0 = Debug|x86
|
||||
{3F0A97C0-48FC-529A-8986-9BEA6AA5B1B4}.Release|ARM.ActiveCfg = Release|ARM
|
||||
{3F0A97C0-48FC-529A-8986-9BEA6AA5B1B4}.Release|ARM.Build.0 = Release|ARM
|
||||
{3F0A97C0-48FC-529A-8986-9BEA6AA5B1B4}.Release|x64.ActiveCfg = Release|x64
|
||||
{3F0A97C0-48FC-529A-8986-9BEA6AA5B1B4}.Release|x64.Build.0 = Release|x64
|
||||
{3F0A97C0-48FC-529A-8986-9BEA6AA5B1B4}.Release|x86.ActiveCfg = Release|x86
|
||||
{3F0A97C0-48FC-529A-8986-9BEA6AA5B1B4}.Release|x86.Build.0 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {82A66FCD-859A-40F5-A8CC-402F20E50B26}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,198 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
|
||||
<ProjectGuid>{87C48C1D-7C6C-5C7B-A524-9DC375917356}</ProjectGuid>
|
||||
<OutputType>AppContainerExe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>SDKTemplate</RootNamespace>
|
||||
<AssemblyName>HotspotApp</AssemblyName>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
|
||||
<TargetPlatformVersion>10.0.15063.0</TargetPlatformVersion>
|
||||
<TargetPlatformMinVersion>10.0.15063.0</TargetPlatformMinVersion>
|
||||
<MinimumVisualStudioVersion>15</MinimumVisualStudioVersion>
|
||||
<EnableDotNetNativeCompatibleProfile>true</EnableDotNetNativeCompatibleProfile>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<RuntimeIdentifiers>win10-arm;win10-arm-aot;win10-x86;win10-x86-aot;win10-x64;win10-x64-aot</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\ARM\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>ARM</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM'">
|
||||
<OutputPath>bin\ARM\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>ARM</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x64\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<OutputPath>bin\x64\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<OutputPath>bin\x86\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\..\..\SharedContent\cs\App.xaml.cs">
|
||||
<Link>App.xaml.cs</Link>
|
||||
<DependentUpon>App.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="..\..\..\SharedContent\cs\MainPage.xaml.cs">
|
||||
<Link>MainPage.xaml.cs</Link>
|
||||
<DependentUpon>MainPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="..\..\..\SharedContent\cs\AssemblyInfo.cs">
|
||||
<Link>Properties\AssemblyInfo.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="SampleConfiguration.cs" />
|
||||
<Compile Include="Scenario1_Initialization.xaml.cs">
|
||||
<DependentUpon>Scenario1_Initialization.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Scenario2_BackgroundTask.xaml.cs">
|
||||
<DependentUpon>Scenario2_BackgroundTask.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Scenario3_ForegroundApp.xaml.cs">
|
||||
<DependentUpon>Scenario3_ForegroundApp.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="ScenarioCommon.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AppxManifest Include="Package.appxmanifest">
|
||||
<SubType>Designer</SubType>
|
||||
</AppxManifest>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ApplicationDefinition Include="..\..\..\SharedContent\xaml\App.xaml">
|
||||
<Link>App.xaml</Link>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</ApplicationDefinition>
|
||||
<Page Include="..\..\..\SharedContent\cs\MainPage.xaml">
|
||||
<Link>MainPage.xaml</Link>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Scenario1_Initialization.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Scenario2_BackgroundTask.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Scenario3_ForegroundApp.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="..\..\..\SharedContent\xaml\Styles.xaml">
|
||||
<Link>Styles\Styles.xaml</Link>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="..\..\..\SharedContent\cs\Default.rd.xml">
|
||||
<Link>Properties\Default.rd.xml</Link>
|
||||
</Content>
|
||||
<Content Include="..\..\..\SharedContent\media\microsoft-sdk.png">
|
||||
<Link>Assets\microsoft-sdk.png</Link>
|
||||
</Content>
|
||||
<Content Include="..\..\..\SharedContent\media\smalltile-sdk.png">
|
||||
<Link>Assets\smallTile-sdk.png</Link>
|
||||
</Content>
|
||||
<Content Include="..\..\..\SharedContent\media\splash-sdk.png">
|
||||
<Link>Assets\splash-sdk.png</Link>
|
||||
</Content>
|
||||
<Content Include="..\..\..\SharedContent\media\squaretile-sdk.png">
|
||||
<Link>Assets\squareTile-sdk.png</Link>
|
||||
</Content>
|
||||
<Content Include="..\..\..\SharedContent\media\storelogo-sdk.png">
|
||||
<Link>Assets\storeLogo-sdk.png</Link>
|
||||
</Content>
|
||||
<Content Include="..\..\..\SharedContent\media\tile-sdk.png">
|
||||
<Link>Assets\tile-sdk.png</Link>
|
||||
</Content>
|
||||
<Content Include="..\..\..\SharedContent\media\windows-sdk.png">
|
||||
<Link>Assets\windows-sdk.png</Link>
|
||||
</Content>
|
||||
<Content Include="ProvisioningData.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
||||
<Version>5.0.0</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="HotspotAuthenticationTask\HotspotAuthenticationTask.csproj">
|
||||
<Project>{f0f4e04a-1851-45f4-9899-4986677874fa}</Project>
|
||||
<Name>HotspotAuthenticationTask</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' < '15.0' ">
|
||||
<VisualStudioVersion>15.0</VisualStudioVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -0,0 +1,196 @@
|
|||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.ApplicationModel.Background;
|
||||
using Windows.Data.Xml.Dom;
|
||||
using Windows.Networking.NetworkOperators;
|
||||
using Windows.UI.Notifications;
|
||||
|
||||
// The namespace for the background tasks.
|
||||
namespace HotspotAuthenticationTask
|
||||
{
|
||||
// A background task always implements the IBackgroundTask interface.
|
||||
public sealed class AuthenticationTask : IBackgroundTask
|
||||
{
|
||||
private const string _foregroundAppId = "HotspotAuthenticationApp.App";
|
||||
private volatile bool _cancelRequested = false;
|
||||
|
||||
private HotspotAuthenticationEventDetails _details;
|
||||
private HotspotAuthenticationContext _context;
|
||||
|
||||
// The Run method is the entry point of a background task.
|
||||
public async void Run(IBackgroundTaskInstance taskInstance)
|
||||
{
|
||||
Debug.WriteLine("Background " + taskInstance.Task.Name + " starting...");
|
||||
|
||||
// Convert the trigger details to a HotspotAuthenticationEventDetails.
|
||||
_details = taskInstance.TriggerDetails as HotspotAuthenticationEventDetails;
|
||||
|
||||
// Associate a cancelation handler with the background task for handlers
|
||||
// that may take a considerable time to complete.
|
||||
taskInstance.Canceled += OnCanceled;
|
||||
|
||||
// Take a deferral so that we can call asynchronous APIs.
|
||||
BackgroundTaskDeferral deferral = taskInstance.GetDeferral();
|
||||
|
||||
// The real work happens in RunAsync.
|
||||
await RunAsync();
|
||||
|
||||
Debug.WriteLine("Background " + taskInstance.Task.Name + " completed");
|
||||
|
||||
deferral.Complete();
|
||||
}
|
||||
|
||||
private async Task RunAsync()
|
||||
{
|
||||
// Do the background task activity. First, get the authentication context.
|
||||
Debug.WriteLine("Getting event details");
|
||||
|
||||
if (!HotspotAuthenticationContext.TryGetAuthenticationContext(_details.EventToken, out _context))
|
||||
{
|
||||
// The event is not targetting this application. There is no further processing to do.
|
||||
Debug.WriteLine("Failed to get event context");
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] ssid = _context.WirelessNetworkId;
|
||||
Debug.WriteLine("SSID: " + System.Text.UTF8Encoding.UTF8.GetString(ssid, 0, ssid.Length));
|
||||
|
||||
if (ConfigStore.UseNativeWISPr)
|
||||
{
|
||||
// Following code can be used if using native WISPr implementation. Please note that
|
||||
// following HotspotAuthenticationContext properties only work on windows and not on windows phone.
|
||||
// On Windows Phone they return un-useful strings
|
||||
// Developers are expected to implement their own WISPr implementation on Phone
|
||||
|
||||
Debug.WriteLine("AuthenticationUrl: " + _context.AuthenticationUrl.OriginalString);
|
||||
Debug.WriteLine("RedirectMessageUrl: " + _context.RedirectMessageUrl.OriginalString);
|
||||
Debug.WriteLine("RedirectMessageXml: " + _context.RedirectMessageXml.GetXml());
|
||||
|
||||
// In this sample, the AuthenticationUrl is always checked in the background task handler
|
||||
// to avoid launching the foreground app in case the authentication host is not trusted.
|
||||
if (ConfigStore.AuthenticationHost != _context.AuthenticationUrl.Host)
|
||||
{
|
||||
// Hotspot is not using the trusted authentication server.
|
||||
// Abort authentication and disconnect.
|
||||
Debug.WriteLine("Authentication server is untrusted");
|
||||
_context.AbortAuthentication(ConfigStore.MarkAsManualConnect);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Run the appropriate scenario selected by the foreground app.
|
||||
if (ConfigStore.AuthenticateThroughBackgroundTask)
|
||||
{
|
||||
await AuthenticateInBackgroundAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
ContinueAuthenticationInForeground();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
private async Task AuthenticateInBackgroundAsync()
|
||||
{
|
||||
// In case this handler performs more complex tasks, it may get canceled at runtime.
|
||||
// Check if task was canceled by now.
|
||||
if (_cancelRequested)
|
||||
{
|
||||
// In case the task handler takes too long to generate credentials and gets canceled,
|
||||
// the handler should terminate the authentication by aborting it
|
||||
Debug.WriteLine("Aborting authentication");
|
||||
_context.AbortAuthentication(ConfigStore.MarkAsManualConnect);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ConfigStore.UseNativeWISPr)
|
||||
{
|
||||
// The most common way of handling an authentication attempt is by providing WISPr credentials
|
||||
// through the IssueCredentialsAsync API.
|
||||
// If the task doesn't take any actions for authentication failures, it can use the
|
||||
// IssueCredentials API to just provide credenstials.
|
||||
// Alternatively, an application could run its own business logic to authentication with the
|
||||
// hotspot. In this case it should call the SkipAuthentication API. Note that it should call
|
||||
// SkipAuthentication after it has authenticated to allow Windows to refresh the network connectivity
|
||||
// state instantly.
|
||||
Debug.WriteLine("Issuing credentials");
|
||||
HotspotCredentialsAuthenticationResult result = await _context.IssueCredentialsAsync(
|
||||
ConfigStore.UserName, ConfigStore.Password, ConfigStore.ExtraParameters, ConfigStore.MarkAsManualConnect);
|
||||
if (result.ResponseCode == HotspotAuthenticationResponseCode.LoginSucceeded)
|
||||
{
|
||||
Debug.WriteLine("Issuing credentials succeeded");
|
||||
Uri logoffUrl = result.LogoffUrl;
|
||||
if (logoffUrl != null)
|
||||
{
|
||||
Debug.WriteLine("The logoff URL is: " + logoffUrl.OriginalString);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.WriteLine("Issuing credentials failed");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//TODO: Please perform any authentication that is required by your particular scenario.
|
||||
// Check _cancelRequested periodically in case the task is canceled.
|
||||
|
||||
// Finally call SkipAuthentication to indicate that we are not doing native WISPr authentication
|
||||
// This call also serves the purpose of indicating a successful authentication.
|
||||
_context.SkipAuthentication();
|
||||
}
|
||||
}
|
||||
|
||||
private void ContinueAuthenticationInForeground()
|
||||
{
|
||||
Debug.WriteLine("Triggering foreground application");
|
||||
// Pass event token to application
|
||||
ConfigStore.AuthenticationToken = _details.EventToken;
|
||||
|
||||
// Trigger notification
|
||||
// Since TriggerAttentionRequired function throws NotImplementedException on phone we will be using
|
||||
// regular Toast Notification to notify user about the authentication, Tapping on the notification will
|
||||
// launch the application where user can complete the authentication
|
||||
if (ConfigStore.UseNativeWISPr)
|
||||
{
|
||||
// The second parameter is used as the activation command line.
|
||||
_context.TriggerAttentionRequired(_foregroundAppId, "");
|
||||
}
|
||||
else
|
||||
{
|
||||
var toastXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText01);
|
||||
toastXml.GetElementsByTagName("text")[0].AppendChild(toastXml.CreateTextNode("Auth by foreground"));
|
||||
IXmlNode toastNode = toastXml.SelectSingleNode("/toast");
|
||||
((XmlElement)toastNode).SetAttribute("launch", "AuthByForeground");
|
||||
|
||||
var toast = new ToastNotification(toastXml);
|
||||
toast.Tag = "AuthByForeground";
|
||||
toast.Group = "HotspotAuthAPI";
|
||||
|
||||
var notification = ToastNotificationManager.CreateToastNotifier();
|
||||
notification.Show(toast);
|
||||
}
|
||||
}
|
||||
|
||||
// Handles background task cancellation.
|
||||
private void OnCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
|
||||
{
|
||||
// Indicate that the background task is canceled.
|
||||
_cancelRequested = true;
|
||||
|
||||
Debug.WriteLine("Background " + sender.Task.Name + " cancel requested...");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using Windows.ApplicationModel.Background;
|
||||
using Windows.Storage;
|
||||
using Windows.Networking.NetworkOperators;
|
||||
|
||||
// The namespace for the background tasks.
|
||||
namespace HotspotAuthenticationTask
|
||||
{
|
||||
// A helper class for providing the application configuration.
|
||||
public sealed class ConfigStore
|
||||
{
|
||||
// For the sake of simplicity of the sample, the following authentication parameters are hard coded:
|
||||
public static string AuthenticationHost => "login.contoso.com";
|
||||
public static string UserName => "MyUserName";
|
||||
public static string Password => "MyPassword";
|
||||
public static string ExtraParameters => "";
|
||||
public static bool MarkAsManualConnect => false;
|
||||
|
||||
public static bool UseNativeWISPr
|
||||
{
|
||||
get { return GetValueWithDefault("usenativewispr", true); }
|
||||
set { ApplicationData.Current.LocalSettings.Values["usenativewispr"] = value; }
|
||||
}
|
||||
|
||||
// This flag is set by the foreground app to toogle authentication to be done by the
|
||||
// background task handler.
|
||||
public static bool AuthenticateThroughBackgroundTask
|
||||
{
|
||||
get { return GetValueWithDefault("background", true); }
|
||||
set { ApplicationData.Current.LocalSettings.Values["background"] = value; }
|
||||
}
|
||||
|
||||
// This item is set by the background task handler to pass an authentication event
|
||||
// token to the foreground app.
|
||||
public static string AuthenticationToken
|
||||
{
|
||||
get { return GetValueWithDefault("token", ""); }
|
||||
set { ApplicationData.Current.LocalSettings.Values["token"] = value; }
|
||||
}
|
||||
|
||||
static T GetValueWithDefault<T>(string name, T defaultValue)
|
||||
{
|
||||
object value;
|
||||
if (ApplicationData.Current.LocalSettings.Values.TryGetValue(name, out value) &&
|
||||
value is T)
|
||||
{
|
||||
return (T)value;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
|
||||
<ProjectGuid>{3F0A97C0-48FC-529A-8986-9BEA6AA5B1B4}</ProjectGuid>
|
||||
<OutputType>winmdobj</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>HotspotAuthenticationTask</RootNamespace>
|
||||
<AssemblyName>HotspotAuthenticationTask</AssemblyName>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
|
||||
<TargetPlatformVersion>10.0.15063.0</TargetPlatformVersion>
|
||||
<TargetPlatformMinVersion>10.0.15063.0</TargetPlatformMinVersion>
|
||||
<MinimumVisualStudioVersion>15</MinimumVisualStudioVersion>
|
||||
<EnableDotNetNativeCompatibleProfile>true</EnableDotNetNativeCompatibleProfile>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<RuntimeIdentifiers>win10-arm;win10-arm-aot;win10-x86;win10-x86-aot;win10-x64;win10-x64-aot</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM'">
|
||||
<PlatformTarget>ARM</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\ARM\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>ARM</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM'">
|
||||
<PlatformTarget>ARM</PlatformTarget>
|
||||
<OutputPath>bin\ARM\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>ARM</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x64\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<OutputPath>bin\x64\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<OutputPath>bin\x86\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="BackgroundTask.cs" />
|
||||
<Compile Include="ConfigStore.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
||||
<Version>5.0.0</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' < '14.0' ">
|
||||
<VisualStudioVersion>14.0</VisualStudioVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -0,0 +1,29 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("Hotspot Authentication CS sample")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("")]
|
||||
[assembly: AssemblyCopyright("")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
[assembly: ComVisible(false)]
|
|
@ -0,0 +1,51 @@
|
|||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<Package
|
||||
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
|
||||
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
|
||||
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
|
||||
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
|
||||
IgnorableNamespaces="uap mp rescap"
|
||||
>
|
||||
<Identity Name="Microsoft.SDKSamples.HotspotAuthentication.CS" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" Version="1.0.0.0"/>
|
||||
<mp:PhoneIdentity PhoneProductId="37343625-8106-449c-be49-b857a3e5be4b" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
|
||||
<Properties>
|
||||
<DisplayName>Hotspot Authentication C# Sample</DisplayName>
|
||||
<PublisherDisplayName>Microsoft Corporation</PublisherDisplayName>
|
||||
<Logo>Assets\StoreLogo-sdk.png</Logo>
|
||||
</Properties>
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.15063.0" MaxVersionTested="10.0.15063.0"/>
|
||||
</Dependencies>
|
||||
<Resources>
|
||||
<Resource Language="x-generate"/>
|
||||
</Resources>
|
||||
<Applications>
|
||||
<Application Id="HotspotApp.App" Executable="$targetnametoken$.exe" EntryPoint="HotspotAuthenticationApp.App">
|
||||
<uap:VisualElements
|
||||
DisplayName="Hotspot Authentication C# sample"
|
||||
Square150x150Logo="Assets\squareTile-sdk.png"
|
||||
Square44x44Logo="Assets\SmallTile-sdk.png"
|
||||
Description="Hotspot Authentication C# sample"
|
||||
BackgroundColor="#00b2f0"
|
||||
>
|
||||
<uap:SplashScreen Image="Assets\Splash-sdk.png"/>
|
||||
<uap:DefaultTile>
|
||||
<uap:ShowNameOnTiles>
|
||||
<uap:ShowOn Tile="square150x150Logo"/>
|
||||
</uap:ShowNameOnTiles>
|
||||
</uap:DefaultTile>
|
||||
</uap:VisualElements>
|
||||
<Extensions>
|
||||
<Extension Category="windows.backgroundTasks" EntryPoint="HotspotAuthenticationTask.AuthenticationTask">
|
||||
<BackgroundTasks>
|
||||
<Task Type="systemEvent"/>
|
||||
</BackgroundTasks>
|
||||
</Extension>
|
||||
</Extensions>
|
||||
</Application>
|
||||
</Applications>
|
||||
<Capabilities>
|
||||
<Capability Name="internetClient"/>
|
||||
<rescap:Capability Name="networkConnectionManagerProvisioning"/>
|
||||
</Capabilities>
|
||||
</Package>
|
|
@ -0,0 +1,36 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<CarrierProvisioning xmlns="http://www.microsoft.com/networking/CarrierControl/v1">
|
||||
<Global>
|
||||
<!-- Adjust the Carrier ID to fit your own ID. Refer to the MSDN documentation about Carrier ID's. -->
|
||||
<CarrierId>{11111111-1111-1111-1111-111111111111}</CarrierId>
|
||||
<!-- Adjust the Susbscriber ID. Refer to the MSDN documentation about Subscriber ID's. -->
|
||||
<SubscriberId>1234567890</SubscriberId>
|
||||
</Global>
|
||||
<WLANProfiles>
|
||||
<WLANProfile xmlns="http://www.microsoft.com/networking/CarrierControl/WLAN/v1">
|
||||
<!-- Adjust the profile name to have a human readable network name. By default this equals the SSID. -->
|
||||
<name>Contoso Wi-Fi</name>
|
||||
<SSIDConfig>
|
||||
<SSID>
|
||||
<!-- Adjust the SSID name to fit the SSID of the hotspot. -->
|
||||
<name>contosowifi</name>
|
||||
</SSID>
|
||||
</SSIDConfig>
|
||||
<MSM>
|
||||
<security>
|
||||
<authEncryption>
|
||||
<authentication>open</authentication>
|
||||
<encryption>none</encryption>
|
||||
<useOneX>false</useOneX>
|
||||
</authEncryption>
|
||||
<HotspotProfile xmlns="http://www.microsoft.com/networking/WLAN/HotspotProfile/v1">
|
||||
<ExtAuth>
|
||||
<!-- Adjust the extension ID to match the package family name of the application running the background task handler. -->
|
||||
<ExtensionId>Microsoft.SDKSamples.HotspotAuthentication.CS_8wekyb3d8bbwe</ExtensionId>
|
||||
</ExtAuth>
|
||||
</HotspotProfile>
|
||||
</security>
|
||||
</MSM>
|
||||
</WLANProfile>
|
||||
</WLANProfiles>
|
||||
</CarrierProvisioning>
|
|
@ -0,0 +1,35 @@
|
|||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace SDKTemplate
|
||||
{
|
||||
public partial class MainPage : Page
|
||||
{
|
||||
public const string FEATURE_NAME = "Hotspot Authentication";
|
||||
|
||||
List<Scenario> scenarios = new List<Scenario>
|
||||
{
|
||||
new Scenario() { Title="Initialization", ClassType=typeof(Scenario1_Initialization)},
|
||||
new Scenario() { Title="Authentication by background task", ClassType=typeof(Scenario2_BackgroundTask)},
|
||||
new Scenario() { Title="Authentication by foreground app", ClassType=typeof(Scenario3_ForegroundApp)},
|
||||
};
|
||||
}
|
||||
|
||||
public class Scenario
|
||||
{
|
||||
public string Title { get; set; }
|
||||
public Type ClassType { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
<!--
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
-->
|
||||
<Page
|
||||
x:Class="SDKTemplate.Scenario1_Initialization"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
<Grid x:Name="RootGrid" Margin="12,10,12,12">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel Margin="0,0,0,10">
|
||||
<TextBlock Text="Description:" Style="{StaticResource SampleHeaderTextStyle}"/>
|
||||
<TextBlock Style="{StaticResource BasicTextStyle}" TextWrapping="Wrap" Text="This sample demonstrates the authentication with a Wi-Fi hotspot through an application. This mechanism can be used as an alternative to configuring static credentials for a hotspot."/>
|
||||
</StackPanel>
|
||||
|
||||
<ScrollViewer Grid.Row="1" VerticalScrollMode="Auto" VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel HorizontalAlignment="Left" VerticalAlignment="Top">
|
||||
<TextBlock Style="{StaticResource BasicTextStyle}" TextWrapping="Wrap" Text="This scenario consists of two parts. First, Windows must be provisioned to trigger the application when authenticating with a certain hotspot. This is achieved by provisioning a WLAN profile with a corresponding configuration. The provisioning XML must either be signed to be consumed by the provisioning API, or this sample must be part of a privileged mobile network operator companion application. Refer to the provisioning agent sample for more details. The sample XML built into this application must be modified to match the hotspot's SSID and signed before it can be applied."/>
|
||||
|
||||
<StackPanel Orientation="Horizontal" Margin="0,10,0,0">
|
||||
<Button x:Name="ProvisionButton" Content="Provision" Margin="0,0,10,0" Click="ProvisionButton_Click"/>
|
||||
</StackPanel>
|
||||
<TextBlock Style="{StaticResource BasicTextStyle}" TextWrapping="Wrap" Text="Next, the application needs to register a background task for the hotspot authentication event. The background task runs whenever Windows attempts to authenticate with a Wi-Fi hotspot and the corresponding WLAN profile has an extension configured. Note that provisioning has to be done first to give the application access to the background event. If an application unregisters from this event, it has to provision again before it can reregister for this event. It is safe to provision multiple times, but an application can only register one background task handler for this event at a time."/>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,10,0,0">
|
||||
<Button x:Name="RegisterButton" Content="Register" Margin="0,0,10,0" Click="RegisterButton_Click"/>
|
||||
<Button x:Name="UnregisterButton" Content="Unregister" Margin="0,0,10,0" Click="UnregisterButton_Click"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Page>
|
|
@ -0,0 +1,175 @@
|
|||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
using HotspotAuthenticationTask;
|
||||
using System;
|
||||
using Windows.ApplicationModel.Background;
|
||||
using Windows.Data.Xml.Dom;
|
||||
using Windows.Networking.NetworkOperators;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
namespace SDKTemplate
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class Scenario1_Initialization : Page
|
||||
{
|
||||
private MainPage rootPage = MainPage.Current;
|
||||
|
||||
public Scenario1_Initialization()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
ConfigStore.UseNativeWISPr = true;
|
||||
|
||||
// Configure background task handler to perform authentication as default
|
||||
ConfigStore.AuthenticateThroughBackgroundTask = true;
|
||||
|
||||
// Setup completion handler
|
||||
var isTaskRegistered = ScenarioCommon.Instance.RegisteredCompletionHandlerForBackgroundTask();
|
||||
|
||||
// Initialize button state
|
||||
UpdateButtonState(isTaskRegistered);
|
||||
|
||||
}
|
||||
|
||||
private async void ProvisionButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ProvisionButton.IsEnabled = false;
|
||||
|
||||
// Open the installation folder
|
||||
var installLocation = Windows.ApplicationModel.Package.Current.InstalledLocation;
|
||||
|
||||
// Access the provisioning file
|
||||
var provisioningFile = await installLocation.GetFileAsync("ProvisioningData.xml");
|
||||
|
||||
// Load with XML parser
|
||||
var xmlDocument = await XmlDocument.LoadFromFileAsync(provisioningFile);
|
||||
|
||||
// Get raw XML
|
||||
var provisioningXml = xmlDocument.GetXml();
|
||||
|
||||
// Create ProvisiongAgent Object
|
||||
var provisioningAgent = new ProvisioningAgent();
|
||||
|
||||
try
|
||||
{
|
||||
// Create ProvisionFromXmlDocumentResults Object
|
||||
var result = await provisioningAgent.ProvisionFromXmlDocumentAsync(provisioningXml);
|
||||
|
||||
if (result.AllElementsProvisioned)
|
||||
{
|
||||
rootPage.NotifyUser("Provisioning was successful", NotifyType.StatusMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
rootPage.NotifyUser("Provisioning result: " + result.ProvisionResultsXml, NotifyType.ErrorMessage);
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
// See https://docs.microsoft.com/en-us/uwp/api/windows.networking.networkoperators.provisioningagent.provisionfromxmldocumentasync
|
||||
// for list of possible exceptions.
|
||||
rootPage.NotifyUser($"Unable to provision: {ex}", NotifyType.ErrorMessage);
|
||||
}
|
||||
|
||||
ProvisionButton.IsEnabled = true;
|
||||
}
|
||||
|
||||
private void RegisterButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Create a new background task builder.
|
||||
var taskBuilder = new BackgroundTaskBuilder();
|
||||
|
||||
// Create a new NetworkOperatorHotspotAuthentication trigger.
|
||||
var trigger = new NetworkOperatorHotspotAuthenticationTrigger();
|
||||
|
||||
// Associate the NetworkOperatorHotspotAuthentication trigger with the background task builder.
|
||||
taskBuilder.SetTrigger(trigger);
|
||||
|
||||
// Specify the background task to run when the trigger fires.
|
||||
taskBuilder.TaskEntryPoint = ScenarioCommon.BackgroundTaskEntryPoint;
|
||||
|
||||
// Name the background task.
|
||||
taskBuilder.Name = ScenarioCommon.BackgroundTaskName;
|
||||
|
||||
try
|
||||
{
|
||||
// Register the background task.
|
||||
var task = taskBuilder.Register();
|
||||
|
||||
// Associate progress and completed event handlers with the new background task.
|
||||
task.Completed += ScenarioCommon.Instance.OnBackgroundTaskCompleted;
|
||||
|
||||
UpdateButtonState(true);
|
||||
|
||||
rootPage.NotifyUser("Register was successful", NotifyType.StatusMessage);
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
rootPage.NotifyUser(ex.ToString(), NotifyType.ErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
private void UnregisterButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (UnregisterBackgroundTask())
|
||||
{
|
||||
UpdateButtonState(false);
|
||||
rootPage.NotifyUser("Unregister was successful.", NotifyType.StatusMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
rootPage.NotifyUser(String.Format("Background {0} task is not registered", ScenarioCommon.BackgroundTaskName), NotifyType.StatusMessage);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private bool UnregisterBackgroundTask()
|
||||
{
|
||||
bool unregister = false;
|
||||
// Loop through all background tasks and unregister any.
|
||||
foreach (var cur in BackgroundTaskRegistration.AllTasks)
|
||||
{
|
||||
if (cur.Value.Name == ScenarioCommon.BackgroundTaskName)
|
||||
{
|
||||
cur.Value.Unregister(true);
|
||||
unregister = true;
|
||||
}
|
||||
}
|
||||
return unregister;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables register and unregister buttons
|
||||
/// </summary>
|
||||
/// <param name="registered">True if background task is registered, false otherwise</param>
|
||||
private void UpdateButtonState(bool registered)
|
||||
{
|
||||
if (registered)
|
||||
{
|
||||
RegisterButton.IsEnabled = false;
|
||||
UnregisterButton.IsEnabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
RegisterButton.IsEnabled = true;
|
||||
UnregisterButton.IsEnabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
<!--
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
-->
|
||||
<Page
|
||||
x:Class="SDKTemplate.Scenario2_BackgroundTask"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
<Grid Margin="12,10,12,12">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel Margin="0,0,0,10">
|
||||
<TextBlock Text="Description:" Style="{StaticResource SampleHeaderTextStyle}"/>
|
||||
<TextBlock Style="{StaticResource ScenarioDescriptionTextStyle}" TextWrapping="Wrap">
|
||||
The background task handler can be used to perform the actual authentication. Connect to the hotspot configured in step one to see it happen. You may exit this application now.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
<ScrollViewer Grid.Row="1" VerticalScrollMode="Auto" VerticalScrollBarVisibility="Auto">
|
||||
<!-- Place scenario content here -->
|
||||
</ScrollViewer>
|
||||
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Page>
|
|
@ -0,0 +1,44 @@
|
|||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
|
||||
using HotspotAuthenticationTask;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
namespace SDKTemplate
|
||||
{
|
||||
/// <summary>
|
||||
/// This page is used to handle authentication by background task scenario
|
||||
/// </summary>
|
||||
public sealed partial class Scenario2_BackgroundTask : Page
|
||||
{
|
||||
// A pointer back to the main page. This is needed if you call methods in MainPage such as NotifyUser()
|
||||
MainPage rootPage = MainPage.Current;
|
||||
public Scenario2_BackgroundTask()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when this page is about to be displayed in a Frame.
|
||||
/// </summary>
|
||||
/// <param name="e">Event data that describes how this page was reached. The Parameter
|
||||
/// property is typically used to configure the page.</param>
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
// Configure background task handler to perform authentication
|
||||
ConfigStore.AuthenticateThroughBackgroundTask = true;
|
||||
|
||||
// Setup completion handler
|
||||
ScenarioCommon.Instance.RegisteredCompletionHandlerForBackgroundTask();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
<!--
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
-->
|
||||
<Page
|
||||
x:Class="SDKTemplate.Scenario3_ForegroundApp"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Margin="12,10,12,12">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel Margin="0,0,0,10">
|
||||
<TextBlock Text="Description:" Style="{StaticResource SampleHeaderTextStyle}"/>
|
||||
<TextBlock Style="{StaticResource ScenarioDescriptionTextStyle}" TextWrapping="Wrap">
|
||||
Authentication can also be performed by the foreground application. In this case, the background task handler triggers the foreground application being launched. This is useful if the application needs additional user input to perform the authentication. Connect to the hotspot configured in step 1 to see it happen. You may exit this application now, and it will be launched on demand.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
<ScrollViewer Grid.Row="1" VerticalScrollMode="Auto" VerticalScrollBarVisibility="Auto">
|
||||
<!-- Place scenario content here -->
|
||||
<StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,10,0,0">
|
||||
<Button x:Name="AuthenticateButton" Content="Authenticate" IsEnabled="False" Margin="0,0,10,0" Click="AuthenticateButton_Click"/>
|
||||
<Button x:Name="SkipButton" Content="Skip" IsEnabled="False" Margin="0,0,10,0" Click="SkipButton_Click"/>
|
||||
<Button x:Name="AbortButton" Content="Abort" IsEnabled="False" Margin="0,0,10,0" Click="AbortButton_Click"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Page>
|
|
@ -0,0 +1,153 @@
|
|||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
|
||||
using HotspotAuthenticationTask;
|
||||
using System;
|
||||
using Windows.Networking.NetworkOperators;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
namespace SDKTemplate
|
||||
{
|
||||
/// <summary>
|
||||
/// The page is used to handle authenticate by foreground app scenario
|
||||
/// </summary>
|
||||
public sealed partial class Scenario3_ForegroundApp : Page
|
||||
{
|
||||
// A pointer back to the main page. This is needed to call methods in MainPage such as NotifyUser()
|
||||
MainPage rootPage = MainPage.Current;
|
||||
|
||||
HotspotAuthenticationContext authenticationContext;
|
||||
|
||||
public Scenario3_ForegroundApp()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when this page is about to be displayed in a Frame.
|
||||
/// </summary>
|
||||
/// <param name="e">Event data that describes how this page was reached. The Parameter
|
||||
/// property is typically used to configure the page.</param>
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
// Configure background task handler to trigger foregound app for authentication
|
||||
ConfigStore.AuthenticateThroughBackgroundTask = false;
|
||||
|
||||
// Setup completion handler
|
||||
ScenarioCommon.Instance.RegisteredCompletionHandlerForBackgroundTask();
|
||||
|
||||
// Register event to update UI state on authentication event
|
||||
ScenarioCommon.Instance.BackgroundAuthenticationCompleted += OnBackgroundAuthenticationCompleted;
|
||||
|
||||
// Check current authentication state
|
||||
InitializeForegroundAppAuthentication();
|
||||
}
|
||||
|
||||
protected override void OnNavigatedFrom(NavigationEventArgs e)
|
||||
{
|
||||
ScenarioCommon.Instance.BackgroundAuthenticationCompleted -= OnBackgroundAuthenticationCompleted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle authentication event triggered by background task
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
async void OnBackgroundAuthenticationCompleted(ScenarioCommon sender, BackgroundAuthenticationCompletedEventArgs e)
|
||||
{
|
||||
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
|
||||
{
|
||||
if (e.Succeeded)
|
||||
{
|
||||
InitializeForegroundAppAuthentication();
|
||||
}
|
||||
else
|
||||
{
|
||||
rootPage.NotifyUser($"Background task encountered exception: {e.ExtendedError}", NotifyType.ErrorMessage);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async void AuthenticateButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
AuthenticateButton.IsEnabled = false;
|
||||
|
||||
HotspotCredentialsAuthenticationResult result = await authenticationContext.IssueCredentialsAsync(
|
||||
ConfigStore.UserName, ConfigStore.Password, ConfigStore.ExtraParameters, ConfigStore.MarkAsManualConnect);
|
||||
if (result.ResponseCode == HotspotAuthenticationResponseCode.LoginSucceeded)
|
||||
{
|
||||
rootPage.NotifyUser("Issuing credentials succeeded", NotifyType.StatusMessage);
|
||||
Uri logoffUrl = result.LogoffUrl;
|
||||
if (logoffUrl != null)
|
||||
{
|
||||
rootPage.NotifyUser("The logoff URL is: " + logoffUrl.OriginalString, NotifyType.StatusMessage);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rootPage.NotifyUser("Issuing credentials failed", NotifyType.ErrorMessage);
|
||||
}
|
||||
AuthenticateButton.IsEnabled = true;
|
||||
}
|
||||
|
||||
private void SkipButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Here you can implement custom authentication.
|
||||
authenticationContext.SkipAuthentication();
|
||||
rootPage.NotifyUser("Authentication skipped", NotifyType.StatusMessage);
|
||||
ClearAuthenticationToken();
|
||||
}
|
||||
|
||||
private void AbortButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
authenticationContext.AbortAuthentication(ConfigStore.MarkAsManualConnect);
|
||||
rootPage.NotifyUser("Authentication aborted", NotifyType.StatusMessage);
|
||||
ClearAuthenticationToken();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Query authentication token from application storage and update the UI.
|
||||
/// The token gets passed from the background task handler.
|
||||
/// </summary>
|
||||
private void InitializeForegroundAppAuthentication()
|
||||
{
|
||||
string token = ConfigStore.AuthenticationToken;
|
||||
if (String.IsNullOrEmpty(token))
|
||||
{
|
||||
return; // no token found
|
||||
}
|
||||
if (!HotspotAuthenticationContext.TryGetAuthenticationContext(token, out authenticationContext))
|
||||
{
|
||||
rootPage.NotifyUser("TryGetAuthenticationContext failed", NotifyType.ErrorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
AuthenticateButton.IsEnabled = true;
|
||||
SkipButton.IsEnabled = true;
|
||||
AbortButton.IsEnabled = true;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Clear the authentication token in the application storage and update the UI.
|
||||
/// </summary>
|
||||
private void ClearAuthenticationToken()
|
||||
{
|
||||
ConfigStore.AuthenticationToken = "";
|
||||
AuthenticateButton.IsEnabled = false;
|
||||
SkipButton.IsEnabled = false;
|
||||
AbortButton.IsEnabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
|
||||
using HotspotAuthenticationTask;
|
||||
using System;
|
||||
using Windows.ApplicationModel.Background;
|
||||
using Windows.UI.Core;
|
||||
|
||||
namespace SDKTemplate
|
||||
{
|
||||
// A delegate type for hooking up foreground authentication notifications.
|
||||
public class BackgroundAuthenticationCompletedEventArgs
|
||||
{
|
||||
public BackgroundAuthenticationCompletedEventArgs(Exception exception)
|
||||
{
|
||||
ExtendedError = exception;
|
||||
}
|
||||
public bool Succeeded => ExtendedError == null;
|
||||
public Exception ExtendedError { get; private set; }
|
||||
}
|
||||
|
||||
public delegate void BackgroundAuthenticationCompletedEventHandler(ScenarioCommon sender, BackgroundAuthenticationCompletedEventArgs e);
|
||||
|
||||
// Shared code for all scenario pages
|
||||
public class ScenarioCommon
|
||||
{
|
||||
public static ScenarioCommon Instance { get; } = new ScenarioCommon();
|
||||
|
||||
// The entry point name of the background task handler:
|
||||
|
||||
public const string BackgroundTaskEntryPoint = "HotspotAuthenticationTask.AuthenticationTask";
|
||||
|
||||
// The (arbitrarily chosen) name assigned to the background task:
|
||||
public const string BackgroundTaskName = "AuthenticationBackgroundTask";
|
||||
|
||||
// A delegate for subscribing for foreground authentication events
|
||||
public BackgroundAuthenticationCompletedEventHandler BackgroundAuthenticationCompleted;
|
||||
|
||||
// A pointer back to the main page. This is needed to call methods in MainPage such as NotifyUser()
|
||||
MainPage rootPage = MainPage.Current;
|
||||
|
||||
// A flag to remember if a background task handler has been registered
|
||||
private bool HasRegisteredBackgroundTaskHandler = false;
|
||||
|
||||
/// <summary>
|
||||
/// Register completion handler for registered background task on application startup.
|
||||
/// </summary>
|
||||
/// <returns>True if a registerd task was found</returns>
|
||||
public bool RegisteredCompletionHandlerForBackgroundTask()
|
||||
{
|
||||
if (!HasRegisteredBackgroundTaskHandler)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Associate background task completed event handler with background task.
|
||||
foreach (var cur in BackgroundTaskRegistration.AllTasks)
|
||||
{
|
||||
if (cur.Value.Name == BackgroundTaskName)
|
||||
{
|
||||
cur.Value.Completed += new BackgroundTaskCompletedEventHandler(OnBackgroundTaskCompleted);
|
||||
HasRegisteredBackgroundTaskHandler = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
rootPage.NotifyUser(ex.ToString(), NotifyType.ErrorMessage);
|
||||
}
|
||||
}
|
||||
return HasRegisteredBackgroundTaskHandler;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Background task completion handler. When authenticating through the foreground app, this triggers the authentication flow if the app is currently running.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
public void OnBackgroundTaskCompleted(IBackgroundTaskRegistration sender, BackgroundTaskCompletedEventArgs e)
|
||||
{
|
||||
if (sender.Name == BackgroundTaskName)
|
||||
{
|
||||
Exception exception = null;
|
||||
try
|
||||
{
|
||||
// If the background task threw an exception, re-raise it here so we can pass it to the event handler.
|
||||
e.CheckResult();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
}
|
||||
|
||||
if (!ConfigStore.AuthenticateThroughBackgroundTask)
|
||||
{
|
||||
BackgroundAuthenticationCompleted?.Invoke(this, new BackgroundAuthenticationCompletedEventArgs(null));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,7 +22,7 @@
|
|||
<ColumnDefinition Width="4*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<StackPanel x:Name="Header" Grid.Row="0" Grid.ColumnSpan="2" Height="60" Visibility="Collapsed">
|
||||
<StackPanel x:Name="Header" Grid.Row="0" Grid.ColumnSpan="2" Height="75" Visibility="Collapsed">
|
||||
<StackPanel Orientation="Horizontal" >
|
||||
<Image Source="ms-appx:///Assets/smallTile-sdk.png" HorizontalAlignment="Left" Stretch="None"/>
|
||||
<RichTextBlock Foreground="Black" FontSize="20" TextAlignment="Left" FontFamily="Segoe UI">
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
<ColumnDefinition Width="4*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<StackPanel x:Name="Header" Grid.Row="0" Grid.ColumnSpan="2" Height="60" Visibility="Collapsed">
|
||||
<StackPanel x:Name="Header" Grid.Row="0" Grid.ColumnSpan="2" Height="75" Visibility="Collapsed">
|
||||
<StackPanel Orientation="Horizontal" >
|
||||
<Image Source="ms-appx:///Assets/smallTile-sdk.png" HorizontalAlignment="Left" Stretch="None"/>
|
||||
<RichTextBlock Foreground="Black" FontSize="20" TextAlignment="Left" FontFamily="Segoe UI">
|
||||
|
|
|
@ -97,6 +97,16 @@ namespace AppUIBasics
|
|||
}
|
||||
}
|
||||
|
||||
public void EnableSound(bool withSpatial = false)
|
||||
{
|
||||
ElementSoundPlayer.State = ElementSoundPlayerState.On;
|
||||
|
||||
if(!withSpatial)
|
||||
ElementSoundPlayer.SpatialAudioMode = ElementSpatialAudioMode.Off;
|
||||
else
|
||||
ElementSoundPlayer.SpatialAudioMode = ElementSpatialAudioMode.On;
|
||||
}
|
||||
|
||||
public static TEnum GetEnum<TEnum>(string text) where TEnum : struct
|
||||
{
|
||||
if (!typeof(TEnum).GetTypeInfo().IsEnum)
|
||||
|
|
|
@ -140,6 +140,7 @@
|
|||
<Content Include="Assets\RatingControl.png" />
|
||||
<Content Include="Assets\Reveal.png" />
|
||||
<Content Include="Assets\RevealFocus.png" />
|
||||
<Content Include="Assets\Sound.png" />
|
||||
<Content Include="Assets\Sun-100-Black.png" />
|
||||
<Content Include="Assets\Sun.32.scale-100-White.png" />
|
||||
<Content Include="Assets\Swipe.png" />
|
||||
|
@ -369,6 +370,9 @@
|
|||
<Compile Include="ControlPages\SliderPage.xaml.cs">
|
||||
<DependentUpon>SliderPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="ControlPages\SoundPage.xaml.cs">
|
||||
<DependentUpon>SoundPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="ControlPages\SplitViewPage.xaml.cs">
|
||||
<DependentUpon>SplitViewPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
|
@ -727,6 +731,10 @@
|
|||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="ControlPages\SoundPage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="ControlPages\SplitViewPage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
|
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 3.3 KiB |
|
@ -0,0 +1,72 @@
|
|||
<!--
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
-->
|
||||
<Page
|
||||
x:Class="AppUIBasics.ControlPages.SoundPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:AppUIBasics"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d">
|
||||
|
||||
<StackPanel>
|
||||
<local:ControlExample x:Name="Example1" HeaderText="Toggling Sound">
|
||||
<ToggleSwitch x:Name="soundToggle" OffContent="Sound Off" OnContent="Sound On" Toggled="soundToggle_Toggled"/>
|
||||
<local:ControlExample.Xaml>
|
||||
<RichTextBlock Style="{StaticResource XamlCodeRichTextBlockStyle}">
|
||||
<Paragraph>ElementSoundPlayer.State = ElementSoundPlayerState.Off;</Paragraph>
|
||||
<Paragraph>ElementSoundPlayer.State = ElementSoundPlayerState.On;</Paragraph>
|
||||
</RichTextBlock>
|
||||
</local:ControlExample.Xaml>
|
||||
</local:ControlExample>
|
||||
<local:ControlExample x:Name="Example2" HeaderText="Toggling Spatial Audio">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<CheckBox x:Name="spatialAudioBox" IsEnabled="False" Content="Enable Spatial Audio" Checked="spatialAudioBox_Checked" Unchecked="spatialAudioBox_Unchecked"/>
|
||||
<TextBlock Margin="0,5,0,0" Text="Can only enable spatial audio when sound is on!" Foreground="Red" Style="{ThemeResource CaptionTextBlockStyle}" FontStyle="Italic"/>
|
||||
</StackPanel>
|
||||
<local:ControlExample.Xaml>
|
||||
<RichTextBlock Style="{StaticResource XamlCodeRichTextBlockStyle}">
|
||||
<Paragraph>ElementSoundPlayer.State = ElementSoundPlayerState.On;</Paragraph>
|
||||
<Paragraph>ElementSoundPlayer.SpatialAudioMode = ElementSpatialAudioMode.On</Paragraph>
|
||||
</RichTextBlock>
|
||||
</local:ControlExample.Xaml>
|
||||
</local:ControlExample>
|
||||
<local:ControlExample x:Name="Example3" HeaderText="Play Specific System Sound">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button Grid.Column="0" Content="Play Sound" ElementSoundMode="Off" Click="Button_Click" HorizontalAlignment="Left"/>
|
||||
<ComboBox x:Name="soundSelection" Grid.Column="1" Header="Pick Custom Sound" SelectedIndex="1" HorizontalAlignment="Right" Margin="50,0,0,0">
|
||||
<ComboBoxItem>Focus</ComboBoxItem>
|
||||
<ComboBoxItem>Invoke</ComboBoxItem>
|
||||
<ComboBoxItem>Show</ComboBoxItem>
|
||||
<ComboBoxItem>Hide</ComboBoxItem>
|
||||
<ComboBoxItem>MoveNext</ComboBoxItem>
|
||||
<ComboBoxItem>MovePrevious</ComboBoxItem>
|
||||
<ComboBoxItem>GoBack</ComboBoxItem>
|
||||
</ComboBox>
|
||||
</Grid>
|
||||
<local:ControlExample.Xaml>
|
||||
<RichTextBlock x:Name="codeText" Style="{StaticResource XamlCodeRichTextBlockStyle}">
|
||||
<Paragraph>ElementSoundPlayer.State = ElementSoundPlayerState.On;</Paragraph>
|
||||
<Paragraph></Paragraph>
|
||||
<Paragraph>ElementSoundPlayer.Play(ElementSoundKind.Focus);</Paragraph>
|
||||
<Paragraph>ElementSoundPlayer.Play(ElementSoundKind.Invoke);</Paragraph>
|
||||
<Paragraph>ElementSoundPlayer.Play(ElementSoundKind.Show);</Paragraph>
|
||||
<Paragraph>ElementSoundPlayer.Play(ElementSoundKind.Hide);</Paragraph>
|
||||
<Paragraph>ElementSoundPlayer.Play(ElementSoundKind.MoveNext);</Paragraph>
|
||||
<Paragraph>ElementSoundPlayer.Play(ElementSoundKind.MovePrevious);</Paragraph>
|
||||
<Paragraph>ElementSoundPlayer.Play(ElementSoundKind.GoBack);</Paragraph>
|
||||
</RichTextBlock>
|
||||
</local:ControlExample.Xaml>
|
||||
</local:ControlExample>
|
||||
</StackPanel>
|
||||
</Page>
|
|
@ -0,0 +1,73 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
using Windows.UI.Xaml.Data;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238
|
||||
|
||||
namespace AppUIBasics.ControlPages
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class SoundPage : Page
|
||||
{
|
||||
public SoundPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
if (ElementSoundPlayer.State == ElementSoundPlayerState.On)
|
||||
soundToggle.IsOn = true;
|
||||
if (ElementSoundPlayer.SpatialAudioMode == ElementSpatialAudioMode.On && soundToggle.IsOn == true)
|
||||
spatialAudioBox.IsChecked = true;
|
||||
}
|
||||
|
||||
private void Button_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ElementSoundPlayer.Play((ElementSoundKind)soundSelection.SelectedIndex);
|
||||
}
|
||||
|
||||
private void spatialAudioBox_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (soundToggle.IsOn == true)
|
||||
{
|
||||
ElementSoundPlayer.SpatialAudioMode = ElementSpatialAudioMode.On;
|
||||
}
|
||||
}
|
||||
|
||||
private void spatialAudioBox_Unchecked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (soundToggle.IsOn == true)
|
||||
{
|
||||
ElementSoundPlayer.SpatialAudioMode = ElementSpatialAudioMode.Off;
|
||||
}
|
||||
}
|
||||
|
||||
private void soundToggle_Toggled(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (soundToggle.IsOn == true)
|
||||
{
|
||||
spatialAudioBox.IsEnabled = true;
|
||||
ElementSoundPlayer.State = ElementSoundPlayerState.On;
|
||||
}
|
||||
else
|
||||
{
|
||||
spatialAudioBox.IsEnabled = false;
|
||||
spatialAudioBox.IsChecked = false;
|
||||
|
||||
ElementSoundPlayer.State = ElementSoundPlayerState.Off;
|
||||
ElementSoundPlayer.SpatialAudioMode = ElementSpatialAudioMode.Off;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -746,6 +746,25 @@
|
|||
"RatingControl"
|
||||
]
|
||||
},
|
||||
{
|
||||
"UniqueId": "Sound",
|
||||
"Title": "Sound",
|
||||
"Subtitle": "A C#-only API that enables 2D and 3D UI sounds on all UWP controls.",
|
||||
"ImagePath": "ms-appx:///Assets/Sound.png",
|
||||
"Description": "Sound is enabled by default for UWP apps running on Xbox, but can be set to always play on all devices if desired. Sound may also be put into Spatial Audio mode for a more immersive 10ft experience.",
|
||||
"Content": "<p>Look at the <i>SoundPage.xaml</i> file in Visual Studio to see the full code for this page.</p>",
|
||||
"IsNew": false,
|
||||
"Docs": [
|
||||
{
|
||||
"Title": "Sound",
|
||||
"Uri": "https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.elementsoundplayer"
|
||||
},
|
||||
{
|
||||
"Title": "Guidelines",
|
||||
"Uri": "https://docs.microsoft.com/en-us/windows/uwp/design/style/sound"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"UniqueId": "TimePicker",
|
||||
"Title": "TimePicker",
|
||||
|
|
|
@ -46,6 +46,16 @@
|
|||
<RadioButton Tag="Default" Checked="OnThemeRadioButtonChecked" Content="Use system setting" />
|
||||
</StackPanel>
|
||||
<Button Click="OnFeedbackButtonClick" Content="Feedback" Margin="0,8,0,0" />
|
||||
<TextBlock
|
||||
Style="{StaticResource TitleTextBlockStyle}"
|
||||
Margin="0,40,0,0"
|
||||
FontWeight="Normal"
|
||||
Text="Sound" />
|
||||
<StackPanel Margin="0,10,0,0">
|
||||
<ToggleSwitch x:Name="soundToggle" Toggled="soundToggle_Toggled" OffContent="Off" OnContent="On"/>
|
||||
<CheckBox x:Name="spatialSoundBox" Unchecked="spatialSoundBox_Unchecked" Checked="spatialSoundBox_Checked" IsEnabled="False" Content="Spatial Audio"/>
|
||||
<TextBlock Text="See the Sound page for how to enable sound in your app!" Style="{ThemeResource CaptionTextBlockStyle}" FontStyle="Italic" Margin="0,8,0,0"/>
|
||||
</StackPanel>
|
||||
<TextBlock
|
||||
Style="{StaticResource TitleTextBlockStyle}"
|
||||
Margin="0,40,0,0"
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
using AppUIBasics.ControlPages;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Windows.System;
|
||||
|
@ -28,6 +29,11 @@ namespace AppUIBasics
|
|||
{
|
||||
this.InitializeComponent();
|
||||
Loaded += OnSettingsPageLoaded;
|
||||
|
||||
if (ElementSoundPlayer.State == ElementSoundPlayerState.On)
|
||||
soundToggle.IsOn = true;
|
||||
if (ElementSoundPlayer.SpatialAudioMode == ElementSpatialAudioMode.On)
|
||||
spatialSoundBox.IsChecked = true;
|
||||
}
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
|
@ -84,5 +90,37 @@ namespace AppUIBasics
|
|||
NavigationRootPage.Current.PageHeader.Focus(FocusState.Programmatic);
|
||||
}
|
||||
}
|
||||
private void spatialSoundBox_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if(soundToggle.IsOn == true)
|
||||
{
|
||||
ElementSoundPlayer.SpatialAudioMode = ElementSpatialAudioMode.On;
|
||||
}
|
||||
}
|
||||
|
||||
private void soundToggle_Toggled(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (soundToggle.IsOn == true)
|
||||
{
|
||||
spatialSoundBox.IsEnabled = true;
|
||||
ElementSoundPlayer.State = ElementSoundPlayerState.On;
|
||||
}
|
||||
else
|
||||
{
|
||||
spatialSoundBox.IsEnabled = false;
|
||||
spatialSoundBox.IsChecked = false;
|
||||
|
||||
ElementSoundPlayer.State = ElementSoundPlayerState.Off;
|
||||
ElementSoundPlayer.SpatialAudioMode = ElementSpatialAudioMode.Off;
|
||||
}
|
||||
}
|
||||
|
||||
private void spatialSoundBox_Unchecked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (soundToggle.IsOn == true)
|
||||
{
|
||||
ElementSoundPlayer.SpatialAudioMode = ElementSpatialAudioMode.Off;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче