Aplicativos universais

Equipe seus aplicativos com OBEX

Uday Gupta

Baixar o código de exemplo

Durante a última década, o Bluetooth se tornou uma tecnologia amplamente utilizada para comunicação sem fio de curto alcance entre dispositivos, como celulares, computadores pessoais e fones de ouvido. O Bluetooth Special Interest Group (BTSIG) é o órgão que define os padrões para serviços sem fio nas especificações de perfil do Bluetooth.

Tal perfil é o Object Push Profile (OPP), que é usado para enviar arquivos de um dispositivo para outro. O Object Exchange Protocol (OBEX) é parte da fundação da OPP. OBEX também é utilizado em perfis além do OPP, como é um protocolo genérico para a transferência de objetos entre dispositivos.

Para os desenvolvedores que desejam usar OBEX dentro de seus aplicativos, eu desenvolvi um conjunto de APIs sobre as APIs do Bluetooth do Windows Runtime (WinRT) que fornecem OBEX de dentro do aplicativo. Essas APIs vêm como um pacote de biblioteca para aplicativos universais, o que significa que os aplicativos do Windows Store e aplicativos do Windows Phone Silverlight podem aproveitar o mesmo conjunto de APIs.

OBEX e OPP

Primeiro, é importante entender o que OBEX e OPP são e como eles funcionam. O OPP permite que um dispositivo Bluetooth envia um arquivo ou objeto para outro dispositivo Bluetooth com capacidade OPP. O uso destinado para OBEX foi o compartilhamento de arquivos via canais infravermelhos. O BTSIG escolhe reutilizar este protocolo para compartilhamento de arquivos através do Bluetooth. Além do meio de transporte subjacente, o OBEX sobre o infravermelho e o OBEX sobre o Bluetooth são semelhantes.

O OBEX é baseado em um modelo de servidor do cliente em que o dispositivo Bluetooth destinatário está executando um servidor OBEX que escuta e aceita conexões de clientes. O dispositivo Bluetooth cliente se conecta ao dispositivo Bluetooth servidor como um canal de fluxo através de Bluetooth. Os requisitos de autenticação para permitir a conexão e a transferência do objeto dependem do serviço ou aplicativo usando OBEX. Por exemplo, o OPP pode permitir uma conexão não autenticada, a fim de agilizar o processo de troca de cartões de visita de forma rápida. Outros serviços usando o OBEX só podem permitir conexões autenticadas.

Os arquivos são compartilhados usando três tipos de pacotes. Estes pacotes são conhecidos como primeiro PUT, PUT intermediário e último PUT. O primeiro pacote PUT marca a inicialização de transferência de arquivos e o último pacote PUT marca a sua conclusão. Os vários pacotes PUT intermediários contêm a maior parte dos dados. Depois que o servidor recebe cada pacote PUT, ele devolve um pacote de confirmação para o cliente.

Em um cenário típico, os pacotes OBEX são enviados da seguinte forma:

  1. O cliente OBEX se conecta ao dispositivo destinatário através do envio de um pacote de conexão. Esse pacote especifica o tamanho máximo do pacote que o cliente pode receber.
  2. Depois de receber uma resposta do servidor indicando que a conexão foi aceita, o cliente OBEX envia o primeiro pacote PUT. Ele contém os metadados que descrevem o objeto, incluindo o nome e tamanho do arquivo. (Enquanto o protocolo OBEX permite para este primeiro pacote PUT incluir também dados de objeto, a implementação de OBEX na biblioteca que eu desenvolvi não envia qualquer desses dados no primeiro pacote PUT.)
  3. Depois de receber o reconhecimento de que o servidor tem o primeiro pacote PUT, o cliente OBEX envia os vários pacotes PUT que contêm dados do objeto. O comprimento desses pacotes é limitado pelo tamanho máximo do pacote que o servidor pode receber, definido pela resposta do servidor para o pacote de conexão enviada na etapa um.
  4. O último pacote PUT contém a última constante PUT e a parte final dos dados do objeto.
  5. Uma vez que o compartilhamento de arquivos é concluído, o cliente OBEX envia um pacote de desconexão e fecha a conexão Bluetooth. O protocolo OBEX permite a repetição das etapas dois a três para enviar vários objetos na mesma conexão.

