// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See LICENSE in the project root for license information. using System; using System.Collections; using UnityEngine; #if !UNITY_EDITOR && UNITY_WSA using System.Threading.Tasks; using Windows.ApplicationModel.Core; using Windows.UI.Core; using Windows.UI.ViewManagement; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; #endif namespace HoloToolkit.Unity { public delegate void ReturnValueCallback<TReturnValue>(TReturnValue returnValue); /// <summary> /// ApplicationViewManager ( For XAML UWP project) can switch app to Plan View, populate an Application View (New Window of UAP), /// then navigate the Window root frame to a page. /// After the page's logic called 'CallbackReturnValue' method, the newly created Application View will be closed, and the system will switch back to your Full3D view. /// The coroutine which was waiting the callback will get the return value. /// </summary> public class ApplicationViewManager : MonoBehaviour { private void Start() { #if !UNITY_EDITOR && UNITY_WSA UnityEngine.WSA.Application.InvokeOnUIThread( () => { Full3DViewId = ApplicationView.GetForCurrentView().Id; }, true); #endif } #if !UNITY_EDITOR && UNITY_WSA static int Full3DViewId { get; set; } static System.Collections.Concurrent.ConcurrentDictionary<int, Action<object>> CallbackDictionary = new System.Collections.Concurrent.ConcurrentDictionary<int, Action<object>>(); #endif /// <summary> /// Call this method with Application View Dispatcher, or in Application View Thread, will return to Full3D View and close Application View /// </summary> /// <param name="returnValue">The return value of the XAML View Execution</param> #if !UNITY_EDITOR && UNITY_WSA public static async void CallbackReturnValue(object returnValue) { var viewId = ApplicationView.GetForCurrentView().Id; var view = CoreApplication.GetCurrentView(); if (CallbackDictionary.TryRemove(viewId, out var cb)) { try { cb(returnValue); } catch (Exception) { } await ApplicationViewSwitcher.TryShowAsStandaloneAsync(ApplicationViewManager.Full3DViewId).AsTask(); view.CoreWindow.Close(); } } #else public static void CallbackReturnValue(object returnValue) { } #endif /// <summary> /// Call this method in Unity App Thread can switch to Plan View, create and show a new XAML View. /// </summary> /// <typeparam name="TReturnValue"></typeparam> /// <param name="xamlPageName"></param> /// <param name="callback"></param> /// <returns></returns> public IEnumerator OnLaunchXamlView<TReturnValue>(string xamlPageName, Action<TReturnValue> callback, object pageNavigateParameter = null) { bool isCompleted = false; #if !UNITY_EDITOR && UNITY_WSA object returnValue = null; CoreApplicationView newView = CoreApplication.CreateNewView(); int newViewId = 0; var dispt = newView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { //This happens when User switch view back to Main App manually void CoreWindow_VisibilityChanged(CoreWindow sender, VisibilityChangedEventArgs args) { if (args.Visible == false) { CallbackReturnValue(null); } } newView.CoreWindow.VisibilityChanged += CoreWindow_VisibilityChanged; Frame frame = new Frame(); var pageType = Type.GetType(Windows.UI.Xaml.Application.Current.GetType().AssemblyQualifiedName.Replace(".App,", $".{xamlPageName},")); var appv = ApplicationView.GetForCurrentView(); newViewId = appv.Id; var cb = new Action<object>(rval => { returnValue = rval; isCompleted = true; }); frame.Navigate(pageType,pageNavigateParameter); CallbackDictionary[newViewId] = cb; Window.Current.Content = frame; Window.Current.Activate(); }).AsTask(); yield return new WaitUntil(() => dispt.IsCompleted || dispt.IsCanceled || dispt.IsFaulted); Task viewShownTask = null; UnityEngine.WSA.Application.InvokeOnUIThread( () => { viewShownTask = ApplicationViewSwitcher.TryShowAsStandaloneAsync(newViewId).AsTask(); }, true); yield return new WaitUntil(() => viewShownTask.IsCompleted || viewShownTask.IsCanceled || viewShownTask.IsFaulted); yield return new WaitUntil(() => isCompleted); try { if (returnValue is TReturnValue) { callback?.Invoke((TReturnValue)returnValue); } else { callback?.Invoke(default(TReturnValue)); } } catch (Exception ex) { Debug.LogError(ex); } #else isCompleted = true; yield return new WaitUntil(() => isCompleted); #endif } } }