Universal Windows Apps — parte 1

NetCoders
netcoders
Published in
10 min readJun 7, 2015

Na Build 2015, evento que aconteceu no final de abril, a Microsoft nos mostrou seu passo mais corajoso com o Windows 10: unificar a plataforma de tal forma que um único executável seja capaz de rodar em qualquer dispositivo com a nova versão do sistema operacional. Para nós desenvolvedores é a realização de um sonho utópico, no qual desenvolveremos apenas uma vez sem nos preocuparmos com o ambiente onde o aplicativo rodará. Observe que nem mesmo o HTML conseguiu essa proeza, haja visto tantos contorcionismo que devem ser feitos afim de evitar conflitos por causa de versões de navegadores e etc.

Imagine fazer um aplicativo uma única vez e vê-lo sendo executado no PC, no tablet, em dispositivos como Rasperberry Pi, no smartphone e até mesmo no Xbox! Isso se chama Universal App. E agora será realidade pois a Microsoft está (finalmente) usando o mesmo núcleo do Windows em todos estes dispositivos, tornando a vida do programador muito mais simples ao termos que aprender apenas uma única API central. Não apenas a API é central, como a UI também foi unificada. Faça UM programa, rode em qualquer dispositivo com Windows 10! E para aqueles casos específicos onde você precise se certificar de que seu app está rodando em um smartphone ou Xbox basta, no código e em tempo de execução, verificar se determinado recurso está disponível ou não.

E você pode estar se perguntando: mas já não existem “Universal Apps” no mundo Windows, com direito ao mesmo app rodando tanto no Windows 8.1 quanto no Windows Phone 8.1? É, concordo que a Microsoft não foi muito feliz nas escolhas das nomenclaturas, e vejo muita confusão com esses termos. Observe que agora temos duas formas de Universal Apps: a “ANTIGA” para Windows 8.1 / Windows Phone 8.1 e a nova para Windows 10 em diante. No jeito antigo você cria uma solução com 3 “projetos”: Windows Store 8.1, Windows Phone 8.1 e Shared Code. Quando você compilar terá como resultado 2 executáveis, um para cada plataforma. Já com o novo UWA — Universal Windows Apps — você cria um único projeto, e o executável rodará em qualquer coisa com Windows 10!

Ou seja, ESQUEÇA Windows 8.1! Se você está desenvolvendo uma Universal App da forma antiga então meus pêsames, e se o prazo permitir lhe recomendo a parar imediatamente e se focar apenas no Windows 10, que será lançado oficialmente em 29 de julho.

Para nossa jornada você precisará:

* Windows 10 — inscreva-se no programa Insiders, baixe a ISO do Windows e instale, de preferência em um HD reserva. Eu já uso a build 10130 como sistema principal estável, mas tenha em mente que é um PREVIEW com alguns bugs. No meu PC eu mantenho vários HDs: Windows 10 + VS 2015, Windows 8.1 + VS 2013 e Windows 7 + VS 2013. Depois do lançamento oficial do Windows 10 o Windows 8.1 morrerá para mim.

Ao instalar o Windows 10, como Administrador, vá em Configurações / Atualizar e Segurança / Para desenvolvedores e marque que seu computador é de programador. Aproveite e marque que gostaria de receber atualizações FAST do próprio Windows 10, pois a Microsoft está soltando novas builds com frequência. Se você não alterar para DevMode o próprio VS lhe lembrará disso quando você tentar criar projetos Universais.

Universal App - modo desenvolvedor

(configurando o Windows para o modo desenvolvedor)

* Visual Studio Community 2015 RC — idem ao Windows 10, é um RELEASE CANDIDATE, ou seja, tenha em mente que ele ainda não é para ser usado para produção. A versão Community é a indicada no lugar das antigas Express. Ao instalar o VS 2015 não esqueça de marcar corretamente a nova forma de Universal App. Não confundir com antiga Universal do Windows 8.1 / Windows Phone 8.1:

