HoloLens (1.ª geração) e Azure 311 - Microsoft Graph

Nota

Os tutoriais Mixed Reality Academy foram concebidos com o HoloLens (1.ª geração) e Mixed Reality Headsets Envolventes em mente. Como tal, consideramos importante deixar estes tutoriais em vigor para os programadores que ainda estão à procura de orientação no desenvolvimento desses dispositivos. Estes tutoriais não serão atualizados com os conjuntos de ferramentas ou interações mais recentes que estão a ser utilizados para HoloLens 2. Serão mantidas para continuarem a trabalhar nos dispositivos suportados. Haverá uma nova série de tutoriais que serão publicados no futuro que demonstrarão como desenvolver para HoloLens 2. Este aviso será atualizado com uma ligação para esses tutoriais quando forem publicados.

Neste curso, irá aprender a utilizar o Microsoft Graph para iniciar sessão na sua conta Microsoft através da autenticação segura numa aplicação de realidade mista. Em seguida, irá obter e apresentar as suas reuniões agendadas na interface da aplicação.

Captura de ecrã que mostra as reuniões agendadas na interface da aplicação.

O Microsoft Graph é um conjunto de APIs concebido para permitir o acesso a muitos dos serviços da Microsoft. A Microsoft descreve o Microsoft Graph como sendo uma matriz de recursos ligados por relações, o que significa que permite que uma aplicação aceda a todos os tipos de dados de utilizador ligados. Para obter mais informações, visite a página do Microsoft Graph.

O desenvolvimento incluirá a criação de uma aplicação na qual o utilizador terá instruções para olhar e, em seguida, tocar numa esfera, o que irá pedir ao utilizador para iniciar sessão em segurança numa conta Microsoft. Depois de iniciar sessão na conta, o utilizador poderá ver uma lista de reuniões agendadas para o dia.

Depois de concluir este curso, terá uma aplicação HoloLens de realidade mista, que poderá fazer o seguinte:

  1. Com o gesto Tocar, toque num objeto, o que irá pedir ao utilizador para iniciar sessão numa Conta Microsoft (sair da aplicação para iniciar sessão e, em seguida, voltar à aplicação novamente).
  2. Ver uma lista de reuniões agendadas para o dia.

Na sua aplicação, cabe-lhe a si saber como irá integrar os resultados com a sua estrutura. Este curso foi concebido para lhe ensinar a integrar um Serviço do Azure no seu projeto do Unity. A sua função é utilizar o conhecimento obtido neste curso para melhorar a sua aplicação de realidade mista.

Suporte de dispositivos

Curso HoloLens Headsets envolventes
MR e Azure 311: Microsoft Graph ✔️

Pré-requisitos

Nota

Este tutorial foi concebido para programadores com experiência básica com o Unity e C#. Tenha também em atenção que os pré-requisitos e as instruções escritas neste documento representam o que foi testado e verificado no momento da escrita (julho de 2018). Pode utilizar o software mais recente, conforme listado no artigo Instalar as ferramentas , embora não se deva presumir que as informações neste curso corresponderão perfeitamente ao que irá encontrar no software mais recente do que o que está listado abaixo.

Recomendamos o seguinte hardware e software para este curso:

Antes de começar

  1. Para evitar problemas ao criar este projeto, é altamente sugerido que crie o projeto mencionado neste tutorial numa pasta raiz ou de raiz próxima (os caminhos de pasta longos podem causar problemas no tempo de compilação).
  2. Configure e teste o HoloLens. Se precisar de suporte para configurar o HoloLens, veja o artigo de configuração do HoloLens.
  3. Recomendamos que execute a Calibragem e o Ajuste do Sensor quando começar a desenvolver uma nova Aplicação HoloLens (por vezes, pode ajudar a realizar essas tarefas para cada utilizador).

Para obter ajuda sobre a Calibragem, siga esta ligação para o artigo Calibragem do HoloLens.

Para obter ajuda sobre a Otimização do Sensor, siga esta ligação para o artigo Otimização do Sensor do HoloLens.

Capítulo 1 - Criar a sua aplicação no Portal de Registo de Aplicações

Para começar, terá de criar e registar a sua aplicação no Portal de Registo de Aplicações.

