2017/01/06

[Android] WifiManager for Android M or later

Android M之後,Google做了很多權限的管理,加上寫Android的工具也從Eclipse變成Android Studio之後,決定來更新WiFiManager的使用。

關於WiFiManager地說明可以參考 [Android] 用WifiManager來get wifi的SSID, power level和所使用的channel基本資訊

本篇主要就Android M以及使用Android Studio為基礎,利用原本的範例當修改。



程式碼下載:GitHub

與原本差異如下:
1. 增加MVC的模型來撰寫程式碼
2. 增加permissions的控制處理

大致上與原本差不多,但主要要增加ACCESS_COARSE_LOCATION這個permission,否則Android M會拿到空的網路掃描結果(我是用Android 7.1.1當測試機)。
<uses-permission-sdk-23 android:name="android.permission.ACCESS_COARSE_LOCATION"/>

其他程式碼的部分
MainActivity.java
package com.cybernut.wifimanager.view;

import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;

import com.cybernut.wifimanager.common.RetainedFragmentManager;
import com.cybernut.wifimanager.controller.MainActivityOps;
import com.cybernut.wifimanager.R;

public class MainActivity extends AppCompatActivity {

    /**
     * Debugging tag used by the Android logger.
     */
    private static final String TAG =
            MainActivity.class.getSimpleName();

    /**
     * Used to retain the MainActivityOps state between runtime configuration
     * changes.
     */
    protected final RetainedFragmentManager mRetainedFragmentManager =
            new RetainedFragmentManager(this.getFragmentManager(),
                    TAG);
    private static final String MAIN_OPS_STATE = "MAIN_OPS_STATE";

    private MainActivityOps mMainActivityOps;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        handleConfigurationChanges();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    /**
     * Callback received when a permissions request has been completed.
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {
        mMainActivityOps.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }

    /**
     * Handle hardware reconfigurations, such as rotating the display.
     */
    protected void handleConfigurationChanges() {
        // If this method returns true then this is the first time the
        // Activity has been created.
        if (mRetainedFragmentManager.firstTimeIn()) {
            Log.d(TAG,
                    "First time onCreate() call");

            // Create the MainActivityOps object one time.
            mMainActivityOps = new MainActivityOps(this);

            // Store the LoginOps into the RetainedFragmentManager.
            mRetainedFragmentManager.put(MAIN_OPS_STATE,
                    mMainActivityOps);

        } else {
            // The RetainedFragmentManager was previously initialized,
            // which means that a runtime configuration change
            // occurred.

            Log.d(TAG,
                    "Second or subsequent onCreate() call");

            // Obtain the LoginOps object from the
            // RetainedFragmentManager.
            mMainActivityOps =
                    mRetainedFragmentManager.get(MAIN_OPS_STATE);

            // This check shouldn't be necessary under normal
            // circumstances, but it's better to lose state than to
            // crash!
            if (mMainActivityOps == null) {
                // Create the MainActivityOps object one time.
                mMainActivityOps = new MainActivityOps(this);

                // Store the LoginOps into the RetainedFragmentManager.
                mRetainedFragmentManager.put(MAIN_OPS_STATE,
                        mMainActivityOps);

            } else
                // Inform it that the runtime configuration change has
                // completed.
                mMainActivityOps.onConfigurationChange(this);
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mMainActivityOps.unregisterReceiverAndDestroy();
    }
}

MainActivityOps.java
package com.cybernut.wifimanager.controller;

import android.annotation.TargetApi;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.TextView;

import com.cybernut.wifimanager.R;
import com.cybernut.wifimanager.model.WiFiList;
import com.cybernut.wifimanager.view.MainActivity;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;

import static android.Manifest.permission.ACCESS_COARSE_LOCATION;

public class MainActivityOps {
    /**
     * Debugging tag used by the Android logger.
     */
    private static final String TAG =
            MainActivityOps.class.getSimpleName();

    private WifiManager wifiManager;
    private int size = 0;
    private List results;
    private ArrayList> mArrayList = new ArrayList<>();
    private SimpleAdapter mAdapter;
    private BroadcastReceiver mBroadcastReceiver;