Universal App - template

(marcando a instalação dos templates do novo Universal Windows App)

Abro aqui um parêntese: SE funcionar, e tem tudo para funcionar, a Microsoft dará uma resposta deliciosamente irônica a Tim Cook da Apple, que no passado disse que essa convergência nunca funcionaria e usou como exemplo geladeira e torradeira. Vou adorar ver meus apps rodando por toda a cozinha, casa, carro, roupas e até mesmo PCs e smarts!

Essa convergência dará ao seu aplicativo uma audiência incrível. Some todos os usuários de Windows PC, Windows Tablet, Windows Notebook, Windows Mobile e Xbox e comece a perceber a quantidade de potenciais consumidores do seu aplicativo. Todos os apps Window 10 ficarão disponíveis na Loja de Apps do Windows, ao alcance de um clique. Percebeu a nova janela de possibilidades que foi aberta a nós, programadores?

Então vamos à questão principal: como raios vou conseguir executar um aplicativo, sem modificações, em vários dispositivos totalmente diferentes? Aqui na minha frente tenho um HTC Ultimate e um desktop convencional. Os dois dispositivos têm métodos de entrada e saída, processadores, memória, armazenamento, rodam um sistema operacional e permitem instalar aplicativos. Ok, são bem parecidos. Mas a tela do HTC é touch com 4.7' e o monitor do meu desktop tem 21'. No desktop eu uso teclado e mouse. Acho que minha webcam está em alguma gaveta, mas no HTC tem um botão dedicado para a câmera. Ok, são BEM diferentes!

Para atingir o objetivo de 1 app — N dispositivos, a Microsoft pensou em três frentes: núcleo único do Windows, API unificada e interface adaptável. Neste artigo falaremos sobre o núcleo e a API, e no próximo falaremos sobre interface de usuário adaptável.

O Núcleo

Em tempos remotos (sim, sou velho) a Microsoft criou vários Windows diferentes, um para cada ambiente. Não vou falar de coisas como Windows CE e Pocket PC, me dão calafrios só de lembrar, e se você não é idoso como eu pode fazer buscas na internet e se divertir com nossos problemas (olá Palm, saudades de você… Já passou!). Agora com o Windows 10 a Microsoft está unificando o próprio núcleo, o que significa maior agilidade no desenvolvimento do sistema operacional bem como a possibilidade de se ter uma base única de API.

Ao manter um único núcleo a Microsoft garante ao programador que o desenvolvimento da API será uniforme, o que torna a nossa vida muito mais fácil, pois não mais teremos que decorar diferenças entre frameworks que desde sempre deveriam ser iguais. Se você usa uma API Universal, a Microsoft está garantindo que essa API será exatamente a mesma em qualquer outro dispositivo que venha a ser suportado.

Observe que não estou falando de um único WINDOWS, e sim um único NÚCLEO, que pode receber atualizações diferentes em cada dispositivo, mas que garante que a API é exatamente a mesma para o programador. Afinal, apesar de todos estarem rodando Windows 10 ainda são dispositivos completamente diferentes um do outro, com necessidades exclusivas. É nessa hora que entra em cena o conceito de Device Family.

Device Family

Universal App - Device Family

Quando você estiver estudando determinada classe da API no MSDN verá no final uma indicação de device, isso é, em qual tipo de dispositivo aquela API é suportada. Tomemos como exemplo o controle StackPanel, que faz parte da classe Windows.UI.Xaml.Controls. No final da referência encontramos as definições de requerimentos para poder usar essa classe. No caso da StackPanel a Device Family é Universal, o que significa que qualquer dispositivo usando Windows 10 dará suporte a este controle. Veja no diagrama acima que todos os dispositivos “herdam” de Universal Device Family, ou seja, tanto o Xbox quanto o desktop suportarão nativamente o StackPanel.