Neste Capítulo, também encontrará a Chave de Serviço que lhe permitirá fazer chamadas para o Microsoft Graph para aceder ao conteúdo da sua conta.

  1. Navegue para o Portal de Registo de Aplicações da Microsoft e inicie sessão com a sua Conta Microsoft. Depois de iniciar sessão, será redirecionado para o Portal de Registo de Aplicações.

  2. Na secção As minhas aplicações , clique no botão Adicionar uma aplicação.

    Captura de ecrã que mostra onde selecionar Adicionar uma aplicação.

    Importante

    O Portal de Registo de Aplicações pode ter um aspeto diferente, consoante tenha trabalhado anteriormente com o Microsoft Graph. As capturas de ecrã abaixo apresentam estas versões diferentes.

  3. Adicione um nome para a sua aplicação e clique em Criar.

    Captura de ecrã que mostra onde adicionar um nome para a sua aplicação.

  4. Assim que a aplicação tiver sido criada, será redirecionado para a página principal da aplicação. Copie o ID da Aplicação e certifique-se de que anota este valor num local seguro. Irá utilizá-lo em breve no seu código.

    Captura de ecrã que mostra onde ver o ID da Aplicação.

  5. Na secção Plataformas , certifique-se de que a Aplicação Nativa é apresentada. Se não clicar em Adicionar Plataforma e selecionar Aplicação Nativa.

    Captura de ecrã que realça a secção Aplicação Nativa.

  6. Desloque-se para baixo na mesma página e na secção denominada Permissões do Microsoft Graph , terá de adicionar permissões adicionais para a aplicação. Clique em Adicionar junto a Permissões Delegadas.

    Captura de ecrã que mostra onde selecionar Adicionar junto a Permissões Delegadas.

  7. Uma vez que pretende que a sua aplicação aceda ao Calendário do utilizador, selecione a caixa denominada Calendários.Ler e clique em OK.

    Captura de ecrã a mostrar a caixa de verificação Calendários.Ler.

  8. Desloque-se para baixo e clique no botão Guardar .

    Captura de ecrã que mostra onde selecionar Guardar.

  9. A sua gravação será confirmada e pode terminar sessão no Portal de Registo de Aplicações.

Capítulo 2 - Configurar o projeto do Unity

