Wi-Fi Direct no Android – Parte 1

On 18 de dezembro de 2015 by Ricardo Pagoto

wifidirectandroid1 – Introdução

Anteriormente (aqui e aqui), falamos sobre a tecnologia Wi-Fi Direct de forma mais teórica na forma como ela é explorada e apresentada na literatura. Neste post iniciaremos uma abordagem mais prática, com exemplos de usos em dispositivos reais Android. Nestes exemplos, explicaremos a abordagem que a plataforma utiliza para possibilitar a comunicação direta entre dispositivos e serão mostrados trechos de códigos para as fazes da a abordagem.

Obs.: Para esse post, é necessário uma compreensão básica sobre o funcionamento de aplicativos Android, como o que são atividades, intents e o ciclo de vida das atividades. Para mais detalhes, acesse o site oficial da API Android. Além disso, como a API Android é baseada em Java, é necessário noções sobre como programar nessa linguagem.

 

2 – Primeiros passos

Para que seu dispositivo possa executar todas as funções do Wi-Fi Direct, tais como, procurar e ouvir outros dispositivos, primeiro devemos criar uma classe, aqui chamada de WiFiDirectBroadcastReceiver, que estenda Broadcast Receiver para responder às requisições de outros dispositivos. Esta classe permite ao dispositivo receber intents enviados por outros para que sua aplicação possa responder a eles.

A classe WiFiDirectBroadcastReceiver receberá em seu construtor uma instância da classe WifiP2pMannager, um canal de comunicação, dado pela subclasse de WifiP2pMannager, WifiP2pMannager.Channel e a atividade que este Broadcast Receiver estará ligado. O primeiro argumento WifiP2pMannager é a classe que permite ao dispositivo, descobrir, requisitar e se conectar a outros através de uma conexão P2P. O canalWifiP2pMannager.Channel é responsável por criar um canal entre a aplicação e o framework P2P wifi, ou seja, ele é responsável por permitir à aplicação utilizar as funções da interface wifi. Já o último, a atividade, diz para a aplicação, qual atividade ficará responsável por controlar as ações do Wi-Fi Direct.

Além do construtor, a classe WiFiDirectBroadcastReceiver precisa da implementação do método onReceive() que recebe o Contexto da aplicação e um Intent. Intent carrega qual a ação que outro dispositivo enviou, ou seja, a informação sobre alguma ação ocorrida nas redondezas. Para utilizar o Wi-Fi Direct, é necessário que a aplicação se interesse em quatro ações:

  1. WIFI_P2P_STATE_CHANGED_ACTION
  2. WIFI_P2P_PEERS_CHANGED_ACTION
  3. WIFI_P2P_CONNECTION_CHANGED_ACTION
  4. WIFI_P2P_THIS_DEVICE_CHANGED_ACTION

A primeira ação é enviada quando o Wi-Fi Direct é ligado ou desligado. A segunda é enviada quando o dispositivo inicia uma procura por outros dispositivos através do método discoverPeers(). A terceira é enviado quandoo estado da conexão wifi do dispositivo muda, ou seja, quando o wifi muda de ligado para desligado e vice-versa. Já a última é enviada quando alguma informação do dispositivo, como nome, é alterada.

/**
 * Um BroadcastReceiver que notifica eventos P2P wifi importantes.
 */
public class WiFiDirectBroadcastReceiver extends BroadcastReceiver{

    private WifiP2pManager mManager;
    private Channel mChannel;
    private MyWiFiActivity mActivity;

    public WiFiDirectBroadcastReceiver(WifiP2pManager manager, Channel channel, MyWifiActivity activity){
        super();
        this.mManager = manager;
        this.mChannel = channel;
        this.mActivity = activity;
    }

    @Override
    public void onReceive(Context context, Intent intent){
        String action = intent.getAction();

        if(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)){
            // Verificar se wifi esta ligado e notificar a atividade apropriada
        }else if(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)){
            // Chamar WifiP2pManager.requestPeers() para obter uma lista dos dispositivos disponiveis
        }else if(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)){
            // Responder a novas conexoes ou desconexoes
        }else if(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)){
           // Responder a mudancas de estado deste dispositivo
        }
    }
}

Código adaptado do site.

