1 – Introdução ao Wi-Fi Direct
No post anterior, vimos como fazer a preparação inicial do Wi-Fi Direct em dispositivos Android. Primeiro precisamos de uma classe para receber as ações ocorridas nos arredores dos dispositivos (Broadcast Receiver) e precisamos de uma outra classe (MeuWiFiDirect) para iniciar o receiver, que é a atividade principal da aplicação e faz todas as ações necessárias ao usuário.
Agora, mostraremos como, dentro do Android, podemos fazer os casos em que um dispositivo se torna AP da rede e quando a comunicação é totalmente P2P, sem AP.
2 – Rede com AP Wi-Fi Direct
Começaremos nossos exemplos no modo “clássico” do Wi-Fi Direct, ou seja, com um dispositivo sendo o AP da rede. Para isso, como discutido nos primeiros posts sobre Wi-Fi Direct, é preciso que algum dispositivo se torno um AP. Isso ocorre de duas formas: uma negociação através dos números de intenção e um dispositivo se tornando automaticamente o AP.
Dentro do Android isso é feito quando criamos uma instância do nosso Broadcast Receiver dentro da classe MeuWiFiDirect. Se olharmos no post anterior, o código de criação é:
mReceiver = new WiFiDirectBroadcastReceiver(mManager, mChannel, this);
Desta forma, todos os dispositivos que utilizarem ela farão a negociação, ou seja, enviarão a seus vizinhos um número de intenção e o maior se torna o AP.
Caso quisermos fazer com que um dispositivo específico, ou um dispositivo que rode uma atividade que seja para o AP, se torne o AP, devemos adicionar mais um parâmetro no construtor: o número de intenção. Portanto para o caso de fazer um dispositivo específico ser o AP, o código ficaria assim:
mReceiver = new WiFiDirectBroadcastReceiver(mManager, mChannel, this, 15);
Agora, existe um número indicando a intenção do dispositivo de se tornar AP, neste caso, ele se tornaria pois 15 é o maior número. Para se ter mais segurança que o dispositivo em questão será o AP, nos outros dispositivos (que serão os clientes) ao invés do número 15, adicione o número 0 (zero), já que se o número não estiver informado, o cliente poderá enviar 15 (aleatoriamente) e tentar se tornar o AP.
Cliente:
mReceiver = new WiFiDirectBroadcastReceiver(mManager, mChannel, this, 0);
Dessa forma, será necessário armazenar esse número no Broadcast Receiver:
WiFiDirectBroadcastReceiver:
public class WiFiDirectBroadcastReceiver extends BroadcastReceiver{ private WifiP2pManager mManager; private Channel mChannel; private MyWiFiActivity mActivity; private int intNumber; public WiFiDirectBroadcastReceiver(WifiP2pManager manager, Channel channel, MyWifiActivity activity, int intNumber){ super(); this.mManager = manager; this.mChannel = channel; this.mActivity = activity; this.intNumber = intNumber; }
2.1 – Descobrindo dispositivos vizinhos
Com isso temos os dispositivos com seus respectivos papéis na rede: AP e cliente. Agora podemos requisitar conexão para fazer a troca de informações. Para isso, devemos primeiro saber os nomes dos dispositivos em volta. Para tal, devemos procurar por esses dispositivos chamado o método discoverPeers() na nossa atividade principal. Este método, caso seja encontrado algum dispositivo, envia o intent WIFI_P2P_PEERS_CHANGED_ACTION. Este intent pode ser recebido pelo Broadcast Receiver e assim quem o receber, pode requisitar uma lista de dispositivos através do método requestPeers().
MeuWiFiDirect:
mManager.discoverPeers(channel,new WifiP2pManager.ActionListener(){ @Override public void onSuccess(){ ... } @Override public void onFailure(int reasonCode){ ... } });
Código adaptado do site.
WiFiDirectBroadcastReceiver:
PeerListListener myPeerListListener; ... if(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)){ // requisita dispositivos disponiveis do wifi p2p manager. Essa eh uma // chamada assincrona e atividade que a esta chamando eh notificada com um // callback em PeerListListener.onPeersAvailable() if(mManager != null){ mManager.requestPeers(mChannel, myPeerListListener); } }
Código adaptado do site.
O método requestPeers() também é assíncrono e pode notificar sua atividade principal quando uma lista de dispositivos está disponível através do método onPeersAvailable(), definido na interface WifiP2pManager.PeerListListener(). Este método, dá à atividade, uma lista dos dispositivos disponíveis na qual ela pode iterar sobre. A lista é uma instância de WifiP2pDeviceList. De posse desta lista, a aplicação pode escolher um dos dispositivos e se conectar a ela.
Essa sequência pode ser melhor exemplificada se adicionarmos um botão para que o usuário escolha iniciar a procura por dispositivos:
MeuWiFiDirect:
... private Button btnProcurar; ... @Override protected void onCreate(Bundle savedInstanceState) { ... btnProcurar = (Button)findViewById(R.id.btnProcurar); btnProcurar.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { mManager.discoverPeers(channel,new WifiP2pManager.ActionListener(){ @Override public void onSuccess(){ ... } @Override public void onFailure(int reasonCode){ ... } }); } } }
WiFiDirectBroadcastReceiver:
... private WifiP2pDeviceList disponiveis; PeerListListener myPeerListListener; ... if(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)){ // requisita dispositivos disponiveis do wifi p2p manager. Essa eh uma // chamada assincrona e atividade que a esta chamando eh notificada com um // callback em PeerListListener.onPeersAvailable() if(mManager != null){ mManager.requestPeers(mChannel, myPeerListListener); } } ... private PeerListListener peerListListener = new PeerListListener() { @Override public void onPeersAvailable(WifiP2pDeviceList peers) { // seu codigo } };
2.2 – Conectando a um dispositivo Wi-Fi Direct
Com a lista de dispositivos em mãos, a aplicação pode escolher um e se conectar a ele. Para tal, criaremos uma lista de dispositivo utilizando a classe List<E> nativa de Java. Preencheremos ela com a lista WifiP2pDeviceList obtida no método onPeersAvailable(). Esse passo é feito pois precisamos do endereço do dispositivo, que não é possível ser obtido na lista retornada pelo método.
Primeiro, preencheremos um spinner com a lista retornada pelo método (não coberto nesse post), e quando um dos dispositivos no spinner for selecionado, nos conectamos ao dispositivo daquela posição chamando o método connect(). Para nos conectar ao dispositivo, precisamos de uma instância de WifiP2pConfig contendo as configurações do dispositivo que desejamos nos conectar. Nessas configurações, caso a aplicação queira que o dispositivo seja o AP, o número de intenção deve ser informado.
Para saber se a conexão foi estabelecida com sucesso, devemos criar uma instância de WifiP2pManager.ActionListener.
Com isso, podemos enviar as informações que desejamos através de sockets java. Como utilizar essa classe não será discutido nesse post.
WiFiDirectBroadcastReceiver:
... private WifiP2pDeviceList disponiveis; PeerListListener myPeerListListener; ... if(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)){ // requisita dispositivos disponiveis do wifi p2p manager. Essa eh uma // chamada assincrona e atividade que a esta chamando eh notificada com um // callback em PeerListListener.onPeersAvailable() if(mManager != null){ mManager.requestPeers(mChannel, myPeerListListener); } } ... private PeerListListener peerListListener = new PeerListListener() { @Override public void onPeersAvailable(WifiP2pDeviceList peers) { disponiveis=peers mActivity.preencherSpinner(peers) } }; ... public void connectDevicePos(int pos){ // variavel 'pos' vem do evento de clique no spinner dentro da atividade principal List<WifiP2pDevice> deviceList = new ArrayList<WifiP2pDevice>(); deviceList.addAll(disponiveis.getDeviceList()); WifiP2pConfig config = new WifiP2pConfig(); config.groupOwnerIntent = this.intNumber; config.deviceAddress = deviceList.get(pos).deviceAddress; config.wps.setup = WpsInfo.PBC; mManager.connect(mChanel, config, new ActionListener(){ @Override public void onSuccess(){ ... } @Override public void onFailure(int reason){ ... } }); }