A qualquer momento, o cliente OBEX pode abortar o processo de compartilhamento enviando um pacote ABORTAR. O compartilhamento é imediatamente cancelado. Na biblioteca que eu escrevi, os detalhes da implementação do protocolo OBEX estão ocultos e você verá apenas APIs de alto nível.

A biblioteca OBEX

A biblioteca cliente Bluetooth OBEX para aplicativos Windows Store é projetada como uma segmentação da biblioteca portátil: Aplicativos da Windows Store e Windows Phone Silverlight 8.1. Eles contêm três DLLs que tomam um tempo de execução da biblioteca para o cliente OBEX. Cada DLL é projetada para manipular uma tarefa específica: Bluetooth.Core.Service, Bluetooth.Core.Sockets e Bluetooth.Services.Obex.

Bluetooth Core Service O arquivo Bluetooth.Core.Service.dll contém o namespace Bluetooth.Core.Service. Essa biblioteca é projetada para procurar e contar dispositivos Bluetooth emparelhados próximos ao dispositivo cliente (consulte Figura 1). Atualmente, ela é restrita a uma contagem de um tempo de dispositivos emparelhados. Futuras versões conterão um observador para manter a procura de dispositivos Bluetooth adicionais.

Figura 1 Métodos e eventos associados do BluetoothService

Método (com parâmetros) Eventos associados
[estático] GetDefault Nenhum evento associado
SearchForPairedDevicesAsync

Sucesso - SearchForDevicesSucceeded

Falha - SearchForPairedDevicesFailed

O serviço Bluetooth principal é representado por uma classe estática chamada BluetoothService, mostrado na Figura 2. Essa classe tem uma API para dispositivos de contagem assíncrona.

Figura 2 Dispositivos emparelhados de contagem do BluetoothService

BluetoothService btService = BluetoothService.GetDefault();
btService.SearchForPairedDevicesFailed 
  += btService_SearchForPairedDevicesFailed;
btService.SearchForPairedDevicesSucceeded 
  += btService_SearchForPairedDevicesSucceeded;
await btService.SearchForPairedDevicesAsync();
void btService_SearchForPairedDevicesSucceeded(object sender,
  SearchForPairedDevicesSucceededEventArgs e)
{
  // Get list of paired devices from e.PairedDevices collection
}
void btService_SearchForPairedDevicesFailed(object sender,
  SearchForPairedDevicesFailedEventArgs e)
{
  // Get the failure reason from e.FailureReason enumeration
}

Bluetooth Core Sockets O arquivo Bluetooth.Core.Sockets.dll contém o namespace Bluetooth.Core.Sockets e é projetado para suportar as operações de soquete com base no fluxo através de uma conexão Bluetooth. A funcionalidade do soquete é exposta através da classe BluetoothSockets (consulte Figura 3). Isso não é nem um TCP nem um soquete UDP. Todas as comunicações com o dispositivo destinatário ocorre através do BluetoothSockets.

Figura 3 Métodos e eventos associados do BluetoothSockets

Método (com parâmetros) Eventos associados

Constructor(Bluetooth.Core.Services.BluetoothDevice)

Constructor(Bluetooth.Core.Services.BluetoothDevice, System.UInt32)

Constructor(Bluetooth.Core.Services.BluetoothDevice, System.String)

Constructor(Bluetooth.Core.Services.BluetoothDevice, System.UInt32, System.String)

Nenhum evento associado
PrepareSocketAsync

Sucesso – SocketConnected

Falha – ErrorOccured

SendDataAsync(System.Byte[])

SendDataAsync(System.String)

