HoloLens (prima generazione) e Azure 311 - Microsoft Graph

Nota

Le esercitazioni di Mixed Reality Academy sono state progettate in base a HoloLens (prima generazione) e ai visori VR immersive di realtà mista. Pertanto, riteniamo importante lasciarle a disposizione degli sviluppatori a cui serve ancora materiale sussidiario per lo sviluppo di questi dispositivi. Queste esercitazioni non verranno aggiornate con i set di strumenti o le interazioni più recenti usati per HoloLens 2. Rimarranno invariate per consentire di continuare a lavorare sui dispositivi supportati. Saranno disponibili nuove serie di esercitazioni che verranno pubblicate in futuro che mostreranno come sviluppare per HoloLens 2. Questo avviso verrà aggiornato con un collegamento a tali esercitazioni quando vengono pubblicati.

In questo corso si apprenderà come usare Microsoft Graph per accedere all'account Microsoft usando l'autenticazione sicura all'interno di un'applicazione di realtà mista. Verranno quindi recuperate e visualizzate le riunioni pianificate nell'interfaccia dell'applicazione.

Screenshot che mostra le riunioni pianificate nell'interfaccia dell'applicazione.

Microsoft Graph è un set di API progettate per consentire l'accesso a molti dei servizi microsoft. Microsoft descrive Microsoft Graph come matrice di risorse connesse dalle relazioni, ovvero consente a un'applicazione di accedere a tutti i tipi di dati utente connessi. Per altre informazioni, visitare la pagina Microsoft Graph.

Lo sviluppo includerà la creazione di un'app in cui l'utente verrà incaricato di guardare e quindi toccare una sfera, che richiederà all'utente di accedere in modo sicuro a un account Microsoft. Dopo aver eseguito l'accesso al proprio account, l'utente sarà in grado di visualizzare un elenco di riunioni pianificate per il giorno.

Dopo aver completato questo corso, si avrà un'applicazione HoloLens di realtà mista, che sarà in grado di eseguire le operazioni seguenti:

  1. Usando il movimento Tap, toccare su un oggetto, che richiederà all'utente di accedere a un account Microsoft (spostandosi dall'app per accedere e quindi tornare all'app di nuovo).
  2. Visualizzare un elenco di riunioni pianificate per il giorno.

Nell'applicazione è possibile integrare i risultati con la progettazione. Questo corso è progettato per insegnare come integrare un servizio di Azure con il progetto Unity. È il tuo lavoro per usare le conoscenze acquisite da questo corso per migliorare l'applicazione di realtà mista.

Supporto di dispositivi

Corso HoloLens Visori VR immersive
MR e Azure 311: Microsoft Graph ✔️

Prerequisiti

Nota

Questa esercitazione è progettata per gli sviluppatori che hanno esperienza di base con Unity e C#. Tenere presente anche che i prerequisiti e le istruzioni scritte all'interno di questo documento rappresentano ciò che è stato testato e verificato al momento della scrittura (luglio 2018). Si è liberi di usare il software più recente, come elencato all'interno dell'articolo degli strumenti di installazione , anche se non dovrebbe essere considerato che le informazioni in questo corso corrisponderanno perfettamente a ciò che troverete nel software più recente rispetto a quello elencato di seguito.

Per questo corso è consigliabile usare l'hardware e il software seguenti:

Prima di iniziare

  1. Per evitare problemi durante la compilazione di questo progetto, è consigliabile creare il progetto menzionato in questa esercitazione in una cartella radice o quasi radice (i percorsi di cartelle lunghe possono causare problemi in fase di compilazione).
  2. Configurare e testare HoloLens. Se è necessario supportare la configurazione di HoloLens, assicurarsi di visitare l'articolo relativo alla configurazione di HoloLens.
  3. È consigliabile eseguire l'ottimizzazione della calibrazione e del sensore quando si inizia a sviluppare una nuova app HoloLens (a volte può aiutare a eseguire queste attività per ogni utente).

Per informazioni sulla calibrazione, seguire questo collegamento all'articolo Di calibrazione HoloLens.

Per informazioni sull'ottimizzazione del sensore, seguire questo collegamento all'articolo Ottimizzazione del sensore HoloLens.

Capitolo 1 - Creare l'app nel portale di registrazione applicazioni

Per iniziare, è necessario creare e registrare l'applicazione nel portale di registrazione dell'applicazione.