Segue-se uma configuração típica para programar com realidade mista e, como tal, é um bom modelo para outros projetos.

  1. Abra o Unity e clique em Novo.

    Captura de ecrã que mostra a interface do Unity.

  2. Tem de fornecer um nome de projeto do Unity. Inserir MSGraphMR. Certifique-se de que o modelo de projeto está definido como 3D. Defina a Localização para um local adequado para si (lembre-se de que é melhor aproximar-se dos diretórios de raiz). Em seguida, clique em Criar projeto.

    Captura de ecrã que mostra onde selecionar Criar Projeto.

  3. Com o Unity aberto, vale a pena verificar se o Editor de Scripts predefinido está definido como Visual Studio. Aceda a Editar>Preferências e, em seguida, a partir da nova janela, navegue para Ferramentas Externas. Altere o Editor de Scripts Externos para o Visual Studio 2017. Feche a janela Preferências .

    Captura de ecrã que mostra onde definir o Editor de Scripts Externos para o Visual Studio 2017.

  4. Aceda aDefinições de Compilação de Ficheiros> e selecione Plataforma Universal do Windows e, em seguida, clique no botão Mudar de Plataforma para aplicar a sua seleção.

    Captura de ecrã que mostra onde selecionar Mudar de Plataforma.

  5. Ainda nasDefinições de Compilação de Ficheiros>, certifique-se de que:

    1. O Dispositivo de Destino está definido como HoloLens

    2. O Tipo de Compilação está definido como D3D

    3. O SDK está definido como Instalado mais recentemente

    4. A Versão do Visual Studio está definida como Mais Recente instalada

    5. Compilar e Executar está definido como Computador Local

    6. Guarde a cena e adicione-a à compilação.

      1. Para tal, selecione Adicionar Cenas Abertas. Será apresentada uma janela guardar.

        Captura de ecrã que mostra onde selecionar Adicionar Cenas Abertas.

      2. Crie uma nova pasta para esta e qualquer cenário futuro. Selecione o botão Nova pasta , para criar uma nova pasta, atribua-lhe o nome Cenas.

        Captura de ecrã que mostra onde atribuir um nome à nova pasta.

      3. Abra a pasta Cenas recém-criada e, em seguida, no campo Nome do ficheiro: texto, escreva MR_ComputerVisionScene e, em seguida, clique em Guardar.

        Captura de ecrã que mostra onde escrever o nome do ficheiro.

        Importante

        Tenha em atenção que tem de guardar as cenas do Unity na pasta Recursos, uma vez que têm de estar associadas ao projeto unity. Criar a pasta cenas (e outras pastas semelhantes) é uma forma típica de estruturar um projeto do Unity.

    7. As restantes definições, em Definições de Compilação, devem ser deixadas como predefinição por agora.

  6. Na janela Definições de Compilação, clique no botão Definições do Leitor . Esta ação abrirá o painel relacionado no espaço onde se encontra o Inspetor .

    Captura de ecrã a mostrar a caixa de diálogo Definições do Leitor.

  7. Neste painel, é necessário verificar algumas definições:

    1. No separador Outras Definições :

      1. A Versão do Runtime de Scripting deve ser Experimental (.NET 4.6 Equivalente), o que irá acionar a necessidade de reiniciar o Editor.

      2. O Back-end de Scripting deve ser .NET

      3. O Nível de Compatibilidade de API deve ser .NET 4.6

        Captura de ecrã que mostra onde verificar o nível de compatibilidade da API.

    2. No separador Definições de Publicação , em Capacidades, verifique:

      • InternetClient

        Captura de ecrã que mostra onde selecionar a opção InternetClient.

    3. Mais abaixo no painel, em Definições XR (encontradas abaixo de Definições de Publicação), verifique a Realidade Virtual Suportada, certifique-se de que o SDK Windows Mixed Reality é adicionado.

      Captura de ecrã que mostra onde adicionar Windows Mixed Reality SDK.

  8. De volta às Definições de Compilação, os Projetos C# do Unity já não estão desativados; selecione a caixa de verificação junto a esta opção.

  9. Feche a janela Definições de Compilação .

  10. Guarde o seu cenário e projeto (FILE>SAVE SCENES/FILE>SAVE PROJECT).

Capítulo 3 - Importar Bibliotecas no Unity

Importante

Se quiser ignorar o componente Configuração do Unity deste curso e continuar diretamente para o código, pode transferir este Azure-MR-311.unitypackage, importá-lo para o seu projeto como um Pacote Personalizado e, em seguida, continuar a partir do Capítulo 5.

Para utilizar o Microsoft Graph no Unity, tem de utilizar a DLL Microsoft.Identity.Client . No entanto, é possível utilizar o SDK do Microsoft Graph. No entanto, será necessária a adição de um pacote NuGet depois de criar o projeto do Unity (ou seja, editar o projeto após a compilação). Considera-se mais simples importar as DLLs necessárias diretamente para o Unity.

Nota

Atualmente, existe um problema conhecido no Unity que requer que os plug-ins sejam reconfigurados após a importação. Estes passos (4 a 7 nesta secção) deixarão de ser necessários depois de o erro ter sido resolvido.

Para importar o Microsoft Graph para o seu próprio projeto, transfira o ficheiro MSGraph_LabPlugins.zip. Este pacote foi criado com versões das bibliotecas que foram testadas.

Se quiser saber mais sobre como adicionar DLLs personalizados ao seu projeto do Unity, siga esta ligação.