Nenhum evento associado
CloseSocket SocketClosed
Nenhum método associado DataReceived

Bluetooth Services Obex O arquivo Bluetooth.Services.Obex.dll contém namespace Bluetooth.Services.Obex. Essa é a implementação principal do OBEX, exposto através de uma classe chamada ObexService. Essa classe fornece a visão abstrata da especificação Bluetooth OPP. Ela expõe o método que ajuda a conectar, enviar e desconectar do dispositivo Bluetooth destinatário. A Figura 4 lista as APIs e eventos associados que essa classe expõe e a Figura 5 demonstra o uso.

Figura 4 Métodos e eventos associados do ObexService

Método (com parâmetros) Eventos associados
[static] GetDefaultForBluetoothDevice(Bluetooth.Core.Services.BluetoothDevice) Nenhum evento associado
ConnectAsync

Sucesso – DeviceConnected

Falha – ConnectionFailed

SendFileAsync(Windows.Storage.IStorageFile)

Sucesso:

ServiceConnected

DataTransferProgressed

DataTransferSucceeded

Desconectando

Desconectado

Falha:

ConnectionFailed

DataTransferFailed

AbortAsync Abortado

Figura 5 Uso do serviço Obex

protected async override void OnNavigatedTo(Windows.UI.Xaml.Navigation.NavigationEventArgs e)
{
  base.OnNavigatedTo(e);
  ObexService obexService = ObexService.GetDefaultForBluetoothDevice(null);
  obexService.DeviceConnected += obexService_DeviceConnected;
  obexService.ServiceConnected += obexService_ServiceConnected;
  obexService.ConnectionFailed += obexService_ConnectionFailed;
  obexService.DataTransferProgressed += obexService_DataTransferProgressed;
  obexService.DataTransferSucceeded += obexService_DataTransferSucceeded;
  obexService.DataTransferFailed += obexService_DataTransferFailed;
  obexService.Disconnecting += obexService_Disconnecting;
  obexService.Disconnected += obexService_Disconnected;
  obexService.Aborted += obexService_Aborted;
  await obexService.ConnectAsync();
}
async void obexService_DeviceConnected(object sender, EventArgs e)
{
  // Device is connected, now send file
  await (sender as ObexService).SendFileAsync(fileToSend);
}
void obexService_ServiceConnected(object sender, EventArgs e)
{
  // Device connected to Obex Service on target device
}
void obexService_ConnectionFailed(object sender, 
  ConnectionFailedEventArgs e)
{
  // Connection to service failed
}
void obexService_DataTransferProgressed(object sender, 
  DataTransferProgressedEventArgs e)
{
  // Get data transfer progress from DataTransferProgressedEventArgs
}
void obexService_DataTransferSucceeded(object sender, EventArgs e)
{
  // Data transfer succeeded
}
void obexService_DataTransferFailed(object sender, DataTransferFailedEventArgs e)
{
  // Data transfer failed, get the reason from DataTransferFailedEventArgs
}
void obexService_Disconnecting(object sender, EventArgs e)
{
  // Device is disconnecting from service
  }
void obexService_Disconnected(object sender, EventArgs e)
{
  // Device is now disconnected from targeted device and service
}
void obexService_Aborted(object sender, EventArgs e)
{
  // Data transfer operation is aborted
}

Um cenário de uso típico para o uso dessas bibliotecas é algo como isto:

  • Utilizando APIs em Bluetooth.Core.Service, contar todos os dispositivos Bluetooth com capacidade OPP emparelhados. Verificação da capacidade OPP já está implementada.
  • Obter a instância do BluetoothDevice com o qual você deseja compartilhar arquivos.
  • Obter uma instância de ObexService para o dispositivo Bluetooth destinatário passando a instância de BluetoothDevice ao método de fábrica. A classe ObexService internamente criará uma instância de BluetoothSocket, sobre o qual o ObexService compartilhará o arquivo.
  • Uma vez que o compartilhamento de arquivos está concluído, o ObexService é desconectado automaticamente.

