// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. // // App.xaml.cpp // Implementation of the App class. // #include "pch.h" #include "App.xaml.h" #include "CalcViewModel/Common/TraceLogger.h" #include "CalcViewModel/Common/Automation/NarratorNotifier.h" #include "CalcViewModel/Common/AppResourceProvider.h" #include "CalcViewModel/Common/LocalizationSettings.h" #include "CalcViewModel/ViewState.h" #include "Views/MainPage.xaml.h" using namespace CalculatorApp; using namespace CalculatorApp::Common; using namespace CalculatorApp::Common::Automation; using namespace Concurrency; using namespace Microsoft::WRL; using namespace Platform; using namespace Windows::ApplicationModel; using namespace Windows::ApplicationModel::Activation; using namespace Windows::ApplicationModel::Core; using namespace Windows::ApplicationModel::Resources; using namespace Windows::Foundation; using namespace Windows::Foundation::Collections; using namespace Windows::Storage; using namespace Windows::System; using namespace Windows::UI::Core; using namespace Windows::UI::Popups; using namespace Windows::UI::StartScreen; using namespace Windows::UI::ViewManagement; using namespace Windows::UI::Xaml; using namespace Windows::UI::Xaml::Controls; using namespace Windows::UI::Xaml::Controls::Primitives; using namespace Windows::UI::Xaml::Data; using namespace Windows::UI::Xaml::Input; using namespace Windows::UI::Xaml::Interop; using namespace Windows::UI::Xaml::Media; using namespace Windows::UI::Xaml::Media::Animation; using namespace Windows::UI::Xaml::Navigation; using namespace Windows::ApplicationModel::Activation; namespace CalculatorApp { namespace ApplicationResourceKeys { StringReference AppMinWindowHeight(L"AppMinWindowHeight"); StringReference AppMinWindowWidth(L"AppMinWindowWidth"); } } /// /// Initializes the singleton application object. This is the first line of authored code /// executed, and as such is the logical equivalent of main() or WinMain(). /// App::App() { TraceLogger::GetInstance().LogAppLaunchStart(); InitializeComponent(); m_preLaunched = false; RegisterDependencyProperties(); // TODO: MSFT 14645325: Set this directly from XAML. // Currently this is bugged so the property is only respected from code-behind. this->HighContrastAdjustment = ApplicationHighContrastAdjustment::None; #if _DEBUG this->DebugSettings->IsBindingTracingEnabled = true; this->DebugSettings->BindingFailed += ref new BindingFailedEventHandler([](_In_ Object^ /*sender*/, _In_ BindingFailedEventArgs^ e) { if (IsDebuggerPresent()) { ::Platform::String^ errorMessage = e->Message; __debugbreak(); } }); #endif } bool App::m_isAnimationEnabled = true; /// /// Return True if animation is enabled by user setting. /// bool App::IsAnimationEnabled() { return App::m_isAnimationEnabled; } /// /// Return the current application view state. The value /// will match one of the constants in the ViewState namespace. /// String^ App::GetAppViewState() { String^ newViewState; CoreWindow^ window = CoreWindow::GetForCurrentThread(); if ((window->Bounds.Width >= 560) && (window->Bounds.Height >= 356)) { newViewState = ViewState::DockedView; } else { newViewState = ViewState::Snap; } return newViewState; } void App::AddWindowToMap(_In_ WindowFrameService^ frameService) { reader_writer_lock::scoped_lock lock(m_windowsMapLock); m_secondaryWindows[frameService->GetViewId()] = frameService; TraceLogger::GetInstance().UpdateWindowCount(m_secondaryWindows.size()); } WindowFrameService^ App::GetWindowFromMap(int viewId) { reader_writer_lock::scoped_lock_read lock(m_windowsMapLock); auto windowMapEntry = m_secondaryWindows.find(viewId); if (windowMapEntry != m_secondaryWindows.end()) { return windowMapEntry->second; } return nullptr; } void App::RemoveWindowFromMap(int viewId) { reader_writer_lock::scoped_lock lock(m_windowsMapLock); auto iter = m_secondaryWindows.find(viewId); assert(iter != m_secondaryWindows.end() && "Window does not exist in the list"); m_secondaryWindows.erase(viewId); } void App::RemoveWindow(_In_ WindowFrameService^ frameService) { // Shell does not allow killing the main window. if (m_mainViewId != frameService->GetViewId()) { HandleViewReleaseAndRemoveWindowFromMap(frameService); } } task App::HandleViewReleaseAndRemoveWindowFromMap(_In_ WindowFrameService^ frameService) { WeakReference weak(this); // Unregister the event handler of the Main Page auto frame = safe_cast(Window::Current->Content); auto mainPage = safe_cast(frame->Content); mainPage->UnregisterEventHandlers(); return frameService->HandleViewRelease() .then([weak, frameService]() { auto that = weak.Resolve(); that->RemoveWindowFromMap(frameService->GetViewId()); }, task_continuation_context::use_arbitrary()); } task App::SetupJumpList() { try { auto calculatorOptions = NavCategoryGroup::CreateCalculatorCategory(); auto jumpList = co_await JumpList::LoadCurrentAsync(); jumpList->SystemGroupKind = JumpListSystemGroupKind::None; jumpList->Items->Clear(); for (NavCategory^ option : calculatorOptions->Categories) { ViewMode mode = option->Mode; auto item = JumpListItem::CreateWithArguments(((int)mode).ToString(), L"ms-resource:///Resources/"+ NavCategory::GetNameResourceKey(mode)); item->Description = L"ms-resource:///Resources/" + NavCategory::GetNameResourceKey(mode); item->GroupName = L"ms-resource:///Resources/" + NavCategoryGroup::GetHeaderResourceKey(calculatorOptions->GroupType); item->Logo = ref new Uri("ms-appx:///Assets/" + mode.ToString() + ".png"); jumpList->Items->Append(item); } co_await jumpList->SaveAsync(); } catch(...) {} } void App::RemoveSecondaryWindow(_In_ WindowFrameService^ frameService) { // Shell does not allow killing the main window. if (m_mainViewId != frameService->GetViewId()) { RemoveWindowFromMap(frameService->GetViewId()); } } Frame^ App::CreateFrame() { auto frame = ref new Frame(); frame->FlowDirection = LocalizationService::GetInstance()->GetFlowDirection(); return frame; } /// /// Invoked when the application is launched normally by the end user. Other entry points /// will be used when the application is launched to open a specific file, to display /// search results, and so forth. /// /// Details about the launch request and process. void App::OnLaunched(LaunchActivatedEventArgs^ args) { TraceLogger::GetInstance().LogWindowLaunched(); if (args->PrelaunchActivated) { // If the app got pre-launch activated, then save that state in a flag m_preLaunched = true; TraceLogger::GetInstance().LogAppPrelaunchedBySystem(); } OnAppLaunch(args, args->Arguments); } void App::OnAppLaunch(IActivatedEventArgs^ args, String^ argument) { auto previousExecutionState = args->PreviousExecutionState; TraceLogger::GetInstance().LogOnAppLaunch(previousExecutionState.ToString()->Data()); // Uncomment the following lines to display frame-rate and per-frame CPU usage info. //#if _DEBUG // if (IsDebuggerPresent()) // { // DebugSettings->EnableFrameRateCounter = true; // } //#endif auto userSettings = ref new Windows::UI::ViewManagement::UISettings(); m_isAnimationEnabled = userSettings->AnimationsEnabled; args->SplashScreen->Dismissed += ref new TypedEventHandler(this, &App::DismissedEventHandler); auto rootFrame = dynamic_cast(Window::Current->Content); WeakReference weak(this); float minWindowWidth = static_cast(static_cast(this->Resources->Lookup(ApplicationResourceKeys::AppMinWindowWidth))); float minWindowHeight = static_cast(static_cast(this->Resources->Lookup(ApplicationResourceKeys::AppMinWindowHeight))); Size minWindowSize = SizeHelper::FromDimensions(minWindowWidth, minWindowHeight); ApplicationView^ appView = ApplicationView::GetForCurrentView(); ApplicationDataContainer^ localSettings = ApplicationData::Current->LocalSettings; // For very first launch, set the size of the calc as size of the default standard mode if (!localSettings->Values->HasKey(L"VeryFirstLaunch")) { localSettings->Values->Insert(ref new String(L"VeryFirstLaunch"), false); appView->SetPreferredMinSize(minWindowSize); appView->TryResizeView(minWindowSize); } else { appView->PreferredLaunchWindowingMode = ApplicationViewWindowingMode::Auto; } // Do not repeat app initialization when the Window already has content, // just ensure that the window is active if (rootFrame == nullptr) { if (!Windows::Foundation::Metadata::ApiInformation::IsTypePresent("Windows.Phone.UI.Input.HardwareButtons")) // PC Family { // Disable the system view activation policy during the first launch of the app // only for PC family devices and not for phone family devices try { ApplicationViewSwitcher::DisableSystemViewActivationPolicy(); } catch (Exception^ e) { // Log that DisableSystemViewActionPolicy didn't work } } // Create a Frame to act as the navigation context rootFrame = App::CreateFrame(); // When the navigation stack isn't restored navigate to the first page, // configuring the new page by passing required information as a navigation // parameter if (!rootFrame->Navigate(MainPage::typeid, argument)) { // We couldn't navigate to the main page, kill the app so we have a good // stack to debug throw std::bad_exception(); } SetMinWindowSizeAndActivate(rootFrame, minWindowSize); m_mainViewId = ApplicationView::GetForCurrentView()->Id; AddWindowToMap(WindowFrameService::CreateNewWindowFrameService(rootFrame, false, weak)); } else { // For first launch, LaunchStart is logged in constructor, this is for subsequent launches. TraceLogger::GetInstance().LogAppLaunchStart(); // !Phone check is required because even in continuum mode user interaction mode is Mouse not Touch if ((UIViewSettings::GetForCurrentView()->UserInteractionMode == UserInteractionMode::Mouse) && (!Windows::Foundation::Metadata::ApiInformation::IsTypePresent("Windows.Phone.UI.Input.HardwareButtons"))) { // If the pre-launch hasn't happened then allow for the new window/view creation if (!m_preLaunched) { auto newCoreAppView = CoreApplication::CreateNewView(); newCoreAppView->Dispatcher->RunAsync(CoreDispatcherPriority::Normal, ref new DispatchedHandler([args, argument, minWindowSize, weak]() { TraceLogger::GetInstance().LogNewWindowCreationBegin(ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread())); auto that = weak.Resolve(); if (that != nullptr) { auto rootFrame = App::CreateFrame(); SetMinWindowSizeAndActivate(rootFrame, minWindowSize); if (!rootFrame->Navigate(MainPage::typeid, argument)) { // We couldn't navigate to the main page, kill the app so we have a good // stack to debug throw std::bad_exception(); } auto frameService = WindowFrameService::CreateNewWindowFrameService(rootFrame, true, weak); that->AddWindowToMap(frameService); auto dispatcher = CoreWindow::GetForCurrentThread()->Dispatcher; auto safeFrameServiceCreation = std::make_shared(frameService, that); int newWindowId = ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread()); ActivationViewSwitcher^ activationViewSwitcher; auto activateEventArgs = dynamic_cast(args); if (activateEventArgs != nullptr) { activationViewSwitcher = activateEventArgs->ViewSwitcher; } if (activationViewSwitcher != nullptr) { activationViewSwitcher->ShowAsStandaloneAsync(newWindowId, ViewSizePreference::Default); safeFrameServiceCreation->SetOperationSuccess(true); } else { auto activatedEventArgs = dynamic_cast(args); if ((activatedEventArgs != nullptr) && (activatedEventArgs->CurrentlyShownApplicationViewId != 0)) { create_task(ApplicationViewSwitcher::TryShowAsStandaloneAsync( frameService->GetViewId(), ViewSizePreference::Default, activatedEventArgs->CurrentlyShownApplicationViewId, ViewSizePreference::Default )) .then([safeFrameServiceCreation](bool viewShown) { // SafeFrameServiceCreation is used to automatically remove the frame // from the list of frames if something goes bad. safeFrameServiceCreation->SetOperationSuccess(viewShown); }, task_continuation_context::use_current()); } } } TraceLogger::GetInstance().LogNewWindowCreationEnd(ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread())); })); } else { TraceLogger::GetInstance().LogNewWindowCreationBegin(ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread())); ActivationViewSwitcher^ activationViewSwitcher; auto activateEventArgs = dynamic_cast(args); if (activateEventArgs != nullptr) { activationViewSwitcher = activateEventArgs->ViewSwitcher; } if (activationViewSwitcher != nullptr) { activationViewSwitcher->ShowAsStandaloneAsync(ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread()), ViewSizePreference::Default); TraceLogger::GetInstance().LogNewWindowCreationEnd(ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread())); TraceLogger::GetInstance().LogPrelaunchedAppActivatedByUser(); } else { TraceLogger::GetInstance().LogError(L"Null_ActivationViewSwitcher"); } } // Set the preLaunched flag to false m_preLaunched = false; } else // for touch devices { if (rootFrame->Content == nullptr) { // When the navigation stack isn't restored navigate to the first page, // configuring the new page by passing required information as a navigation // parameter if (!rootFrame->Navigate(MainPage::typeid, argument)) { // We couldn't navigate to the main page, // kill the app so we have a good stack to debug throw std::bad_exception(); } } if (!Windows::Foundation::Metadata::ApiInformation::IsTypePresent("Windows.Phone.UI.Input.HardwareButtons")) { // for tablet mode: since system view activation policy is disabled so do ShowAsStandaloneAsync if activationViewSwitcher exists in activationArgs ActivationViewSwitcher^ activationViewSwitcher; auto activateEventArgs = dynamic_cast(args); if (activateEventArgs != nullptr) { activationViewSwitcher = activateEventArgs->ViewSwitcher; } if (activationViewSwitcher != nullptr) { auto viewId = safe_cast(args)->CurrentlyShownApplicationViewId; if (viewId != 0) { activationViewSwitcher->ShowAsStandaloneAsync(viewId); } } } // Ensure the current window is active Window::Current->Activate(); } } } void App::SetMinWindowSizeAndActivate(Frame^ rootFrame, Size minWindowSize) { // SetPreferredMinSize should always be called before Window::Activate ApplicationView^ appView = ApplicationView::GetForCurrentView(); appView->SetPreferredMinSize(minWindowSize); // Place the frame in the current Window Window::Current->Content = rootFrame; Window::Current->Activate(); } void App::RegisterDependencyProperties() { NarratorNotifier::RegisterDependencyProperties(); } void App::OnActivated(IActivatedEventArgs^ args) { if (args->Kind == ActivationKind::Protocol) { TraceLogger::GetInstance().LogWindowActivated(); // We currently don't pass the uri as an argument, // and handle any protocol launch as a normal app launch. OnAppLaunch(args, nullptr); } } void App::DismissedEventHandler(SplashScreen^ sender, Object^ e) { SetupJumpList(); } float App::GetAppWindowHeight() { CoreWindow^ window = CoreWindow::GetForCurrentThread(); return window->Bounds.Height; }