Para importar o pacote:

  1. Adicione o Pacote do Unity ao Unity com a opção de menuPacote PersonalizadoImportar Pacotes> de Importação de Recursos>. Selecione o pacote que acabou de transferir.

  2. Na caixa Importar Pacote do Unity que é apresentada, certifique-se de que está selecionado tudo em (e incluindo) Plug-ins .

    Captura de ecrã que mostra os parâmetros de configuração selecionados em Plug-ins.

  3. Clique no botão Importar para adicionar os itens ao projeto.

  4. Aceda à pasta MSGraph em Plug-ins no Painel de Projeto e selecione o plug-in denominado Microsoft.Identity.Client.

    Captura de ecrã a mostrar o plug-in Microsoft.Identity.Client.

  5. Com o plug-in selecionado, certifique-se de que Qualquer Plataforma está desmarcada e, em seguida, certifique-se de que o WSAPlayer também está desmarcado e, em seguida, clique em Aplicar. Isto é apenas para confirmar que os ficheiros estão configurados corretamente.

    Captura de ecrã que mostra onde confirmar que qualquer Plataforma e WSAPlayer não estão selecionados.

    Nota

    Marcar estes plug-ins configura-os para serem utilizados apenas no Editor do Unity. Existe um conjunto diferente de DLLs na pasta WSA que será utilizado após o projeto ser exportado do Unity como uma Aplicação Universal do Windows.

  6. Em seguida, tem de abrir a pasta WSA , na pasta MSGraph . Verá uma cópia do mesmo ficheiro que acabou de configurar. Selecione o ficheiro e, em seguida, no inspetor:

    • certifique-se de que Qualquer Plataforma está desmarcada e que apenaso WSAPlayer está selecionado.

    • Confirme que o SDK está definido como UWP e que o Back-end de Scripting está definido como Ponto Net

    • Certifique-se de que Não processar está selecionado.

      Captura de ecrã que mostra que Não Processar está selecionado.

  7. Clique em Aplicar.

Capítulo 4 - Configuração da Câmara

