Поиск по этому блогу

воскресенье, 4 мая 2014 г.

Как подключиться к конкретной WiFi точке в Android

Всем снова привет! Давно я ничего не писал и вот решил сегодня выдать новый перл (: 

Недавно мне нужно было сделать проект в котором приложение подключается к конкретной сети и делает кое какие манипуляции, долго колупался и пытался подключиться по SSID к своей домашней сети но так и не вышло, на stack overflow куча ответов где говорят что это возможно но у меня не вышло, и тогда я попробовал подключиться к MAC своего компьютера и все получилось, девайс после нажатия на кнопку подключался к нужной сети. 

Так вот собственно как я это сделал сегодня и хочу рассказать.

Так сложилось что что-бы подключиться к сети нужно сперва ее просканировать классом ScanResult который выведет вам полный список доступных Wifi точек. Потом нужно найти нужную сеть и достать ее BSSID — это по простому MAC адрес компьютеру к которому хотим подключиться. Сперва я приведу пример как это сделать без пароля, а потом просто добавлю строчку которая добавляет пароль к конкретной сети.

У нас будет всего лишь один класс который будет выполнять работу, еще будет подкласс рессивер который будет коннектиться к нужной нам сети. 

Для начала давайте создадим разметку и добавим препишены для приложения в манифесте. И так сначала разметка. У нас будет всего два объекта, EditText и Bitton по нажатию на которую будет производиться подключение к имени нужной сети которое вы введете в EditText.

activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

     <EditText
            android:id="@+id/editText1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_alignParentRight="true"
            android:layout_alignParentTop="true"
            android:ems="10"
            android:hint="Enter wifi point name" >

        <requestFocus />
    </EditText>

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/editText1"
        android:layout_below="@+id/editText1"
        android:text="Connect" />
</RelativeLayout>


А так же нам нужно добавить кучу премишенов для того что бы можно было работать с вайфай, интернетом, регулировать состояние вайфая, доступ к вайфаю и так далее.

Вот так будет выглядеть Манифест после редактирования:

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.connecttowifi"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="19" />

     <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
        <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <uses-feature android:name="android.hardware.wifi" />
    
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.connecttowifi.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>


С настройками для работы мы закончили, так же скорей всего если у вас обновленный Eclipse вам понадобиться appcompat_v7 который Eclipse создает теперь после каждого создания нового проекта, это жутко бесить но без него работать наперекор всему отказывается, проект выдает кучи ошибок и все. Так что я его прикреплю к проекту как дополнительную библиотеку, и на гите он будет лежать вместе с проектом.

Теперь можем смело вставлять в MainActivity наш код который подключается к конкретной точке.

MainActivity.java
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
public class MainActivity extends ActionBarActivity {

        private WifiManager wifiManager;
        private WifiConfiguration wifiConfig;
        private WifiReceiver wifiResiver;
        private boolean wifiEnabled;
        private EditText url;
        private Button conect;
        private boolean isClick = false;
        
        public void init() {
                
                conect = (Button) findViewById(R.id.button1);
                url = (EditText) findViewById(R.id.editText1);
                
                // создаем новый объект для подключения к конкретной точке
                wifiConfig = new WifiConfiguration();
                // сканнер вайфая который нам будет помогать подключаться к нужной точке
                wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
                //узнаем включен вайфай  или нет
                wifiEnabled = wifiManager.isWifiEnabled();

                //наш рессивер который будем подключать нас столько сколько нам понадобиться, пока не будет подключена нужная точка
                wifiResiver = new WifiReceiver();
        }
        
        @Override
        protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_main);
                
                // метод который инициализирует все что нам нужно
                init();
                
                conect.setOnClickListener(new OnClickListener() {
                        
                        @Override
                        public void onClick(View v) {

                                //если файвай включен то ничего не делаем иначе включаем его
                                if(!wifiEnabled) {
                                        wifiManager.setWifiEnabled(true);
                                }
                                
                                //запускаем сканнер вайфая, и подключаемся если подкходящая нам есть есть
                                scheduleSendLocation();
                                //запускаем рессивер
                                isClick = true;
                        }
                });             
        }
        
        /*
         * Подключаемся к wifi указаному в edit text
         * */
        public void scheduleSendLocation() {
            
                registerReceiver(wifiResiver, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
            wifiManager.startScan();
        }
        
        protected void onPause() {
                
                //если приложение уходит в фон или например выключаем его вообще, то и рессивер тормазим и выключаем.
        unregisterReceiver(wifiResiver);
        super.onPause();
    }
        
        public void onResume() {
                
                //если кликнули то запускаем рессивер, если же isClick = false то ждем пока кнопка будет нажата
                if(isClick)
                        registerReceiver(wifiResiver, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
                super.onResume();
        }
        
        /*
         * Рессивер который каждый раз запускает сканнер сети 
         * */
        public class WifiReceiver extends BroadcastReceiver {
                
                @Override
        public void onReceive(Context c, Intent intent) {
                
                        //сканируем вайфай точки и узнаем какие доступны
           List<ScanResult> results = wifiManager.getScanResults();
           //проходимся по всем возможным точкам
           for (final ScanResult ap : results) {
                   //ищем нужную нам точку с помощью ифа, будет находить то которую вы ввели
               if(ap.SSID.toString().trim().equals(url.getText().toString().trim())) {
                   // дальше получаем ее MAC и передаем для коннекрта, MAC получаем из результата
                   //здесь мы уже начинаем коннектиться
                                        wifiConfig.BSSID = ap.BSSID;
                                wifiConfig.priority = 1;
                                wifiConfig.allowedKeyManagement.set(KeyMgmt.NONE);
                                wifiConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
                                wifiConfig.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
                                wifiConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
                                wifiConfig.status = WifiConfiguration.Status.ENABLED;

                                //получаем ID сети и пытаемся к ней подключиться, 
                                int netId = wifiManager.addNetwork(wifiConfig);
                                wifiManager.saveConfiguration();
                                //если вайфай выключен то включаем его
                                wifiManager.enableNetwork(netId, true);
                                //если же он включен но подключен к другой сети то перегружаем вайфай.
                                wifiManager.reconnect();                
                                break;
               }
           }
        }
        }
}


Думаю комментарии тут совершенно лишними не будут. Что мы тут делаем? В первую очередь мы создали кучу переменных для работы с Wifi, такие как конфигурации и менеджеры, они нам дают возможность полностью командовать нашим Wifi в телефоне \ планшете. 

Далее мы все это инициализируем в методе init(). После чего в методе onCreate() мы сперва вызываем этот init(), а после обрабатываем нажатие на кнопку и после нажатия вызываем метод и делаем нашу булевую переменную активной которая запускает рессивер.

Все получается запутанно, а это только начало (: Думаю проследить в коде будет проще, там такие же комментарии.

Дальше переходим в сам рессивер. В нем мы сканируем все сети WiFi и получаем кокретные их имена
List<ScanResult> results = wifiManager.getScanResults();
           //проходимся по всем возможным точкам
           for (final ScanResult ap : results) {
                //здесь можно вывести Toast с именами, примерно вот так
                Toast.makeText(MainActivity.this, ap.SSID, Toast.LENGTH_LONG).show();
                // тут иф
           }

дальше при помощи ифа мы вытаскиваем нужное нам имя,
if(ap.SSID.toString().trim().equals(url.getText().toString().trim())) { ... }

и подключаемся к той сети имя которой похоже на введенное из EditText
wifiConfig.BSSID = ap.BSSID;

Остальные параметры важны но самое важное в принципе я описал.

Все это мы делаем в рессивере для того что бы выполнялось оно в отдельном потоке и не захламляло UI поток, да и без отдельного потока приложение просто падает с NullPointerExeption. Так что вот такие пироги.

В итоге вы должны после нажатия кнопку подключаться к нужной сети.

Исходники:
GitHub || Dropbox