introdução

Como a minha biblioteca é destinada a aplicativos do Windows Store e Windows Phone 8.1, eu começarei com um aplicativo universal. Elas são uma ótima maneira de desenvolver aplicativos para todos os dispositivos do Windows. Para saber mais sobre os aplicativos universais, visite bit.ly/1h3AQeu. Para começar com aplicativos universais, eu usarei o Visual Studio 2013 e criarei um novo projeto de aplicativos universais sob o nó de aplicativos Store (consulte Figura 6). Eu usei Visual C#, mas você também pode usar o Visual Basic e Visual C++.

Aplicativo universal em branco para criar um novo projeto
Figura 6 Aplicativo universal em branco para criar um novo projeto

Antes de eu começar a programação para um aplicativo cliente Bluetooth OBEX, eu atualizarei o arquivo package.appx-manifest para ambos os projetos (aplicativos para o Windows 8.1 e Windows Phone 8.1):

<Capabilities>
  <m2:DeviceCapability Name="bluetooth.rfcomm">
    <m2:Device Id="any">
      <m2:Function Type="name:obexObjectPush"/>
    </m2:Device>
  </m2:DeviceCapability>
</Capabilities>

Para aplicar essa atualização, eu abro o package.appx-manifest como um arquivo de código ao escolher Exibir Código no menu de contexto no Gerenciador de Soluções. Colocarei este trecho onde a tag do <Aplicativo> termina. Esse trecho de código é necessário para fornecer capacidade de nível de dispositivo para utilizar o serviço de comunicação de frequência de rádio Bluetooth (RFCOMM) com este aplicativo. Eu também especifiquei todos os serviços e tipos de dispositivos compatíveis com este dispositivo.

Para o meu cenário, eu preciso de suporte obexObjectPush do dispositivo que é compatível com qualquer dispositivo com capacidade OPP. Para obter mais informações sobre os perfis suportados no Windows 8.1 e Windows Phone 8.1, visite bit.ly/1pG6rYO. Se essa capacidade não é mencionada, a enumeração do dispositivo falhará com a constante de enum. CapabilityNotDefined.

Antes de eu começar a codificação, adicionarei a referência aos três arquivos de biblioteca mencionados anteriormente para que eu possa usar a implementação OBEX dentro dessas bibliotecas. Eu preciso adicionar uma referência a essas bibliotecas para ambos os projetos separadamente. Se a referência não é adicionada, o projeto não será capaz de utilizar qualquer recurso.

Eu seguirei esses padrões e práticas de codificação:

  • Implementar o design UX para o aplicativo do Windows Store 8.1 no projeto do Windows.
  • Implementar o design UX para os aplicativos do Windows Phone 8.1 no projeto do Windows Phone.
  • Implementar o recurso comum em projeto compartilhado.
  • Execute a implementação de plataforma específica no projeto compartilhado usando constantes do compilador específicos da plataforma. Para Windows 8.1, eu usarei o WINDOWS_APP. Para Windows Phone 8.1, eu usarei o WINDOWS_PHONE_APP. Essas constantes do compilador já estão definidas como parte do projeto.

Baixe o código de exemplo para obter experiência prática com estas bibliotecas, juntamente com as práticas de codificação que você deve seguir para o desenvolvimento de aplicativos universais. A Figura 7 mostra a janela do Gerenciador de Soluções do exemplo de projeto com a estrutura e padrões do arquivo.

Gerenciador de Soluções do aplicativo BluetoothDemo
Figura 7 Gerenciador de Soluções do aplicativo BluetoothDemo

Enumerando os dispositivos emparelhados

Antes que eu possa compartilhar arquivos com um dispositivo emparelhado, eu preciso ver a lista de dispositivos emparelhados e escolher um destino. Eu pegarei o manipulador para a instância de Bluetooth.Core.Services.BluetoothService, que representa o serviço principal de Bluetooth fornecido pelo meu dispositivo. Eu adquiri esta instância utilizando o método de fábrica estático GetDefault, porque há apenas um serviço Bluetooth disponível por dispositivo.