Durante este Capítulo, irá configurar a Câmara Principal da sua cena:

  1. No Painel hierarquia, selecione a Câmara Principal.

  2. Depois de selecionado, poderá ver todos os componentes da Câmara Principal no painel Inspetor .

    1. O objeto Câmara tem de ter o nome Câmara Principal (anote a ortografia!)

    2. A Etiqueta da Câmara Principal tem de ser definida como MainCamera (anote a ortografia!)

    3. Certifique-se de que a Posição da Transformação está definida como 0, 0, 0

    4. Defina Limpar Sinalizadores como Cor Sólida

    5. Defina a Cor de Fundo do Componente da Câmara como Preto, Alfa 0(Código Hexadecima: #00000000)

      Captura de ecrã que realça onde definir a cor de fundo.

  3. A estrutura final do objeto no Painel de Hierarquia deve ser semelhante à apresentada na imagem abaixo:

    Captura de ecrã que mostra a estrutura final do objeto no Painel de Hierarquia.

Capítulo 5 - Criar ReuniõesUtilidade de UTILIZADOR

O primeiro script que precisa de criar é MeetingsUI, que é responsável por alojar e preencher a IU da aplicação (mensagem de boas-vindas, instruções e detalhes das reuniões).

Para criar esta classe:

  1. Clique com o botão direito do rato na pasta Recursos no Painel de Projeto e, em seguida, selecione Criar>Pasta. Atribua um nome aos Scripts da pasta.

    Captura de ecrã que mostra onde encontrar a pasta Recursos.Captura de ecrã que mostra onde criar a pasta Scripts.

  2. Abra a pasta Scripts e, em seguida, nessa pasta, clique com o botão direito do rato em Criar>Script C#. Atribua o nome MeetingsUI ao script.

    Captura de ecrã que mostra onde criar a pasta MeetingsUI.

  3. Faça duplo clique no novo script MeetingsUI para o abrir com o Visual Studio.

  4. Insira os seguintes espaços de nomes:

    using System;
    using UnityEngine;
    
  5. Dentro da classe, insira as seguintes variáveis:

        /// <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. Em seguida, substitua o método Start() e adicione um método Awake( ). Estes serão chamados quando a classe inicializar:

        /// <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. Adicione os métodos responsáveis pela criação da IU das Reuniões e preencha-a com as reuniões atuais quando solicitado:

        /// <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. Elimine o método Update() e guarde as alterações no Visual Studio antes de regressar ao Unity.

Capítulo 6 - Criar a classe Graph

O script seguinte a criar é o script do Graph . Este script é responsável por fazer as chamadas para autenticar o utilizador e obter as reuniões agendadas para o dia atual a partir do calendário do utilizador.

Para criar esta classe:

  1. Faça duplo clique na pasta Scripts para abri-la.

  2. Clique com o botão direito do rato dentro da pasta Scripts e clique em Criar>Script C#. Atribua o nome Graph ao script.

  3. Faça duplo clique no script para o abrir com o Visual Studio.

  4. Insira os seguintes espaços de nomes:

    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

    Irá reparar que partes do código neste script estão encapsuladas em diretivas de pré-compilação, isto é, para evitar problemas com as bibliotecas ao criar a Solução do Visual Studio.

  5. Elimine os métodos Start() e Update(), uma vez que não serão utilizados.

  6. Fora da classe Graph , insira os seguintes objetos, que são necessários para anular a serialização do objeto JSON que representa as reuniões agendadas diárias:

    /// <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. Dentro da classe Graph , adicione as seguintes variáveis:

        /// <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

    Altere o valor appId para ser o ID da Aplicação que anotou no Capítulo 1, passo 4. Este valor deve ser o mesmo que o apresentado no Portal de Registo de Aplicações, na página de registo da aplicação.

  8. Na classe Graph , adicione os métodos SignInAsync() e AquireTokenAsync(), que irão pedir ao utilizador para inserir as credenciais de início de sessão.

        /// <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. Adicione os dois métodos seguintes:

    1. BuildTodayCalendarEndpoint(), que cria o URI que especifica o dia e o intervalo de tempo, no qual as reuniões agendadas são obtidas.

    2. ListMeetingsAsync(), que pede as reuniões agendadas do 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. Concluiu agora o script do Graph . Guarde as alterações no Visual Studio antes de regressar ao Unity.

Capítulo 7 - Criar o script GazeInput

Agora, irá criar o GazeInput. Esta classe processa e controla o olhar do utilizador, utilizando um Raycast proveniente da Câmara Principal, projetando para a frente.

Para criar o script:

  1. Faça duplo clique na pasta Scripts para abri-la.

  2. Clique com o botão direito do rato dentro da pasta Scripts e clique em Criar>Script C#. Atribua o nome GazeInput ao script.

  3. Faça duplo clique no script para o abrir com o Visual Studio.

  4. Altere o código dos espaços de nomes para corresponder ao seguinte, juntamente com a adição da etiqueta "[System.Serializable]" acima da classe GazeInput , para que possa ser serializada:

    using UnityEngine;
    
    /// <summary>
    /// Class responsible for the User's Gaze interactions
    /// </summary>
    [System.Serializable]
    public class GazeInput : MonoBehaviour
    {
    
  5. Na classe GazeInput , adicione as seguintes variáveis:

        [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. Adicione o método CreateCursor() para criar o cursor do HoloLens na cena e chame o método a partir do método 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. Os métodos seguintes permitem o olhar de olhar raycast e controlar os objetos focados.

    /// <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. Guarde as alterações no Visual Studio antes de regressar ao Unity.

Capítulo 8 - Criar a classe Interações

Agora, terá de criar o script Interações , que é responsável por:

  • Lidar com a interação Toque e o Olhar da Câmara, o que permite ao utilizador interagir com o "botão" de início de sessão na cena.

  • Criar o objeto "botão" de início de sessão na cena com o qual o utilizador interage.

Para criar o script:

  1. Faça duplo clique na pasta Scripts para abri-la.

  2. Clique com o botão direito do rato dentro da pasta Scripts e clique em Criar>Script C#. Atribua o nome Interactions (Interações) ao script.

  3. Faça duplo clique no script para o abrir com o Visual Studio.

  4. Insira os seguintes espaços de nomes:

    using UnityEngine;
    using UnityEngine.XR.WSA.Input;
    
  5. Altere a herança da classe Interaction de MonoBehaviour para GazeInput.

    Interações de classe pública: MonoBehaviour

    public class Interactions : GazeInput
    
  6. Dentro da classe Interação , insira a seguinte variável:

        /// <summary>
        /// Allows input recognition with the HoloLens
        /// </summary>
        private GestureRecognizer _gestureRecognizer;
    
  7. Substitua o método Iniciar ; repare que é um método de substituição, que chama o método de classe Gaze "base". Start() será chamado quando a classe inicializar, registando-se para reconhecimento de entrada e criando o botão de início de sessão na cena:

        /// <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. Adicione o método CreateSignInButton(), que irá instanciar o botão de início de sessão na cena e definir as respetivas propriedades:

        /// <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. Adicione o método GestureRecognizer_Tapped(), que será respondido para o evento Desativar utilizador.

        /// <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. Elimine o método Update() e, em seguida, guarde as alterações no Visual Studio antes de regressar ao Unity.

Capítulo 9 - Configurar as referências de script

Neste Capítulo, tem de colocar o script Interações na Câmara Principal. Em seguida, esse script processará a colocação dos outros scripts onde precisam de estar.

  • Na pasta Scripts no Painel de Projeto, arraste o script Interações para o objeto Câmara Principal , conforme ilustrado abaixo.

    Captura de ecrã que mostra onde arrastar o script Interações.

Capítulo 10 - Configurar a Etiqueta

O código que está a processar o olhar irá utilizar o SignInButton de Etiqueta para identificar com que objeto o utilizador irá interagir para iniciar sessão no Microsoft Graph.

Para criar a Etiqueta:

  1. No Editor do Unity, clique na Câmara Principal no Painel hierarquia.

  2. No Painel de Inspetor, clique na EtiquetaMainCamera para abrir uma lista pendente. Clique em Adicionar Etiqueta...

    Captura de ecrã que realça a opção Adicionar Etiqueta... opção.

  3. Clique no + botão .

    Captura de ecrã a mostrar o botão + .

  4. Escreva o Nome da etiqueta como SignInButton e clique em Guardar.

    Captura de ecrã que mostra onde adicionar o nome da etiqueta SignInButton.

Capítulo 11 - Criar o projeto do Unity para uWP

Tudo o que é necessário para a secção Unity deste projeto já foi concluído, por isso está na altura de o construir a partir do Unity.

  1. Navegue para Definições de Compilação (Definições de Compilação de Ficheiros>).

    Captura de ecrã a mostrar a caixa de diálogo Definições de Compilação.

  2. Se ainda não estiver, assinale Projetos C# do Unity.

  3. Clique em Compilar. O Unity irá iniciar uma janela de Explorador de Ficheiros, onde tem de criar e, em seguida, selecionar uma pasta para compilar a aplicação. Crie essa pasta agora e dê-lhe o nome Aplicação. Em seguida, com a pasta Aplicação selecionada, clique em Selecionar Pasta.

  4. O Unity começará a criar o seu projeto para a pasta Aplicação .

  5. Assim que o Unity terminar a criação (poderá demorar algum tempo), abrirá uma janela de Explorador de Ficheiros na localização da compilação (verifique a barra de tarefas, uma vez que pode nem sempre aparecer acima das janelas, mas irá notificá-lo sobre a adição de uma nova janela).

Capítulo 12 - Implementar no HoloLens

Para implementar no HoloLens:

  1. Precisará do Endereço IP do HoloLens (para Implementação Remota) e para garantir que o HoloLens está no Modo de Programador. Para o fazer:

    1. Enquanto estiver a usar o HoloLens, abra as Definições.

    2. Aceda aOpções Avançadas de Rede & Internet>Wi-Fi>

    3. Anote o endereço IPv4 .

    4. Em seguida, navegue de volta para Definições e, em seguida, para Atualizar Segurança> &para Programadores

    5. Defina Modo de Programador Ativado.

  2. Navegue para a nova compilação do Unity (a pasta Aplicação ) e abra o ficheiro de solução com o Visual Studio.

  3. Na Configuração da Solução , selecione Depurar.

  4. Na Plataforma de Soluções, selecione x86, Máquina Remota. Ser-lhe-á pedido para inserir o endereço IP de um dispositivo remoto (o HoloLens, neste caso, que anotou).

    Captura de ecrã que mostra onde selecionar x86 e Máquina Remota.

  5. Aceda ao menu Compilar e clique em Implementar Solução para fazer sideload da aplicação para o HoloLens.

  6. A sua aplicação deverá agora aparecer na lista de aplicações instaladas no HoloLens, prontas para serem iniciadas!

A sua aplicação Do Microsoft Graph HoloLens

Parabéns, criou uma aplicação de realidade mista que tira partido do Microsoft Graph para ler e apresentar dados do Calendário do utilizador.

Captura de ecrã que mostra a aplicação de realidade mista concluída.

Exercícios de bónus

Exercício 1

Utilizar o Microsoft Graph para apresentar outras informações sobre o utilizador

  • E-mail do utilizador/número de telefone/imagem de perfil

Exercício 1

Implemente o controlo de voz para navegar na IU do Microsoft Graph.