Agora compare StackPanel com a classe HardwareButtons. Enquanto a Device Family de StackPanel é Universal, a Device Family de HardwareButtons é Mobile, ou seja, apenas dispositivos móveis (phones, phablets e tablets) suportarão esta API em especial. No seu app você pode incluir qualquer API afim de aumentar a possibilidades de dispositivos que irão executá-lo.

Ao criar seu app você deve decidir em quais famílias de dispositivos ele pode ser executado. Se escolher Universal Device Family significa que seu app rodará em todos os dispositivos com Windows 10. Se escolher exclusivamente Mobile Device Family significa que ele rodará apenas nos dispositivos que suportem esse subgrupo de APIs. Mas você não está preso a apenas uma família de dispositivos, podendo fazer as mais variadas combinações. Seu app pode rodar no desktop E Xbox. Ou apenas no Hololens. Em todos os tipos de dispositivos, exceto no Surface Hub. Enfim, essa decisão é sua, e influenciará em como você usará as APIs condicionalmente dentro do seu app. A Microsoft chama esse condicionamento de Adaptive Coding.

Detectando API

Vamos supor que seu app rodará tanto no desktop quanto no móvel, e que ele terá recursos de impressão. Não há diretamente recursos de impressão na Universal App, você deve incluir uma referência ao Microsoft Desktop Extension SDK e chamar as API de impressão, seja no desktop, seja no móvel. E o móvel pode ter um botão físico de voltar, o app deveria interceptar quando o usuário pressionar este botão físico. Para isso você incluiria uma referência ao Microsoft Mobile Extension SDK. Espere por outras Extension SDKs em breve para os mais diversos dispositivos.

UniversalApp - Extension SDKs

(referenciando as Extension SDK de desktop e móvel)

Se você tentar usar uma API em um dispositivo que não dá suporte a essa API o sistema disparará uma exceção. Para que você use as APIs específicas de cada família de dispositivos é preciso primeiro verificar se o dispositivo onde o aplicativo está sendo executando assinou o contrato com essa API. Um contrato é um meio pelo qual o dispositivo diz se suporta ou não determinada API, então, ao invés de verificar o DISPOSITIVO você verifica o CONTRATO. Isso é exposto pela classe Windows.Foundation.Metadata.ApiInformation.

Porque não verificar por dispositivo? Porque um dispositivo pode dar suporte a determinada API enquanto outro dispositivo da mesma família pode não dar suporte, como é o caso de HardwareButtons. Nem todo móvel tem um botão físico de voltar, tem um botão dedicado para tirar fotos ou aceita um cartão SD por exemplo. Uma vez que é possível que seu app esteja rodando no desktop ou em um móvel que não tenha um botão físico de voltar, antes de usar alguma API exclusiva você deve verificar no contrato se a API é permitida no dispositivo. Veja o código abaixo:

[sourcecode language=”csharp”]
// se existe um botão físico de voltar, esconde um botão virtual em XAML
// que está no CommandBar e assina o evento para quando pressionarem
// o botão físico de voltar
if (Windows.Foundation.Metadata.ApiInformation.IsTypePresent(“Windows.Phone.UI.Input.HardwareButtons”))
{
BackButton.Visibility = Visibility.Collapsed;
Windows.Phone.UI.Input.HardwareButtons.BackPressed += OnHardwareBackButtonPressed;
}
[/sourcecode]

IsTypePresent me retorna se determinada classe / tipo está disponível, mas novos membros da classe podem ser adicionados a qualquer momento, então o ideal é consultar diretamente por esses membros usando IsEventPresent, IsMethodPresent ou IsPropertyPresent:

[sourcecode language=”csharp”]
// testa se existe o evento CameraPressed
bool isAPIPresent = Windows.Foundation.Metadata.ApiInformation.IsEventPresent(“Windows.Phone.UI.Input.HardwareButtons”, “CameraPressed”);
[/sourcecode]

Entrada de Dados