Para enumeração, eu farei uma chamada para o método SearchForPairedDevicesAsync. Este método começará enumerando dispositivos emparelhados com o meu dispositivo. Para aplicativos Windows Store 8.1, eu preciso permitir o uso de um dispositivo emparelhado para obter os dispositivos enumerados. Se eu bloquear o uso, esse dispositivo emparelhado não será enumerado.

Se esse API é bem-sucedido, ele aumentará o evento SearchForPairedDevicesSucceeded e buscará a coleção de dispositivos emparelhados de seu argumento do evento. Caso contrário, o evento SearchForPairedDevicesFailed será gerado, com constante falha de enum. disponível em seu argumento do evento. A Figura 8 mostra o código para a enumeração dos dispositivos.

Figura 8 Enumerando os dispositivos emparelhados

protected async override void OnNavigatedTo(NavigationEventArgs e)
{
  base.OnNavigatedTo(e);
  await EnumerateDevicesAsync();
}
public async Task EnumerateDevicesAsync()
{
  BluetoothService btService = BluetoothService.GetDefault();
  btService.SearchForPairedDevicesFailed +=
    btService_SearchForPairedDevicesFailed;
  btService.SearchForPairedDevicesSucceeded +=
    btService_SearchForPairedDevicesSucceeded;
  await btService.SearchForPairedDevicesAsync();
}
void btService_SearchForPairedDevicesSucceeded(object sender,
  SearchForPairedDevicesSucceededEventArgs e)
{
  (sender as BluetoothService).SearchForPairedDevicesFailed -=
    btService_SearchForPairedDevicesFailed;
  (sender as BluetoothService).SearchForPairedDevicesSucceeded -=
    btService_SearchForPairedDevicesSucceeded;
  this.cvBtDevices.Source = e.PairedDevices;
}
void btService_SearchForPairedDevicesFailed(object sender,
  SearchForPairedDevicesFailedEventArgs e)
{
  (sender as BluetoothService).SearchForPairedDevicesFailed -=
    btService_SearchForPairedDevicesFailed;
  (sender as BluetoothService).SearchForPairedDevicesSucceeded -=
    btService_SearchForPairedDevicesSucceeded;
  txtblkErrorBtDevices.Text = e.FailureReason.ToString();
}

Eu também forneci o botão de digitalização no BottomAppBar para o Windows 8.1 e ApplicationBar para Windows Phone 8.1. Dessa forma, um usuário do aplicativo pode examinar novamente por dispositivos quando um novo dispositivo emparelhado chegar.

Ao enumerar os dispositivos no Windows Phone 8.1, ele enumerará todos os dispositivos com capacidade OBEX, independentemente da sua presença física e se o rádio Bluetooth está ligado. No entanto, ao enumerar os dispositivos no Windows 8.1, ele só listará os dispositivos presentes fisicamente próximos do dispositivo Windows 8.1 e com o seu rádio Bluetooth ligado.

Uma vez que eu enumerar os dispositivos emparelhados, posso selecionar um dispositivo para compartilhar os arquivos. Cada dispositivo é representado como um objeto da classe Bluetooth.Core.Services.BluetoothDevice. O objeto contém detalhes de conexão e o nome de exibição do dispositivo emparelhado. O Bluetooth.Services.Obex.ObexService usará os detalhes de conexão internamente para criar uma instância de Bluetooth.Core.Sockets.BluetoothSocket e se conectar a um dispositivo emparelhado.

Usando o ObexService