    /**
     * Used to enable garbage collection.
     */
    private WeakReference mMainActivity;
    private WeakReference mFab;
    private WeakReference mTextView;

    /**
     * Id to identity ACCESS_COARSE_LOCATION permission request.
     */
    private static final int REQUEST_ACCESS_LOCATION = 101;

    public MainActivityOps(MainActivity mainActivity) {
        // Initialize the WeakReference.
        mMainActivity = new WeakReference<>(mainActivity);

        // Finish the initialization steps.
        initializeViewFields();
        initializeNonViewFields();
    }

    private void initializeViewFields() {
        Log.d(TAG, "initializeViewFields");
        // Get references to the UI components.
        mMainActivity.get().setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) mMainActivity.get().findViewById(R.id.toolbar);
        mMainActivity.get().setSupportActionBar(toolbar);
        mTextView = new WeakReference<>
                ((TextView) mMainActivity.get().findViewById(R.id.textView2));
        WeakReference mListView = new WeakReference<>
                ((ListView) mMainActivity.get().findViewById(R.id.listView1));

        populateAutoComplete();

        wifiManager = (WifiManager) mMainActivity.get().getSystemService(Context.WIFI_SERVICE);
        mAdapter = new SimpleAdapter(mMainActivity.get(), mArrayList, R.layout.list_wifi,
                new String[] {"ssid", "power", "freq"}, new int[] {R.id.ssid, R.id.power, R.id.freq});
        mListView.get().setAdapter(mAdapter);

        mFab = new WeakReference<>
                ((FloatingActionButton) mMainActivity.get().findViewById(R.id.fab));
        mFab.get().setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                wifiManager.startScan();

                Snackbar.make(view, "Scanning...", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });

        if(!wifiManager.isWifiEnabled())
        {
            AlertDialog.Builder dialog = new AlertDialog.Builder(mMainActivity.get());
            dialog.setTitle("Remind");
            dialog.setMessage("Your Wi-Fi is disabled, enable it?");
            dialog.setIcon(android.R.drawable.ic_dialog_info);
            dialog.setCancelable(false);
            dialog.setPositiveButton("OK", new DialogInterface.OnClickListener() {

                @Override
                public void onClick(DialogInterface dialog, int which) {
                    // TODO Auto-generated method stub
                    wifiManager.setWifiEnabled(true);
                    Snackbar.make(mFab.get(),
                            "WiFi is disabled... making it enabled", Snackbar.LENGTH_LONG)
                            .setAction("Action", null).show();

                }
            });
            dialog.show();
        }