In questo capitolo si troverà anche la chiave del servizio che consente di effettuare chiamate a Microsoft Graph per accedere al contenuto dell'account.

  1. Passare al portale di registrazione delle applicazioni Microsoft e accedere con l'account Microsoft. Dopo aver eseguito l'accesso, si verrà reindirizzati al portale di registrazione dell'applicazione.

  2. Nella sezione Applicazioni personali fare clic sul pulsante Aggiungi un'app.

    Screenshot che mostra dove selezionare Aggiungi un'app.

    Importante

    Il portale di registrazione dell'applicazione può essere diverso, a seconda che in precedenza si sia lavorato con Microsoft Graph. Gli screenshot seguenti visualizzano queste diverse versioni.

  3. Aggiungere un nome per l'applicazione e fare clic su Crea.

    Screenshot che mostra dove aggiungere un nome per l'applicazione.

  4. Dopo aver creato l'applicazione, si verrà reindirizzati alla pagina principale dell'applicazione. Copiare l'ID applicazione e assicurarsi di prendere nota di questo valore da qualche parte sicuro, verrà usato presto nel codice.

    Screenshot che mostra dove visualizzare l'ID applicazione.

  5. Nella sezione Piattaforme assicurarsi che l'applicazione nativa venga visualizzata. Se non fare clic su Aggiungi piattaforma e selezionare Applicazione nativa.

    Screenshot che evidenzia la sezione Applicazione nativa.

  6. Scorrere verso il basso nella stessa pagina e nella sezione denominata Microsoft Graph Permissions sarà necessario aggiungere autorizzazioni aggiuntive per l'applicazione. Fare clic su Aggiungi accanto a Autorizzazioni delegate.

    Screenshot che mostra dove selezionare Aggiungi accanto a Autorizzazioni delegate.

  7. Poiché si vuole che l'applicazione accinga al calendario dell'utente, selezionare la casella denominata Calendars.Read e fare clic su OK.

    Screenshot che mostra la casella di controllo Calendars.Read.

  8. Scorrere verso il basso e fare clic sul pulsante Salva .

    Screenshot che mostra dove selezionare Salva.

  9. Il salvataggio verrà confermato e sarà possibile disconnettersi dal portale di registrazione dell'applicazione.

Capitolo 2 - Configurare il progetto Unity

Di seguito è riportata una configurazione tipica per lo sviluppo con realtà mista e, di conseguenza, è un modello valido per altri progetti.

  1. Aprire Unity e fare clic su Nuovo.

    Screenshot che mostra l'interfaccia Unity.

  2. È necessario specificare un nome di progetto Unity. Inserire MSGraphMR. Assicurarsi che il modello di progetto sia impostato su 3D. Impostare La posizione su un punto appropriato per l'utente (ricordarsi, più vicino alle directory radice è meglio). Fare quindi clic su Crea progetto.

    Screenshot che mostra dove selezionare Crea progetto.

  3. Con Unity aperto, vale la pena verificare che l'editor di script predefinito sia impostato su Visual Studio. Passare a Modifica>preferenze e quindi dalla nuova finestra passare a Strumenti esterni. Modificare l'editor script esterno in Visual Studio 2017. Chiudere la finestra Preferenze .

    Screenshot che mostra dove impostare l'editor script esterno in Visual Studio 2017.

  4. Passare aImpostazioni di compilazionefile> e selezionare piattaforma UWP (Universal Windows Platform), quindi fare clic sul pulsante Cambia piattaforma per applicare la selezione.

    Screenshot che mostra dove selezionare Cambia piattaforma.

  5. Sebbene sia ancora inImpostazioni di compilazionefile>, assicurarsi che:

    1. Dispositivo di destinazione è impostato su HoloLens

    2. Il tipo di compilazione è impostato su D3D

    3. SDK è impostato su Più recente installato

    4. La versione di Visual Studio è impostata su Più recente installata

    5. Compilare ed eseguire è impostato su Computer locale

    6. Salvare la scena e aggiungerla alla compilazione.

      1. Eseguire questa operazione selezionando Aggiungi scene aperte. Verrà visualizzata una finestra di salvataggio.

        Screenshot che mostra dove selezionare Aggiungi scene aperte.

      2. Creare una nuova cartella per questa e qualsiasi scena futura. Selezionare il pulsante Nuova cartella per creare una nuova cartella, denominarla Scene.

        Screenshot che mostra dove assegnare un nome alla nuova cartella.

      3. Aprire la cartella Scene appena creata e quindi nel campo Nome file: testo, digitare MR_ComputerVisionScene, quindi fare clic su Salva.

        Screenshot che mostra dove digitare il nome del file.

        Importante

        Tenere presente che è necessario salvare le scene di Unity all'interno della cartella Asset , perché devono essere associate al progetto Unity. La creazione della cartella scene (e altre cartelle simili) è un modo tipico per strutturare un progetto Unity.

    7. Le impostazioni rimanenti, in Impostazioni di compilazione, devono essere lasciate come predefinite per il momento.

  6. Nella finestra Impostazioni di compilazione fare clic sul pulsante Impostazioni lettore , verrà aperto il pannello correlato nello spazio in cui si trova Il controllo .

    Screenshot che mostra la finestra di dialogo Impostazioni lettore.

  7. In questo pannello è necessario verificare alcune impostazioni:

    1. Nella scheda Altre impostazioni :

      1. La versione del runtimedi script deve essere sperimentale (equivalente.NET 4.6), che attiverà una necessità di riavviare l'editor.

      2. Scripting Back-end deve essere .NET

      3. Il livello di compatibilità api deve essere .NET 4.6

        Screenshot che mostra dove controllare il livello di compatibilità dell'API.

    2. Nella scheda Impostazioni di pubblicazione , in Funzionalità selezionare:

      • InternetClient

        Screenshot che mostra dove selezionare l'opzione InternetClient.

    3. Più avanti nel pannello, in Impostazioni XR (disponibili sotto Impostazioni di pubblicazione), selezionare Virtual Reality Supportato, assicurarsi che l'SDK di Windows Mixed Reality sia stato aggiunto.

      Screenshot che mostra dove aggiungere Windows Mixed Reality SDK.

  8. Torna in Impostazioni di compilazione, i progetti C# di Unity non sono più disattivati; selezionare la casella di controllo accanto a questa.

  9. Chiudere la finestra Build Settings (Impostazioni compilazione).

  10. Salvare la scena e il progetto (FILE>SAVE SCENE /FILE>SAVE PROJECT).