Uma vez que eu obtenho a instância do objeto Bluetooth.Core.Services.BluetoothDevice que representa meu dispositivo de destino para compartilhar arquivos, eu posso usarBluetooth.Services.Obex.ObexService para compartilhar arquivos usando OPP. Eu também preciso de uma lista de arquivos para que eu possa colocá-los em fila para compartilhamento. No exemplo de código, eu só tenho fornecido alguns arquivos. Caso contrário, eu poderia usar Windows.Storage.Pickers.FileOpenPicker (consulte bit.ly/1qtiLeh) ou lógica personalizada no meu Windows.Storage.ApplicationData.Current.LocalFolder (consulte bit.ly/1qtiSGI) para selecionar vários arquivos.

A partir de agora, só posso compartilhar um arquivo por conexão. Quando o compartilhamento está completo, a conexão para o dispositivo de destino é fechada. Se eu precisar enviar vários arquivos, eu preciso obter o manipulador para a instância Bluetooth.Services.Obex.ObexService várias vezes. Eu posso adquirir esta instância utilizando o método de fábrica estático GetDefaultForBluetoothDevice (Bluetooth.Core.Services.BluetoothDevice). Este método retorna a única instância do Bluetooth.Services.Obex.ObexService que representa o serviço Obex no dispositivo.

Para representar o arquivo para compartilhar, eu usarei a classe FileItemToShare. Este contém o nome, caminho e tamanho do arquivo, e a instância do Windows.Storage.IStorageFile (consulte bit.ly/1qMcZlB), que representa a instância do arquivo no disco. Colocarei em fila todos os arquivos que eu tenho para compartilhar em termos de objetos de estrutura de dados. Ao compartilhar vários arquivos, o primeiro arquivo da lista é o que está atualmente sendo compartilhado. É removido da lista quando o compartilhamento está concluído. A Figura 9 mostra como conectar o ObexService e seus eventos para o compartilhamento de arquivos.

Figura 9 Conectando o ObexService e seus eventos

ObexService obexService = null;
BluetoothDevice BtDevice = null;
ObservableCollection<FileItemToShare> filesToShare = null;
async override void OnNavigatedTo(NavigationEventArgs e)
{
  base.OnNavigatedTo(e);
  if (e.Parameter == null || !(e.Parameter is BluetoothDevice))
  {
    MessageDialog messageBox = new MessageDialog(
      "Invalid navigation detected. Moving to Main Page", "Bluetooth Hub");
    messageBox.Commands.Add(new UICommand("OK", (uiCommand) =>
    {
      this.Frame.Navigate(typeof(MainPage));
    }));
    await messageBox.ShowAsync();
    return;
  }
  BtDevice = e.Parameter as BluetoothDevice;
  filesToShare = GetFilesFromLocalStorage();
  this.cvFileItems.Source = filesToShare;
  obexService = ObexService.GetDefaultForBluetoothDevice(BtDevice);
  obexService.Aborted += obexService_Aborted;
  obexService.ConnectionFailed += obexService_ConnectionFailed;
  obexService.DataTransferFailed += obexService_DataTransferFailed;
  obexService.DataTransferProgressed += obexService_DataTransferProgressed;
  obexService.DataTransferSucceeded += obexService_DataTransferSucceeded;
  obexService.DeviceConnected += obexService_DeviceConnected;
  obexService.Disconnected += obexService_Disconnected;
  obexService.Disconnecting += obexService_Disconnecting;
  obexService.ServiceConnected += obexService_ServiceConnected;
  await obexService.ConnectAsync();
}

Quando eu chamar o método ConnectAsync, o objeto ObexService recebe as propriedades de conexão do objeto BluetoothDevice, que foi aprovado no método de fábrica. Ele tenta criar uma conexão com o BluetoothDevice de destino pelo canal de Bluetooth. Quando este é bem-sucedido, ele gera o evento DeviceConnected. A Figura 10 mostra o manipulador do evento DeviceConnected do ObexService.

Figura 10 Método do manipulador do evento DeviceConnected

async void obexService_DeviceConnected(object sender, EventArgs e)
{
  await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
  {
    System.Diagnostics.Debug.WriteLine("device connected");
    if (filesToShare.Count > 0)
    {
      filesToShare.ShareStatus = FileShareStatus.Connecting;
      await obexService.SendFileAsync(filesToShare[0].FileToShare);
    }
    ...
  });
}

