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. Ci sarà una nuova serie di esercitazioni che verranno pubblicate in futuro che dimostreranno come sviluppare per HoloLens 2. Questo avviso verrà aggiornato con un collegamento a tali esercitazioni quando vengono pubblicate.

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 da 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 verrà richiesto all'utente 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 un oggetto , che chiederà all'utente di accedere a un account Microsoft (spostandosi dall'app per accedere e quindi di nuovo nell'app).
  2. Visualizzare un elenco di riunioni pianificate per il giorno.

Nell'applicazione spetta all'utente come integrare i risultati con la progettazione. Questo corso è progettato per illustrare come integrare un servizio di Azure con il progetto Unity. È il tuo compito 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). Sei libero di usare il software più recente, come elencato all'interno dell'articolo installare gli strumenti , anche se non si deve presumere che le informazioni in questo corso corrisponderanno perfettamente a ciò che troverai 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 di riscontrare problemi durante la compilazione di questo progetto, è consigliabile creare il progetto menzionato in questa esercitazione in una cartella radice o quasi radice (i percorsi delle cartelle lunghe possono causare problemi in fase di compilazione).
  2. Configurare e testare HoloLens. Se hai bisogno di supporto per configurare HoloLens, assicurati di visitare l'articolo configurazione di HoloLens.
  3. È consigliabile eseguire la calibrazione e l'ottimizzazione dei sensori quando si inizia a sviluppare una nuova app HoloLens (a volte può essere utile per eseguire tali attività per ogni utente).

Per informazioni su Calibrazione, seguire questo collegamento all'articolo Calibrazione di HoloLens.

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

Capitolo 1: Creare l'app nel portale di registrazione delle 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 consentirà 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 delle applicazioni.

  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 delle applicazioni può avere un aspetto 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 annotare questo valore in un punto sicuro, che verrà usato a breve nel codice.

    Screenshot che mostra dove visualizzare l'ID applicazione.

  5. Nella sezione Piattaforme verificare che sia visualizzata l'applicazione nativa. Se non si fa 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 Autorizzazioni di Microsoft Graph è 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 calendars.Read e fare clic su OK.

    Screenshot che mostra la casella di controllo Calendars.Read.

  8. Scorrere fino alla fine e fare clic sul pulsante Salva .

    Screenshot che mostra dove selezionare Salva.

  9. Il salvataggio verrà confermato ed è possibile disconnettersi dal portale di registrazione delle applicazioni.

Capitolo 2 - Configurare il progetto Unity

Di seguito è riportata una configurazione tipica per lo sviluppo con la 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 (tenere presente che più vicino alle directory radice è preferibile). 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 di script esterni in Visual Studio 2017. Chiudere la finestra Preferenze .

    Screenshot che mostra dove impostare External Script Editor su Visual Studio 2017.

  4. Passare a Impostazioni compilazione file>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. Sempre in Impostazioni di compilazione file>, assicurarsi che:

    1. Il dispositivo di destinazione è impostato su HoloLens

    2. Il tipo di compilazione è impostato su D3D

    3. L'SDK è impostato su Latest installed (Versione più recente installata)

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

    5. Compilazione ed esecuzione è impostata su Computer locale

    6. Salvare la scena e aggiungerla alla compilazione.

      1. A tale scopo, selezionare Aggiungi scene aperte. Verrà visualizzata una finestra di salvataggio.

        Screenshot che mostra dove selezionare Aggiungi scene aperte.

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

        Screenshot che mostra dove denominare la nuova cartella.

      3. Aprire la cartella Scene appena creata, 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 Assets , perché devono essere associate al progetto Unity. La creazione della cartella scene (e altre cartelle simili) è un modo tipico di 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 per aprire 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 runtime di scripting deve essere sperimentale (equivalente a .NET 4.6), che attiverà la necessità di riavviare l'editor.

      2. Il back-end di scripting 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 Realtà virtuale supportata, assicurarsi che Windows Realtà mista SDK sia stato aggiunto.

      Screenshot che mostra dove aggiungere Windows Realtà mista SDK.

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

  9. Chiudi la finestra Build Settings (Impostazioni di compilazione).

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

Capitolo 3 - Importare librerie in Unity