Capitolo 3 - Importare librerie in Unity

Importante

Se si desidera ignorare il componente Set up di Unity di questo corso e continuare direttamente nel codice, è possibile scaricare questo pacchetto azure-MR-311.unitypackage, importarlo nel progetto come pacchetto personalizzato e quindi continuare dal capitolo 5.

Per usare Microsoft Graph all'interno di Unity, è necessario usare la DLL Microsoft.Identity.Client . È tuttavia possibile usare Microsoft Graph SDK, ma richiederà l'aggiunta di un pacchetto NuGet dopo aver compilato il progetto Unity (ovvero la modifica del progetto dopo la compilazione). È considerato più semplice importare le DLL necessarie direttamente in Unity.

Nota

È attualmente presente un problema noto in Unity che richiede la riconfigurazione dei plug-in dopo l'importazione. Questi passaggi (4 - 7 in questa sezione) non saranno più necessari dopo che il bug è stato risolto.

Per importare Microsoft Graph nel proprio progetto, scaricare il file MSGraph_LabPlugins.zip. Questo pacchetto è stato creato con le versioni delle librerie testate.

Per altre informazioni su come aggiungere DLL personalizzate al progetto Unity, seguire questo collegamento.

Per importare il pacchetto:

  1. Aggiungere il pacchetto Unity a Unity usando l'opzione> di menuAsset Importa pacchetto>personalizzato pacchetto. Selezionare il pacchetto appena scaricato.

  2. Nella casella Importa pacchetto Unity visualizzata assicurarsi che tutti i plug-in (e inclusi) siano selezionati.

    Screenshot che mostra i parametri di configurazione selezionati in Plug-in.

  3. Fare clic sul pulsante Importa per aggiungere gli elementi al progetto.

  4. Passare alla cartella MSGraph in Plug-in nel pannello di progetto e selezionare il plug-in denominato Microsoft.Identity.Client.

    Screenshot che mostra il plug-in Microsoft.Identity.Client.

  5. Con il plug-in selezionato, assicurarsi che Any Platform sia deselezionato, quindi assicurarsi che WSAPlayer sia deselezionato, quindi fare clic su Applica. Si tratta solo di confermare che i file sono configurati correttamente.

    Screenshot che mostra dove verificare che Qualsiasi piattaforma e WSAPlayer non siano controllati.

    Nota

    Contrassegnare questi plug-in consente di configurarli solo nell'editor di Unity. Esistono diversi set di DLL nella cartella WSA che verrà usata dopo l'esportazione del progetto da Unity come applicazione di Windows universale.

  6. È quindi necessario aprire la cartella WSA all'interno della cartella MSGraph . Verrà visualizzata una copia dello stesso file appena configurato. Selezionare il file e quindi nel controllo:

    • assicurarsi che Any Platform sia deselezionato e che venga selezionatosoloWSAPlayer.

    • Assicurarsi che SDK sia impostato su UWP e il back-end di scripting sia impostato su Dot Net

    • Assicurarsi che non sia selezionatal'operazione.

      Screenshot che mostra che Non elaborare è selezionato.

  7. Fare clic su Applica.

Capitolo 4 - Configurazione della fotocamera