Assim que o dispositivo está conectado ao dispositivo de destino, eu começarei a compartilhar o arquivo no índice 0 da lista de arquivos. O arquivo é compartilhado ao chamar SendFileAsync (Windows.Storage.IStorageFile) e passar o objeto de arquivo representado por IStorageFile do objeto do tipo de estrutura de dados FileToShare. Quando este método é chamado, o ObexService tenta se conectar ao Servidor OBEX em execução no dispositivo de destino. Se a conexão for bem-sucedida, ela gerará o evento ServiceConnected. Caso contrário, ela gerará o evento ConnectionFailed. Este código mostra o manipulador de eventos ServiceConnected:

async void obexService_ServiceConnected(object sender, EventArgs e)
{
  await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
  {
    System.Diagnostics.Debug.WriteLine("service connected");
    filesToShare[0].ShareStatus = FileShareStatus.Sharing;
  });
}

A Figura 11 mostra o manipulador de eventos ConnectionFailed do ObexService.

Figura 11 Método do manipulador de evento ConnectionFailed

async void obexService_ConnectionFailed(object sender, 
  ConnectionFailedEventArgs e)
{
  await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
  {
    System.Diagnostics.Debug.WriteLine("connection failed");
    filesToShare[0].ShareStatus = FileShareStatus.Error;
    filesToShare[0].Progress = 0;
    FileItemToShare currentItem = filesToShare[0];
    filesToShare.RemoveAt(0);
    filesToShare.Add(currentItem);
  });
}

Quando o dispositivo se conecta ao servidor OBEX do dispositivo alvo, o processo de compartilhamento de arquivos começa. O progresso de arquivos compartilhados pode ser determinado pelo evento DataTransferProgressed. O código a seguir mostra o método DataTransferProgressed:

async void obexService_DataTransferProgressed(object sender,
  DataTransferProgressedEventArgs e)
{
  await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
  {
    System.Diagnostics.Debug.WriteLine("Bytes {0}, Percentage {1}",
      e.TransferInBytes, e.TransferInPercentage);
    filesToShare[0].Progress = e.TransferInBytes;
  });
}

Uma vez que o compartilhamento de arquivos está concluído, ele gera o evento DataTransferSucceeded. Se o compartilhamento de arquivos não for bem sucedido, ele gera um DataTransferFailedEvent. O código a seguir mostra o manipulador de eventos DataTransferSucceeded:

async void obexService_DataTransferSucceeded(object sender, EventArgs e)
{
  await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
  {
    System.Diagnostics.Debug.WriteLine("data transfer succeeded");
    filesToShare.RemoveAt(0);
  });
}

Em caso de um erro de compartilhamento de arquivos, ele gerará um evento DataTransferFailed. O manipulador de eventos é mostrado no código a seguir:

async void obexService_DataTransferFailed(object sender,
  DataTransferFailedEventArgs e)
{
  await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
  {
    System.Diagnostics.Debug.WriteLine("Data transfer failed {0}",
      e.ExceptionObject.ToString());
    filesToShare[0].ShareStatus = FileShareStatus.Error;
    filesToShare[0].Progress = 0;
    FileItemToShare fileToShare = this.filesToShare[0];
    filesToShare.RemoveAt(0);
    this.filesToShare.Add(fileToShare);
  });
}

Quando a transferência de dados estiver concluída, o arquivo compartilhado é removido da lista e o ObexService é desconectado. Quando o ObexService é desconectado, ele gera o evento Desconectar. E, quando a conexão é desconectada corretamente, o evento Desconectado é gerado. O manipulador de eventos Desconectar é mostrado aqui:

void obexService_Disconnecting(object sender, EventArgs e)
{
  System.Diagnostics.Debug.WriteLine("disconnecting");
}