Cada dispositivo pode aceitar várias formas de entrada de dados. Mouse, teclado, toque, controle do Xbox, caneta, enfim, a lista é imensa. Imagine criar um app de desenho e, ao invés de assinar vários eventos para cada uma dessas formas de interação, seu app passa a contar com APIs unificadas, que expõem o ato da interação, independente da forma como ele ocorra.

Usando a classe PointerDevice seu app pode descobrir uma lista de todas as formas de interação disponibilizadas pelo dispositivo. Veja o código abaixo:

[sourcecode language=”csharp”]
// lista todos os tipos de interação aceita pelo dispositivo
var pointerDevices = Windows.Devices.Input.PointerDevice.GetPointerDevices();
for (i = 0; i < pointerDevices.Size; i++)
{
Debug.Print(getPointerDeviceType(pointerDevices[i].PointerDeviceType.ToString());
}
[/sourcecode]

PointerDevice retorna o tipo do dispositivo de interação, se é externo, quantos pontos de contato no máximo, tamanho da área de contato, entre outros. Seu app poderia consultar PointerDevice para saber se o dispositivo aceita o toque simultâneo de 4 pontos, por exemplo. Junto a PointerDevice temos a classe PointerPoint que trata da interação por caneta, toque ou mouse, retornando valores em propriedades como IsInContact, que indica se o usuário está pressionando algo (caneta, dedo ou botão do mouse).

Recursos Unificados

Nas versões anteriores do Windows e do Windows Phone existem recursos exclusivos em cada plataforma. No Windows Phone temos o controle Pivot e a área de notificações. No Windows PC o acesso ao armazenamento é feito por Local Storage, Roaming Storage e Temp Storage, ao invés de Isolated Storage (veja aqui um exemplo dessas várias diferenças entre plataformas).

Nas versões 8.1 a Microsoft começou o processo de unificação de funcionalidades, controles e APIs, mas ainda exigia a criação de dois projetos em separados, um para cada plataforma. Agora com a versão 10 a Microsoft lhe garante que não só seu app rodará em todos os dispositivos como terá acesso a uma base unificada de recursos e infraestrutura. Um exemplo de recurso unificado é o Roaming Storage. Através dessa API seu app salvará arquivos e valores quaisquer, como uma configuração ou informação sobre em que parte o usuário está dentro do seu app, e o sistema automaticamente compartilhará esses arquivos e valores por todos os dispositivos que o usuário tenha instalado o seu app.

Imagine o usuário lendo uma notícia no desktop e, ao abrir seu app no Xbox, continuar exatamente de onde ele havia parado a leitura. Para conseguir isso seu app usaria a API RoamingSettings para salvar a configuração na pasta Roaming do aplicativo no dispositivo, enquanto a Microsoft lhe garante que não só todos os dispositivos terão essa pasta como o sistema automaticamente se encarregará de sincronizar seu conteúdo por todos os dispositivos usados pelo seu app.

[sourcecode language=”csharp”]
// criando uma configuração no dispositivo A
var roamingSettings = Windows.Storage.ApplicationData.Current.RoamingSettings;
roamingSettings.Values[“exampleSetting”] = “Hello World”;
[/sourcecode]

[sourcecode language=”csharp”]
// no evento App_Launched do dispositivo B, carregar a configuração já sincronizada
Object value = roamingSettings.Values[“exampleSetting”];
[/sourcecode]

Nesse artigo vimos como a estratégia da Microsoft de unificar sua plataforma nos entrega uma base sólida de codificação. Para nós programadores o dispositivo onde será executado nosso código não tem mais tanta importância quanto antes, nos deixando livre para pensar nos objetivos do aplicativo em si. Lidaremos com uma única API, ser vertentes, sem diferenciações como antes. Poderemos nos concentrar em codificar, sem nos preocupar com detalhes como infraestrutura ou ter que fazer compilações condicionais.

Mas e como lidar com os mais variados tamanhos de tela sem enlouquecer? É disso que se trata Adaptive UI, e esse é o tema do próximo e último artigo sobre o nascimento da real Universal Windows App. Até lá.

--

--