Durante questo capitolo verrà configurata la fotocamera principale della scena:

  1. Nel pannello gerarchia selezionare la fotocamera principale.

  2. Dopo aver selezionato, sarà possibile visualizzare tutti i componenti della fotocamera principale nel pannello Controllo .

    1. L'oggetto Camera deve essere denominato Main Camera (nota l'ortografia!)

    2. Il tag della fotocamera principale deve essere impostato su MainCamera (nota l'ortografia!)

    3. Assicurarsi che la posizione di trasformazione sia impostata su 0, 0, 0

    4. Impostare flag cancella su colore a tinta unita

    5. Impostare il colore di sfondo del componente fotocamera su nero, alfa 0(codice esadecimale: #000000000)

      Screenshot che evidenzia dove impostare il colore di sfondo.

  3. La struttura dell'oggetto finale nel pannello gerarchia deve essere simile a quella illustrata nell'immagine seguente:

    Screenshot che mostra la struttura dell'oggetto finale nel pannello gerarchia.

Capitolo 5 - Creare una classe MeetingsUI

Il primo script da creare è MeetingsUI, responsabile dell'hosting e della popolamento dell'interfaccia utente dell'applicazione (messaggio di benvenuto, istruzioni e dettagli delle riunioni).

Per creare questa classe:

  1. Fare clic con il pulsante destro del mouse sulla cartella Asset nel pannello di progetto, quindi selezionare Crea>cartella. Assegnare un nome alla cartella Script.

    Screenshot che mostra dove trovare la cartella Asset.Screenshot che mostra dove creare la cartella Script.

  2. Aprire la cartella Script e quindi, all'interno di tale cartella, fare clic con il pulsante destro del mouse su Crea>script C#. Assegnare al nome lo script MeetingsUI.

    Screenshot che mostra dove creare la cartella MeetingsUI.

  3. Fare doppio clic sul nuovo script MeetingsUI per aprirlo con Visual Studio.

  4. Inserire gli spazi dei nomi seguenti:

    using System;
    using UnityEngine;
    
  5. All'interno della classe inserire le variabili seguenti:

        /// <summary>
        /// Allows this class to behave like a singleton
        /// </summary>
        public static MeetingsUI Instance;
    
        /// <summary>
        /// The 3D text of the scene
        /// </summary>
        private TextMesh _meetingDisplayTextMesh;
    
  6. Sostituire quindi il metodo Start() e aggiungere un metodo Awake(). Questi verranno chiamati quando la classe inizializza:

        /// <summary>
        /// Called on initialization
        /// </summary>
        void Awake()
        {
            Instance = this;
        }
    
        /// <summary>
        /// Called on initialization, after Awake
        /// </summary>
        void Start ()
        {
            // Creating the text mesh within the scene
            _meetingDisplayTextMesh = CreateMeetingsDisplay();
        }
    
  7. Aggiungere i metodi responsabili della creazione dell'interfaccia utente riunioni e popolarla con le riunioni correnti quando richiesto:

        /// <summary>
        /// Set the welcome message for the user
        /// </summary>
        internal void WelcomeUser(string userName)
        {
            if(!string.IsNullOrEmpty(userName))
            {
                _meetingDisplayTextMesh.text = $"Welcome {userName}";
            }
            else 
            {
                _meetingDisplayTextMesh.text = "Welcome";
            }
        }
    
        /// <summary>
        /// Set up the parameters for the UI text
        /// </summary>
        /// <returns>Returns the 3D text in the scene</returns>
        private TextMesh CreateMeetingsDisplay()
        {
            GameObject display = new GameObject();
            display.transform.localScale = new Vector3(0.03f, 0.03f, 0.03f);
            display.transform.position = new Vector3(-3.5f, 2f, 9f);
            TextMesh textMesh = display.AddComponent<TextMesh>();
            textMesh.anchor = TextAnchor.MiddleLeft;
            textMesh.alignment = TextAlignment.Left;
            textMesh.fontSize = 80;
            textMesh.text = "Welcome! \nPlease gaze at the button" +
                "\nand use the Tap Gesture to display your meetings";
    
            return textMesh;
        }
    
        /// <summary>
        /// Adds a new Meeting in the UI by chaining the existing UI text
        /// </summary>
        internal void AddMeeting(string subject, DateTime dateTime, string location)
        {
            string newText = $"\n{_meetingDisplayTextMesh.text}\n\n Meeting,\nSubject: {subject},\nToday at {dateTime},\nLocation: {location}";
    
            _meetingDisplayTextMesh.text = newText;
        }
    
  8. Eliminare il metodo Update() e salvare le modifiche in Visual Studio prima di tornare a Unity.

Capitolo 6 - Creare la classe Graph

Lo script successivo da creare è lo script Graph . Questo script è responsabile dell'esecuzione delle chiamate per autenticare l'utente e recuperare le riunioni pianificate per il giorno corrente dal calendario dell'utente.

Per creare questa classe:

  1. Fare doppio clic sulla cartella Script per aprirla.

  2. Fare clic con il pulsante destro del mouse all'interno della cartella Script , fare clic su Crea>script C#. Assegnare un nome al grafico dello script.

  3. Fare doppio clic sullo script per aprirlo con Visual Studio.

  4. Inserire gli spazi dei nomi seguenti:

    using System.Collections.Generic;
    using UnityEngine;
    using Microsoft.Identity.Client;
    using System;
    using System.Threading.Tasks;
    
    #if !UNITY_EDITOR && UNITY_WSA
    using System.Net.Http;
    using System.Net.Http.Headers;
    using Windows.Storage;
    #endif
    

    Importante

    Si noterà che parti del codice in questo script vengono racchiuse in direttive precompilazione, in modo da evitare problemi con le librerie durante la compilazione della soluzione di Visual Studio.

  5. Eliminare i metodi Start() e Update(), perché non verranno usati.

  6. All'esterno della classe Graph , inserire gli oggetti seguenti, necessari per deserializzare l'oggetto JSON che rappresenta le riunioni pianificate giornaliere:

    /// <summary>
    /// The object hosting the scheduled meetings
    /// </summary>
    [Serializable]
    public class Rootobject
    {
        public List<Value> value;
    }
    
    [Serializable]
    public class Value
    {
        public string subject { get; set; }
        public StartTime start { get; set; }
        public Location location { get; set; }
    }
    
    [Serializable]
    public class StartTime
    {
        public string dateTime;
    
        private DateTime? _startDateTime;
        public DateTime StartDateTime
        {
            get
            {
                if (_startDateTime != null)
                    return _startDateTime.Value;
                DateTime dt;
                DateTime.TryParse(dateTime, out dt);
                _startDateTime = dt;
                return _startDateTime.Value;
            }
        }
    }
    
    [Serializable]
    public class Location
    {
        public string displayName { get; set; }
    }
    
  7. All'interno della classe Graph aggiungere le variabili seguenti:

        /// <summary>
        /// Insert your Application Id here
        /// </summary>
        private string _appId = "-- Insert your Application Id here --";
    
        /// <summary>
        /// Application scopes, determine Microsoft Graph accessibility level to user account
        /// </summary>
        private IEnumerable<string> _scopes = new List<string>() { "User.Read", "Calendars.Read" };
    
        /// <summary>
        /// Microsoft Graph API, user reference
        /// </summary>
        private PublicClientApplication _client;
    
        /// <summary>
        /// Microsoft Graph API, authentication
        /// </summary>
        private AuthenticationResult _authResult;
    
    

    Nota

    Modificare il valore appId in modo che sia l'ID app annotato nel capitolo 1, passaggio 4. Questo valore deve corrispondere a quello visualizzato nel portale di registrazione dell'applicazione, nella pagina di registrazione dell'applicazione.

  8. All'interno della classe Graph aggiungere i metodi SignInAsync() e AquireTokenAsync(), che richiederà all'utente di inserire le credenziali di accesso.

        /// <summary>
        /// Begin the Sign In process using Microsoft Graph Library
        /// </summary>
        internal async void SignInAsync()
        {
    #if !UNITY_EDITOR && UNITY_WSA
            // Set up Grap user settings, determine if needs auth
            ApplicationDataContainer localSettings = ApplicationData.Current.LocalSettings;
            string userId = localSettings.Values["UserId"] as string;
            _client = new PublicClientApplication(_appId);
    
            // Attempt authentication
            _authResult = await AcquireTokenAsync(_client, _scopes, userId);
    
            // If authentication is successful, retrieve the meetings
            if (!string.IsNullOrEmpty(_authResult.AccessToken))
            {
                // Once Auth as been completed, find the meetings for the day
                await ListMeetingsAsync(_authResult.AccessToken);
            }
    #endif
        }
    
        /// <summary>
        /// Attempt to retrieve the Access Token by either retrieving
        /// previously stored credentials or by prompting user to Login
        /// </summary>
        private async Task<AuthenticationResult> AcquireTokenAsync(
            IPublicClientApplication app, IEnumerable<string> scopes, string userId)
        {
            IUser user = !string.IsNullOrEmpty(userId) ? app.GetUser(userId) : null;
            string userName = user != null ? user.Name : "null";
    
            // Once the User name is found, display it as a welcome message
            MeetingsUI.Instance.WelcomeUser(userName);
    
            // Attempt to Log In the user with a pre-stored token. Only happens
            // in case the user Logged In with this app on this device previously
            try
            {
                _authResult = await app.AcquireTokenSilentAsync(scopes, user);
            }
            catch (MsalUiRequiredException)
            {
                // Pre-stored token not found, prompt the user to log-in 
                try
                {
                    _authResult = await app.AcquireTokenAsync(scopes);
                }
                catch (MsalException msalex)
                {
                    Debug.Log($"Error Acquiring Token: {msalex.Message}");
                    return _authResult;
                }
            }
    
            MeetingsUI.Instance.WelcomeUser(_authResult.User.Name);
    
    #if !UNITY_EDITOR && UNITY_WSA
            ApplicationData.Current.LocalSettings.Values["UserId"] = 
            _authResult.User.Identifier;
    #endif
            return _authResult;
        }
    
  9. Aggiungere i due metodi seguenti:

    1. BuildTodayCalendarEndpoint(), che compila l'URI che specifica il giorno e l'intervallo di tempo, in cui vengono recuperate le riunioni pianificate.

    2. ListMeetingsAsync(), che richiede le riunioni pianificate da Microsoft Graph.

        /// <summary>
        /// Build the endpoint to retrieve the meetings for the current day.
        /// </summary>
        /// <returns>Returns the Calendar Endpoint</returns>
        public string BuildTodayCalendarEndpoint()
        {
            DateTime startOfTheDay = DateTime.Today.AddDays(0);
            DateTime endOfTheDay = DateTime.Today.AddDays(1);
            DateTime startOfTheDayUTC = startOfTheDay.ToUniversalTime();
            DateTime endOfTheDayUTC = endOfTheDay.ToUniversalTime();
    
            string todayDate = startOfTheDayUTC.ToString("o");
            string tomorrowDate = endOfTheDayUTC.ToString("o");
            string todayCalendarEndpoint = string.Format(
                "https://graph.microsoft.com/v1.0/me/calendarview?startdatetime={0}&enddatetime={1}",
                todayDate,
                tomorrowDate);
    
            return todayCalendarEndpoint;
        }
    
        /// <summary>
        /// Request all the scheduled meetings for the current day.
        /// </summary>
        private async Task ListMeetingsAsync(string accessToken)
        {
    #if !UNITY_EDITOR && UNITY_WSA
            var http = new HttpClient();
    
            http.DefaultRequestHeaders.Authorization = 
            new AuthenticationHeaderValue("Bearer", accessToken);
            var response = await http.GetAsync(BuildTodayCalendarEndpoint());
    
            var jsonResponse = await response.Content.ReadAsStringAsync();
    
            Rootobject rootObject = new Rootobject();
            try
            {
                // Parse the JSON response.
                rootObject = JsonUtility.FromJson<Rootobject>(jsonResponse);
    
                // Sort the meeting list by starting time.
                rootObject.value.Sort((x, y) => DateTime.Compare(x.start.StartDateTime, y.start.StartDateTime));
    
                // Populate the UI with the meetings.
                for (int i = 0; i < rootObject.value.Count; i++)
                {
                    MeetingsUI.Instance.AddMeeting(rootObject.value[i].subject,
                                                rootObject.value[i].start.StartDateTime.ToLocalTime(),
                                                rootObject.value[i].location.displayName);
                }
            }
            catch (Exception ex)
            {
                Debug.Log($"Error = {ex.Message}");
                return;
            }
    #endif
        }
    
  10. Lo script Graph è stato completato. Salvare le modifiche in Visual Studio prima di tornare a Unity.

Capitolo 7 - Creare lo script GazeInput

A questo momento si creerà GazeInput. Questa classe gestisce e tiene traccia dello sguardo dell'utente, usando un Raycast proveniente dalla fotocamera principale, proiettando in avanti.

Per creare lo script:

  1. Fare doppio clic sulla cartella Scripts per aprirla.

  2. Fare clic con il pulsante destro del mouse all'interno della cartella Script , scegliere Crea>script C#. Assegnare allo script il nome GazeInput.

  3. Fare doppio clic sullo script per aprirlo con Visual Studio.

  4. Modificare il codice degli spazi dei nomi in modo che corrisponda a quello riportato di seguito, insieme all'aggiunta del tag '[System.Serializable]' sopra la classe GazeInput , in modo che possa essere serializzata:

    using UnityEngine;
    
    /// <summary>
    /// Class responsible for the User's Gaze interactions
    /// </summary>
    [System.Serializable]
    public class GazeInput : MonoBehaviour
    {
    
  5. All'interno della classe GazeInput aggiungere le variabili seguenti:

        [Tooltip("Used to compare whether an object is to be interacted with.")]
        internal string InteractibleTag = "SignInButton";
    
        /// <summary>
        /// Length of the gaze
        /// </summary>
        internal float GazeMaxDistance = 300;
    
        /// <summary>
        /// Object currently gazed
        /// </summary>
        internal GameObject FocusedObject { get; private set; }
    
        internal GameObject oldFocusedObject { get; private set; }
    
        internal RaycastHit HitInfo { get; private set; }
    
        /// <summary>
        /// Cursor object visible in the scene
        /// </summary>
        internal GameObject Cursor { get; private set; }
    
        internal bool Hit { get; private set; }
    
        internal Vector3 Position { get; private set; }
    
        internal Vector3 Normal { get; private set; }
    
        private Vector3 _gazeOrigin;
    
        private Vector3 _gazeDirection;
    
  6. Aggiungere il metodo CreateCursor() per creare il cursore HoloLens nella scena e chiamare il metodo dal metodo Start():

        /// <summary>
        /// Start method used upon initialisation.
        /// </summary>
        internal virtual void Start()
        {
            FocusedObject = null;
            Cursor = CreateCursor();
        }
    
        /// <summary>
        /// Method to create a cursor object.
        /// </summary>
        internal GameObject CreateCursor()
        {
            GameObject newCursor = GameObject.CreatePrimitive(PrimitiveType.Sphere);
            newCursor.SetActive(false);
            // Remove the collider, so it doesn't block raycast.
            Destroy(newCursor.GetComponent<SphereCollider>());
            newCursor.transform.localScale = new Vector3(0.05f, 0.05f, 0.05f);
            Material mat = new Material(Shader.Find("Diffuse"));
            newCursor.GetComponent<MeshRenderer>().material = mat;
            mat.color = Color.HSVToRGB(0.0223f, 0.7922f, 1.000f);
            newCursor.SetActive(true);
    
            return newCursor;
        }
    
  7. I metodi seguenti abilitano lo sguardo fisso Raycast e tengono traccia degli oggetti con stato attivo.

    /// <summary>
    /// Called every frame
    /// </summary>
    internal virtual void Update()
    {
        _gazeOrigin = Camera.main.transform.position;
    
        _gazeDirection = Camera.main.transform.forward;
    
        UpdateRaycast();
    }
    /// <summary>
    /// Reset the old focused object, stop the gaze timer, and send data if it
    /// is greater than one.
    /// </summary>
    private void ResetFocusedObject()
    {
        // Ensure the old focused object is not null.
        if (oldFocusedObject != null)
        {
            if (oldFocusedObject.CompareTag(InteractibleTag))
            {
                // Provide the 'Gaze Exited' event.
                oldFocusedObject.SendMessage("OnGazeExited", SendMessageOptions.DontRequireReceiver);
            }
        }
    }
    
        private void UpdateRaycast()
        {
            // Set the old focused gameobject.
            oldFocusedObject = FocusedObject;
            RaycastHit hitInfo;
    
            // Initialise Raycasting.
            Hit = Physics.Raycast(_gazeOrigin,
                _gazeDirection,
                out hitInfo,
                GazeMaxDistance);
                HitInfo = hitInfo;
    
            // Check whether raycast has hit.
            if (Hit == true)
            {
                Position = hitInfo.point;
                Normal = hitInfo.normal;
    
                // Check whether the hit has a collider.
                if (hitInfo.collider != null)
                {
                    // Set the focused object with what the user just looked at.
                    FocusedObject = hitInfo.collider.gameObject;
                }
                else
                {
                    // Object looked on is not valid, set focused gameobject to null.
                    FocusedObject = null;
                }
            }
            else
            {
                // No object looked upon, set focused gameobject to null.
                FocusedObject = null;
    
                // Provide default position for cursor.
                Position = _gazeOrigin + (_gazeDirection * GazeMaxDistance);
    
                // Provide a default normal.
                Normal = _gazeDirection;
            }
    
            // Lerp the cursor to the given position, which helps to stabilize the gaze.
            Cursor.transform.position = Vector3.Lerp(Cursor.transform.position, Position, 0.6f);
    
            // Check whether the previous focused object is this same. If so, reset the focused object.
            if (FocusedObject != oldFocusedObject)
            {
                ResetFocusedObject();
                if (FocusedObject != null)
                {
                    if (FocusedObject.CompareTag(InteractibleTag))
                    {
                        // Provide the 'Gaze Entered' event.
                        FocusedObject.SendMessage("OnGazeEntered", 
                            SendMessageOptions.DontRequireReceiver);
                    }
                }
            }
        }
    
  8. Salvare le modifiche in Visual Studio prima di tornare a Unity.

Capitolo 8 - Creare la classe Interazioni

Sarà ora necessario creare lo script Interazioni , che è responsabile di:

  • Gestione dell'interazione tap e dello sguardo fisso della fotocamera, che consente all'utente di interagire con il log in "button" nella scena.

  • Creazione dell'oggetto "button" di accesso nella scena con cui l'utente può interagire.

Per creare lo script:

  1. Fare doppio clic sulla cartella Scripts per aprirla.

  2. Fare clic con il pulsante destro del mouse all'interno della cartella Script , scegliere Crea>script C#. Assegnare allo script il nome Interazioni.

  3. Fare doppio clic sullo script per aprirlo con Visual Studio.

  4. Inserire gli spazi dei nomi seguenti:

    using UnityEngine;
    using UnityEngine.XR.WSA.Input;
    
  5. Modificare l'ereditarietà della classe Interaction da MonoBehaviour a GazeInput.

    public class Interactions : MonoBehaviour

    public class Interactions : GazeInput
    
  6. All'interno della classe Interaction inserire la variabile seguente:

        /// <summary>
        /// Allows input recognition with the HoloLens
        /// </summary>
        private GestureRecognizer _gestureRecognizer;
    
  7. Sostituire il metodo Start ; si noti che si tratta di un metodo di override, che chiama il metodo di classe 'base' Gaze. Start() verrà chiamato quando la classe inizializza, registrandosi per il riconoscimento dell'input e creando il pulsante di accesso nella scena:

        /// <summary>
        /// Called on initialization, after Awake
        /// </summary>
        internal override void Start()
        {
            base.Start();
    
            // Register the application to recognize HoloLens user inputs
            _gestureRecognizer = new GestureRecognizer();
            _gestureRecognizer.SetRecognizableGestures(GestureSettings.Tap);
            _gestureRecognizer.Tapped += GestureRecognizer_Tapped;
            _gestureRecognizer.StartCapturingGestures();
    
            // Add the Graph script to this object
            gameObject.AddComponent<MeetingsUI>();
            CreateSignInButton();
        }
    
  8. Aggiungere il metodo CreateSignInButton(), che creerà un'istanza del pulsante di accesso nella scena e imposterà le relative proprietà:

        /// <summary>
        /// Create the sign in button object in the scene
        /// and sets its properties
        /// </summary>
        void CreateSignInButton()
        {
            GameObject signInButton = GameObject.CreatePrimitive(PrimitiveType.Sphere);
    
            Material mat = new Material(Shader.Find("Diffuse"));
            signInButton.GetComponent<Renderer>().material = mat;
            mat.color = Color.blue;
    
            signInButton.transform.position = new Vector3(3.5f, 2f, 9f);
            signInButton.tag = "SignInButton";
            signInButton.AddComponent<Graph>();
        }
    
  9. Aggiungere il metodo GestureRecognizer_Tapped(), che deve rispondere per l'evento utente Tap .

        /// <summary>
        /// Detects the User Tap Input
        /// </summary>
        private void GestureRecognizer_Tapped(TappedEventArgs obj)
        {
            if(base.FocusedObject != null)
            {
                Debug.Log($"TAP on {base.FocusedObject.name}");
                base.FocusedObject.SendMessage("SignInAsync", SendMessageOptions.RequireReceiver);
            }
        }
    
  10. Eliminare il metodo Update() e quindi salvare le modifiche in Visual Studio prima di tornare a Unity.

Capitolo 9 - Configurare i riferimenti allo script

In questo capitolo è necessario inserire lo script Interazioni nella fotocamera principale. Tale script gestirà quindi l'inserimento degli altri script in cui devono trovarsi.

  • Dalla cartella Scripts nel Pannello di progetto trascinare lo script Interazioni nell'oggetto Fotocamera principale , come illustrato di seguito.

    Screenshot che mostra dove trascinare lo script Interazioni.

Capitolo 10 - Impostazione del tag

Il codice che gestisce lo sguardo fisso userà tag SignInButton per identificare l'oggetto con cui l'utente interagirà per accedere a Microsoft Graph.

Per creare il tag:

  1. Nell'editor di Unity fare clic sulla fotocamera principale nel pannello Gerarchia.

  2. Nel Pannello di controllo fare clic sul tagMainCamera per aprire un elenco a discesa. Fare clic su Aggiungi tag...

    Screenshot che evidenzia l'opzione Aggiungi tag... Opzione.

  3. Fare clic sul pulsante +.

    Screenshot che mostra il pulsante + .

  4. Scrivere il nome del tag come SignInButton e fare clic su Salva.

    Screenshot che mostra dove aggiungere il nome del tag SignInButton.

Capitolo 11 - Compilare il progetto Unity in UWP

Tutto il necessario per la sezione Unity di questo progetto è stato completato, quindi è il momento di compilarlo da Unity.

  1. Passare a Impostazioni dicompilazione (impostazioni di compilazionefile>).

    Screenshot che mostra la finestra di dialogo Impostazioni compilazione.

  2. In caso contrario, selezionare Progetti C# unity.

  3. Fare clic su Compila. Unity avvierà una finestra di Esplora file, in cui è necessario creare e quindi selezionare una cartella in cui compilare l'app. Creare la cartella e denominarla App. Quindi, con la cartella App selezionata, fare clic su Seleziona cartella.

  4. Unity inizierà a compilare il progetto nella cartella App .

  5. Una volta completata la compilazione di Unity (potrebbe essere necessario del tempo), verrà aperta una finestra di Esplora file nella posizione della compilazione (controllare la barra delle applicazioni, perché potrebbe non essere sempre visualizzata sopra le finestre, ma invierà una notifica dell'aggiunta di una nuova finestra).

Capitolo 12 - Distribuire in HoloLens

Per eseguire la distribuzione in HoloLens:

  1. È necessario l'indirizzo IP di HoloLens (per distribuzione remota) e per assicurarsi che HoloLens sia in modalità sviluppatore. A tale scopo:

    1. Mentre indossa holoLens, apri le impostazioni.

    2. Vai a Rete &Opzioni avanzateWi-Fi> Internet >

    3. Prendere nota dell'indirizzo IPv4 .

    4. Tornare quindi a Impostazioni e quindi aggiornare & sicurezza>per sviluppatori

    5. Impostare la modalità sviluppatore attivata.

  2. Passare alla nuova compilazione di Unity (cartella App ) e aprire il file della soluzione con Visual Studio.

  3. In Configurazione soluzione selezionare Debug.

  4. Nella piattaforma della soluzione selezionare x86, Computer remoto. Verrà richiesto di inserire l'indirizzo IP di un dispositivo remoto (holoLens, in questo caso, annotato).

    Screenshot che mostra dove selezionare x86 e Computer remoto.

  5. Passare al menu Compila e fare clic su Distribuisci soluzione per trasferire localmente l'applicazione in HoloLens.

  6. L'app dovrebbe ora essere visualizzata nell'elenco delle app installate in HoloLens, pronto per l'avvio.

Applicazione Microsoft Graph HoloLens

È stata creata un'app di realtà mista che sfrutta Microsoft Graph per leggere e visualizzare i dati del calendario utente.

Screenshot che mostra l'app di realtà mista completata.

Esercizi aggiuntivi

Esercizio 1

Usare Microsoft Graph per visualizzare altre informazioni sull'utente

  • Indirizzo di posta elettronica utente/numero di telefono/immagine del profilo

Esercizio 1

Implementare il controllo vocale per spostarsi nell'interfaccia utente di Microsoft Graph.