Importante

Se si vuole ignorare il componente Di configurazione 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 in Unity, è necessario usare la DLL Microsoft.Identity.Client . È possibile usare Microsoft Graph SDK, ma richiederà l'aggiunta di un pacchetto NuGet dopo la compilazione del progetto Unity (ovvero la modifica del progetto dopo la compilazione). È considerato più semplice importare le DLL necessarie direttamente in Unity.

Nota

Esiste attualmente un problema noto in Unity che richiede la riconfigurazione dei plug-in dopo l'importazione. Questi passaggi (da 4 a 7 in questa sezione) non saranno più necessari dopo la risoluzione del bug.

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 menu Asset>Importa pacchetto>personalizzato pacchetto. Selezionare il pacchetto appena scaricato.

  2. Nella casella Importa pacchetto Unity visualizzata verificare che tutti gli elementi in (e inclusi) Plug-in 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 del 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 Qualsiasi piattaforma sia deselezionata, quindi assicurarsi che WSAPlayer sia deselezionato, quindi fare clic su Applica. Questo è solo per confermare che i file sono configurati correttamente.

    Screenshot che mostra dove verificare che Any Platform e WSAPlayer non siano controllati.

    Nota

    Contrassegnando questi plug-in, questi plug-in vengono configurati in modo che vengano usati solo nell'editor di Unity. Nella cartella WSA è presente un set diverso di DLL che verranno usate dopo l'esportazione del progetto da Unity come applicazione di Windows universale.

  6. Successivamente, è 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 sia selezionato solo WSAPlayer.

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

    • Assicurarsi che Non elaborare sia selezionata.

      Screenshot che mostra che l'opzione Don't Process è selezionata.

  7. Fare clic su Applica.

Capitolo 4 - Configurazione della fotocamera

Durante questo capitolo si configurerà la fotocamera principale della scena:

  1. Nel pannello Gerarchia selezionare la fotocamera principale.

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

    1. L'oggetto Camera deve essere denominato Fotocamera principale (si noti l'ortografia!)

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

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

    4. Imposta flag di cancellazione su colore a tinta unita

    5. Impostare il colore di sfondo del componente fotocamera su Nero, Alfa 0 (Codice esadecimale: #00000000)

      Screenshot che evidenzia dove impostare il colore di sfondo.

  3. La struttura dell'oggetto finale nel pannello gerarchia dovrebbe 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 del 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 Assets nel pannello del progetto, quindi scegliere Crea>cartella. Denominare la cartella Scripts.

    Screenshot che mostra dove trovare la cartella Assets.Screenshot che mostra dove creare la cartella Scripts.

  2. Aprire la cartella Scripts e quindi, all'interno di tale cartella, fare clic con il pulsante destro del mouse su Crea>script C#. Assegnare allo script il nome 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 popolarlo 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 di effettuare le 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 Scripts per aprirla.

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

  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 tra le direttive precompile, 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 essere uguale 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 chiederà 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

Si creerà ora 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 Scripts e 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 Scripts e 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 della classe gaze 'base'. 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 ne imposta le 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 del 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 tag MainCamera 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 ciò che serve per la sezione Unity di questo progetto è stato completato, quindi è il momento di compilarlo da Unity.

  1. Passare a Impostazioni di compilazione (impostazioni di compilazione file>).

    Screenshot che mostra la finestra di dialogo Impostazioni di compilazione.

  2. Se non è già stato fatto, selezionare Progetti C# unity.

  3. Fare clic su Compila. Unity avvierà una finestra Esplora file, in cui è necessario creare e quindi selezionare una cartella in cui compilare l'app. Creare ora 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 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 la distribuzione remota) e assicurarsi che HoloLens sia in modalità sviluppatore. A tale scopo:

    1. Mentre indossa HoloLens, apri le impostazioni.

    2. Vai a Rete e Internet>Wi-Fi>Opzioni avanzate

    3. Prendere nota dell'indirizzo IPv4 .

    4. Tornare quindi a Impostazioni e quindi a Aggiorna e sicurezza>per sviluppatori

    5. Impostare la modalità sviluppatore attivata.

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

  3. Nella configurazione della 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, pronte 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.