Uma vez que a conexão é desconectada com sucesso, o código na Figura 12 manipula, gerando o evento Desconectado.

Figura 12 Método de manipulação do evento Desconectado

async void obexService_Disconnected(object sender, EventArgs e)
{
  await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
  {
    System.Diagnostics.Debug.WriteLine("disconnected");
    obexService.Aborted -= obexService_Aborted;
    obexService.ConnectionFailed -= obexService_ConnectionFailed;
    obexService.DataTransferFailed -= obexService_DataTransferFailed;
    obexService.DataTransferProgressed -= 
      obexService_DataTransferProgressed;
    obexService.DataTransferSucceeded -= 
      obexService_DataTransferSucceeded;
    obexService.DeviceConnected -= obexService_DeviceConnected;
    obexService.Disconnected -= obexService_Disconnected;
    obexService.Disconnecting -= obexService_Disconnecting;
    obexService.ServiceConnected -= obexService_ServiceConnected;
    obexService = null;
    if (filesToShare.Count.Equals(0))
    {
      ...
      MessageDialog messageBox =
        new MessageDialog("All files are shared successfully",
        "Bluetooth DemoApp");
      messageBox.Commands.Add(new UICommand("OK", (uiCommand) =>
      {
        this.Frame.Navigate(typeof(MainPage));
      }));
      await messageBox.ShowAsync();
    }
    else
    {
      obexService = ObexService.GetDefaultForBluetoothDevice(BtDevice);
      obexService.Aborted += obexService_Aborted;
      obexService.ConnectionFailed += obexService_ConnectionFailed;
      obexService.DataTransferFailed += obexService_DataTransferFailed;
      obexService.DataTransferProgressed += 
        obexService_DataTransferProgressed;
      obexService.DataTransferSucceeded += 
        obexService_DataTransferSucceeded;
      obexService.DeviceConnected += obexService_DeviceConnected;
      obexService.Disconnected += obexService_Disconnected;
      obexService.Disconnecting += obexService_Disconnecting;
      obexService.ServiceConnected += obexService_ServiceConnected;
      await obexService.ConnectAsync();
    }
  });
}

Quando o evento Desconectado é gerado, remova todos os manipuladores e limpe a instância ObexService. Durante a transferência de dados, as condições podem surgir requerendo que você aborte a transferência atual. Para abortar a transferência atual, chame AbortAsync. O evento Abortado é gerado com o seguinte código e, em seguida, a conexão com o dispositivo de destino é encerrada:

async void obexService_Aborted(object sender, EventArgs e)
{
  await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
  {
    System.Diagnostics.Debug.WriteLine("Aborted");
    if (!filesToShare.Count.Equals(0))
    {
      filesToShare.RemoveAt(0);
    }
  });
}

Conclusão

O aplicativo de demonstração foi concluído. O conceito de aplicativos universais podem realmente ajudá-lo a escrever uma única parte do código para várias plataformas do Windows e fatores de forma, reduzindo assim, o esforço global de desenvolvimento.

Eu usei essas bibliotecas em vários aplicativos do Windows Store e do Windows Phone. Pesquisar por Criar Código (um aplicativo do Windows Phone gratuito) ou OBEX (aplicativo universal) e obter uma prévia de como essas APIs trabalham em conjunto com o aplicativo. Estas bibliotecas estão disponíveis para download no repositório NuGet. Basta procurar por “Bluetooth OBEX para Aplicativos da Store” na caixa de diálogo online NuGet, direto da Solução do Visual Studio e importar essas bibliotecas como uma referência para os projetos.


Uday Gupta é um engenheiro sênior de desenvolvimento de produto na Symphony Teleca Corp. Pvt Ltd., na Índia. Ele tem experiência em muitas tecnologias .NET, especialmente Windows Presentation Foundation, Silverlight, Windows Phone e Windows 8.x.

Agradecemos aos seguintes especialistas técnicos da Microsoft pela revisão deste artigo: Jeff Kelley e Guruprasad Subbarayan