        mBroadcastReceiver = new BroadcastReceiver() {

            @Override
            public void onReceive(Context context, Intent intent) {
                // TODO Auto-generated method stub
                results = wifiManager.getScanResults();
                size = results.size();
                Log.d(TAG, "Acquire wifi "+size);
                mArrayList.clear();

                for(int i=0; i item = new HashMap<>();
                    item.put("ssid", results.get(i).SSID);
                    item.put("power", results.get(i).level+" dBm");
                    String wifichn = WiFiList.WIFI_CHANNELS.containsKey(
                            Integer.toString(results.get(i).frequency))?
                            WiFiList.WIFI_CHANNELS.
                                    get(Integer.toString(results.get(i).frequency)):"5G";
                    item.put("freq", wifichn);
                    mArrayList.add(item);
                }

                // Sort by power
                Collections.sort(mArrayList, new Comparator>() {

                    @Override
                    public int compare(HashMap lhs,
                                       HashMap rhs) {
                        // TODO Auto-generated method stub
                        return (lhs.get("power")).compareTo(rhs.get("power"));
                    }
                });

                if(size > 0) {
                    mTextView.get().setText(mArrayList.get(0).get("ssid"));
                }
                mAdapter.notifyDataSetChanged();
            }
        };
        mMainActivity.get().registerReceiver(mBroadcastReceiver,
                new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));

        if(size > 0) {
            mTextView.get().setText(mArrayList.get(0).get("ssid"));
        }

    }

    /**
     * (Re)initialize the non-view fields (e.g.,
     * GenericServiceConnection objects).
     */
    private void initializeNonViewFields() {
        Log.d(TAG, "initializeNonViewFields");

    }

    private void populateAutoComplete() {
        if (!mayRequestLocation()) {
            return;
        }

    }

    private boolean mayRequestLocation() {
        Log.d(TAG, "mayRequestLocation");
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            return true;
        }
        Log.d(TAG, "newer than M");
        if (mMainActivity.get().checkSelfPermission(ACCESS_COARSE_LOCATION)
                == PackageManager.PERMISSION_GRANTED) {
            return true;
        }
        Log.d(TAG, "no permission");

        if (mMainActivity.get().
                shouldShowRequestPermissionRationale(ACCESS_COARSE_LOCATION)) {
            Log.d(TAG, "request permission");
            Snackbar.make(mFab.get(),
                    R.string.permission_rationale, Snackbar.LENGTH_INDEFINITE)
                    .setAction(android.R.string.ok, new View.OnClickListener() {
                        @Override
                        @TargetApi(Build.VERSION_CODES.M)
                        public void onClick(View v) {
                            mMainActivity.get().
                                    requestPermissions(new String[]{
                                                    ACCESS_COARSE_LOCATION},
                                            REQUEST_ACCESS_LOCATION);
                        }
                    });
        } else {
            Log.d(TAG, "Permission OK");
            mMainActivity.get().
                    requestPermissions(new String[]{
                                    ACCESS_COARSE_LOCATION},
                            REQUEST_ACCESS_LOCATION);
        }
        return false;
    }

    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {
        Log.d(TAG, "onRequestPermissionsResult");
        if (requestCode == REQUEST_ACCESS_LOCATION) {
            if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                populateAutoComplete();
            }
        }
    }

    /**
     * Called after a runtime configuration change occurs to finish
     * the initialization steps.
     */
    public void onConfigurationChange(MainActivity mainActivity) {
        Log.d(TAG,
                "onConfigurationChange() called");
        // Reset the mActivity WeakReference.
        mMainActivity = new WeakReference<>(mainActivity);
        // (Re)initialize all the View fields.
        initializeViewFields();
    }

    public void unregisterReceiverAndDestroy() {
        Log.d(TAG, "go to unregisterReceiverAndDestroy");
        if(mBroadcastReceiver!=null) {
            mMainActivity.get().unregisterReceiver(mBroadcastReceiver);
            mBroadcastReceiver = null;
        }
    }

}

Layout設定
activity_main.xml
&lt?xml version="1.0" encoding="utf-8"?&gt
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context="com.cybernut.wifimanager.view.MainActivity"&gt

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay"&gt

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" /&gt

    </android.support.design.widget.AppBarLayout&gt

    <include layout="@layout/content_main" /&gt

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="@dimen/fab_margin"
        app:srcCompat="@android:drawable/ic_dialog_info" /&gt

</android.support.design.widget.CoordinatorLayout&gt

content_main.xml
&lt?xml version="1.0" encoding="utf-8"?&gt
&ltRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/content_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="com.cybernut.wifimanager.view.MainActivity"
    tools:showIn="@layout/activity_main"&gt

    &ltListView
        android:id="@+id/listView1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/textView1"
        android:layout_marginTop="46dp"
        android:background="#ff8" &gt
    &lt/ListView>

    &ltTextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:text="@string/wifi_ssid" /&gt

    &ltTextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/wifi_analysis"
        android:layout_below="@+id/textView1"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true" /&gt
&lt/RelativeLayout&gt

list_wifi.xml
&lt?xml version="1.0" encoding="utf-8"?&gt
&ltRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent" android:layout_height="match_parent"&gt

    &ltTextView
        android:id="@+id/ssid"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="16dp"
        android:layout_marginTop="16dp"
        android:text="TextView" /&gt

    &ltTextView
        android:id="@+id/power"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/ssid"
        android:layout_below="@+id/ssid"
        android:text="TextView" /&gt

    &ltTextView
        android:id="@+id/freq"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@+id/power"
        android:layout_alignBottom="@+id/power"
        android:layout_centerHorizontal="true"
        android:text="TextView" /&gt

&lt/RelativeLayout&gt

執行結果
Android 7.1.1 API 25, Nexus 5X




Android 4.1.1 API 16, HTC J


利用這樣就可以簡單取得WiFi的資訊。

沒有留言:

張貼留言