O método WifiP2pManager.requestPeers() informado na segunda ação do onReceive() faz com que o dispositivo em questão, após receber uma ação de que alguém iniciou uma procura por dispositivos, crie uma lista dos dispositivos no seu alcance. Este método será discutido mais a diante.

2 – Criando uma aplicação

Até agora, o que fizemos foi mostrar como um dispositivo pode perceber e, possivelmente, identificar os dispositivos ao seu redor. Agora, iremos integrar essa funcionalidade a uma aplicação real.

Primeiro, tenha em mente que a atividade principal da aplicação, que é onde você irá criar um Broadcast Receiver e as funções da aplicação, é um arquivo (uma classe), e o Broadcast Receiver em si é outro arquivo (outra classe). Chamaremos a primeira classe de MeuWiFiDirect e a segunda de WiFiDirectBroadcastReceiver.

Para que a aplicação possa utilizar as funções P2P do dispositivo, algumas permissões devem ser adicionadas no arquivo manifest da aplicação. Essas permissões são:

  1. CHANGE_WIFI_STATE
  2. CHANGE_NETWORK_STATE
  3. INTERNET
  4. ACCESS_NETWORK_STATE

Isso é feito adicionando as seguintes linhas no arquivo:

<uses-sdk android:minSdkVersion="14">
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE">
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE">
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE">
<uses-permission android:name="android.permission.INTERNET">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE">

Código adaptado do site.

Com essas linhas, sua aplicação poderá utilizar os recursos do Wi-Fi Direct.

Essas linhas porém, só dão permissão para uma possível utilização do hardware, mas não garantem que seu dispositivo possua o Wi-Fi Direct habilitado. Esse tipo de verificação é possível de ser feita no WiFiDirectBroadcastReceiver.

Assim que o dispositivo perceber alguma mudança no estado do Wi-Fi Direct de alguém, ele pode verificar se o seu está ligado e habilitado ou não. Essa mudança é capturada quando ele recebe uma ação de WIFI_P2P_STATE_CHANGED_ACTION, que possui um número extra, indicando se o dispositivo possui ou não o Wi-Fi Direct habilitado. Com essa informação, a aplicação deve ser notificada e reagir da forma correta.

/**
 * Um BroadcastReceiver que notifica eventos P2P wifi importantes.
 */
public class WiFiDirectBroadcastReceiver extends BroadcastReceiver{

    private WifiP2pManager mManager;
    private Channel mChannel;
    private MyWiFiActivity mActivity;

    public WiFiDirectBroadcastReceiver(WifiP2pManager manager, Channel channel, MyWifiActivity activity){
        super();
        this.mManager = manager;
        this.mChannel = channel;
        this.mActivity = activity;
    }

    @Override
    public void onReceive(Context context, Intent intent){
        String action = intent.getAction();

        //percebeu a mudanca no estado do Wi-Fi Direct
        if(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)){
            int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE,-1);
            if(state == WifiP2pManager.WIFI_P2P_STATE_ENABLED){
                // Wi-Fi Direct esta habilitado
            }else{
                // Wi-Fi Direct nao esta habilitado
            }
        }
        ...
    }

Código adaptado do site.

Perceba que o sistema do dispositivo que recebeu a ação encapsula todas as informações dentro do Intent para que elas possam ser recuperadas nesse método.

Agora, sabemos se o dispositivo suporta ou não o Wi-Fi Direct. Contudo, não fizemos com que o WiFiDirectBroadcastReceiver fosse inicializado para que ele pudesse nos dar essa informação. Esta etapa é feita na classe MeuWiFiDirect. Na seção 1, falamos que para criar um WiFiDirectBroadcastReceiver é necessário criar um construtor, e que esse construtor recebe algumas informações como parâmetros. Essas informações são uma instância da classe WifiP2pMannager, um canal de comunicaçãoWifiP2pMannager.Channel e a atividade que o Broadcast Receiver estará ligado. Estas informações são criadas na classe MeuWiFiDirect e passadas ao construtor do WiFiDirectBroadcastReceiver.

Esta etapa de criação é feita no método onCreate() do MeuWiFiDirect:

WifiP2pManager mManager;
Channel mChannel;
BroadcastReceiver mReceiver;
...
@Override
protected void onCreate(Bundle savedInstanceState){
    ...
    mManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
    mChannel = mManager.initialize(this, getMainLooper(),null);
    mReceiver = new WiFiDirectBroadcastReceiver(mManager, mChannel,this);
    ...
}

Código adaptado do site.

Dessa forma, nossa aplicação já possui uma instância do WiFiDirectBroadcastReceiver. Porém essa instância não está registrada para começar a ouvir as ações e nem sabe quais ações que deve responder, portanto devemos fazer mais essa etapa.

Para informar a quais ações responder, devemos criar um IntentFilter com as ações desejadas:

IntentFilter mIntentFilter;
...
@Override
protected void onCreate(Bundle savedInstanceState){
    ...
    mIntentFilter = new IntentFilter();
    mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
    mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
    mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
    mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
    ...
}

Código adaptado do site.

Perceba que esta ação também ocorre dentro do método onCreate() do MeuWiFiDirect. Com isso, possuímos todas as informações necessárias par registar nosso WiFiDirectBroadcastReceiver no método onResume() do MeuWiFiDirect:

/* registre o broadcast receiver com os valores de intent desejados*/
@Override
protected void onResume(){
    super.onResume();
    registerReceiver(mReceiver, mIntentFilter);
}
/* retire o registro do broadcast receiver */
@Override
protected void onPause(){
    super.onPause();
    unregisterReceiver(mReceiver);
}

Código adaptado do site.

Com todos esses passos, possuímos um BroadcastReceiver registrado que receberá as informações pertinentes ao Wi-Fi Direct e podemos fazer nossa aplicação utilizar suas funcionalidades.

3 – Códigos

MeuWiFiDirect:

WifiP2pManager mManager;
Channel mChannel;
BroadcastReceiver mReceiver;
...
@Override
protected void onCreate(Bundle savedInstanceState){
    ...
    mManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
    mChannel = mManager.initialize(this, getMainLooper(),null);
    mReceiver = new WiFiDirectBroadcastReceiver(mManager, mChannel,this);

    mIntentFilter = new IntentFilter();
    mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
    mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
    mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
    mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
    ...
}

@Override
protected void onResume(){
super.onResume();
registerReceiver(mReceiver, mIntentFilter)
}

@Override
protected void onPause(){
super.onPause();
unregisterReceiver(mReceiver)
}

WiFiDirectBroadcastReceiver:

/**
 * Um BroadcastReceiver que notifica eventos P2P wifi importantes.
 */
public class WiFiDirectBroadcastReceiver extends BroadcastReceiver{

    private WifiP2pManager mManager;
    private Channel mChannel;
    private MyWiFiActivity mActivity;

    public WiFiDirectBroadcastReceiver(WifiP2pManager manager, Channel channel, MyWifiActivity activity){
        super();
        this.mManager = manager;
        this.mChannel = channel;
        this.mActivity = activity;
    }

    @Override
    public void onReceive(Context context, Intent intent){
        String action = intent.getAction();

        //percebeu a mudanca no estado do Wi-Fi Direct
        if(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)){
            int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE,-1);
            if(state == WifiP2pManager.WIFI_P2P_STATE_ENABLED){
                // Wi-Fi Direct esta habilitado
            }else{
                // Wi-Fi Direct nao esta habilitado
            }
        }else if(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)){
            // Chamar WifiP2pManager.requestPeers() para obter uma lista dos dispositivos disponiveis
        }else if(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)){
            // Responder a novas conexoes ou desconexoes
        }else if(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)){
           // Responder a mudancas de estado deste dispositivo
        }
    }
}

4 – Conclusão

Este post mostrou como criar o início do funcionamento do Wi-Fi Direct no Android. Nele vimos que, principalmente, precisamos de uma classe que fique responsável por perceber todas as funções, tais como procurar e reconhecer dispositivos, discutidas nos outros posts. Além disso, vimos como fazer com que a aplicação obtêm autorização para acessar o hardware do dispositivo para utilizar as funções do Wi-Fi Direct.

Por fim, unimos uma atividade com a classe que monitora os arredores para que a aplicação possa realizar suas atividades com Wi-Fi Direct.

Summary
Wi-Fi Direct no Android - Parte 1
Article Name
Wi-Fi Direct no Android - Parte 1
Description
Programando WiFi Direct no Android- Android Programming: WiFi Direct
Author
Publisher Name
iMobilis
Publisher Logo

Deixe um comentário

O seu endereço de e-mail não será publicado.