首页 > 代码库 > 二维码的妙用:通过Zxing实现wifi账号密码分享功能

二维码的妙用:通过Zxing实现wifi账号密码分享功能

        二维码是搭载信息的一种载体,通过二维码可以传递名片、网址、商品信息等,本文讲到二维码的另外一种妙用:通过二维码实现wifi账号和密码分享。

        关于二维码的基础知识,请访问:二维码的生成细节和原理

        试想这样一种场景:一伙人去同一餐馆或者在一起开会,有的人之前去过且已经使用过那个场景的wifi账号,所以一去手机就能够直接连上那里的wifi,但有的同学是第一次去,也有连接wifi的需求,这时我们一般是通过别人告知wifi账号和密码然后手动登陆,但问题是有时候已经连上wifi的人也不记得wifi的密码了,本文结合这个需求场景,做了一个wifi账号和密码分享的小demo,前提是双方都需要安装这个app,并且分享wifi的一方需要有root权限(目前是,也可以将分享wifi的客户端作为热点,然后将热点分享给其他人,这个可以自行研究),这样减少了手动输入密码的麻烦。


本文主要介绍通过二维码实现wifi分享的两个核心功能:

1、获取本机已经连接上的wifi账号、密码和加密方式;

2、给定指定内容生成二维码,扫描二维码解析出其中搭载的信息。


1、获取本机已经连接上的wifi账号、密码和加密方式:

所有的事情都可以通过下面这个类搞定:

public class WifiAdmin {
    public WifiAdmin(Context context) {   
        mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);   
        mWifiInfo = mWifiManager.getConnectionInfo();   
    }   
   
    // 打开WIFI    
    public void openWifi() {   
        if (!mWifiManager.isWifiEnabled()) {   
            mWifiManager.setWifiEnabled(true);   
        }   
    }   
   
    // 关闭WIFI    
    public void closeWifi() {   
        if (mWifiManager.isWifiEnabled()) {   
            mWifiManager.setWifiEnabled(false);   
        }   
    }   
   
    // 检查当前WIFI状态    
    public int checkState() {   
        return mWifiManager.getWifiState();   
    }   
   
    // 锁定WifiLock    
    public void acquireWifiLock() {   
        mWifiLock.acquire();   
    }   
   
    // 解锁WifiLock    
    public void releaseWifiLock() {   
        // 判断时候锁定    
        if (mWifiLock.isHeld()) {   
            mWifiLock.acquire();   
        }   
    }   
   
    // 创建一个WifiLock    
    public void creatWifiLock() {   
        mWifiLock = mWifiManager.createWifiLock("Test");   
    }   
   
    // 得到配置好的网络    
    public List<WifiConfiguration> getConfiguration() {   
        return mWifiConfiguration;   
    }   
   
    // 指定配置好的网络进行连接    
    public void connectConfiguration(int index) {   
        // 索引大于配置好的网络索引返回    
        if (index > mWifiConfiguration.size()) {   
            return;   
        }   
        // 连接配置好的指定ID的网络    
        mWifiManager.enableNetwork(mWifiConfiguration.get(index).networkId,   
                true);   
    }   
   
    public void startScan() {   
        mWifiManager.startScan();   
        // 得到扫描结果    
        mWifiList = mWifiManager.getScanResults();   
        // 得到配置好的网络连接    
        mWifiConfiguration = mWifiManager.getConfiguredNetworks();   
    }   
   
    // 得到网络列表    
    public List<ScanResult> getWifiList() {   
        return mWifiList;   
    }   
   
    // 查看扫描结果    
    @SuppressLint("UseValueOf")
	public StringBuilder lookUpScan() {   
    	StringBuilder stringBuilder = new StringBuilder();   
        for (int i = 0; i < mWifiList.size(); i++) {   
        	stringBuilder.append("Index_" + new Integer(i + 1).toString() + ":");   
            // 将ScanResult信息转换成一个字符串包 其中把包括:BSSID、SSID、capabilities、frequency、level    
            stringBuilder.append((mWifiList.get(i)).toString());   
            stringBuilder.append("/n");   
        }   
        return stringBuilder;   
    }  
   
    // 得到MAC地址    
    public String getMacAddress() {   
        return (mWifiInfo == null) ? "NULL" : mWifiInfo.getMacAddress();   
    }   
   
    // 得到接入点的BSSID    
    public String getBSSID() {   
        return (mWifiInfo == null) ? "NULL" : mWifiInfo.getBSSID();   
    }   
   
    // 得到IP地址    
    public int getIPAddress() {   
        return (mWifiInfo == null) ? 0 : mWifiInfo.getIpAddress();   
    }   
   
    // 得到连接的ID    
    public int getNetworkId() {   
        return (mWifiInfo == null) ? 0 : mWifiInfo.getNetworkId();   
    }   
    
    // 得到WifiInfo的所有信息包    
    public WifiInfo getWifiInfo() {   
        return mWifiInfo;   
    }
   
    // 添加一个网络并连接    
    public void addNetwork(WifiConfiguration wcg) {   
     int wcgID = mWifiManager.addNetwork(wcg);   
     boolean b =  mWifiManager.enableNetwork(wcgID, true);   
     System.out.println("a--" + wcgID);  
     System.out.println("b--" + b);  
    }   
   
    // 断开指定ID的网络    
    public void disconnectWifi(int netId) {   
        mWifiManager.disableNetwork(netId);   
        mWifiManager.disconnect();   
    }   
   
	// 然后是一个实际应用方法,只验证过没有密码的情况:
	// 分为三种情况:1没有密码2用wep加密3用wpa加密
	public WifiConfiguration CreateWifiInfo(String SSID, String password, int type) {
		WifiConfiguration config = new WifiConfiguration();
		config.allowedAuthAlgorithms.clear();
		config.allowedGroupCiphers.clear();
		config.allowedKeyManagement.clear();
		config.allowedPairwiseCiphers.clear();
		config.allowedProtocols.clear();
		config.SSID = "\"" + SSID + "\"";

		WifiConfiguration tempConfig = this.IsExsits(SSID);
		if (tempConfig != null) {
			mWifiManager.removeNetwork(tempConfig.networkId);
		}

		if (type == 1) {
			// WIFICIPHER_NOPASS
			config.wepKeys[0] = "";
			config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
			config.wepTxKeyIndex = 0;
		}
		if (type == 2) {
			// WIFICIPHER_WEP
			config.hiddenSSID = true;
			config.wepKeys[0] = "\"" + password + "\"";
			config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
			config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
			config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
			config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);
			config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104);
			config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
			config.wepTxKeyIndex = 0;
		}
		if (type == 3) {
			// WIFICIPHER_WPA
			config.preSharedKey = "\"" + password + "\"";
			config.hiddenSSID = true;
			config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
			config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
			config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
			config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
			// config.allowedProtocols.set(WifiConfiguration.Protocol.WPA);
			config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
			config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
			config.status = WifiConfiguration.Status.ENABLED;
		}
		
		return config;
	} 
      
	private WifiConfiguration IsExsits(String SSID) {
		List<WifiConfiguration> existingConfigs = mWifiManager.getConfiguredNetworks();
		if (null == existingConfigs) {
			return null;
		}
		for (WifiConfiguration existingConfig : existingConfigs) {
			System.out.println("existingConfig == " + existingConfig.toString());
			if (existingConfig.SSID.equals("\"" + SSID + "\"")) {
				System.out.println("existingConfig.SSID == " + existingConfig.SSID + " SSID == " + SSID);
				return existingConfig;
			}
		}
		
		return null;
	}
    
    public List<UserWifiInfo> getUserWifiInfoList( ) throws Exception {  
        List<UserWifiInfo> wifiInfos=new ArrayList<UserWifiInfo>();  
      
        Process process = null;  
        DataOutputStream dataOutputStream = null;  
        DataInputStream dataInputStream = null;  
        StringBuffer wifiConf = new StringBuffer();  
        try {  
            process = Runtime.getRuntime().exec("su");  
            dataOutputStream = new DataOutputStream(process.getOutputStream());  
            dataInputStream = new DataInputStream(process.getInputStream());  
            dataOutputStream.writeBytes("cat /data/misc/wifi/*.conf\n");  
            dataOutputStream.writeBytes("exit\n");  
            dataOutputStream.flush();  
            InputStreamReader inputStreamReader = new InputStreamReader(  
                    dataInputStream, "UTF-8");  
            BufferedReader bufferedReader = new BufferedReader(  
                    inputStreamReader);  
            String line = null;  
            while ((line = bufferedReader.readLine()) != null) {  
                wifiConf.append(line);  
            }  
            bufferedReader.close();  
            inputStreamReader.close();  
            process.waitFor();  
        } catch (Exception e) {  
            throw e;  
        } finally {  
            try {  
                if (dataOutputStream != null) {  
                    dataOutputStream.close();  
                }  
                if (dataInputStream != null) {  
                    dataInputStream.close();  
                }  
                process.destroy();  
            } catch (Exception e) {  
                throw e;  
            }  
        }     
      
          
        Pattern network = Pattern.compile("network=\\{([^\\}]+)\\}", Pattern.DOTALL);  
        Matcher networkMatcher = network.matcher(wifiConf.toString() );  
        while (networkMatcher.find() ) {  
            String networkBlock = networkMatcher.group();  
            Pattern ssid = Pattern.compile("ssid=\"([^\"]+)\"");  
            Matcher ssidMatcher = ssid.matcher(networkBlock);  
              
            if (ssidMatcher.find() ) {     
                UserWifiInfo wifiInfo=new UserWifiInfo();  
                wifiInfo.Ssid=ssidMatcher.group(1);  
                Pattern psk = Pattern.compile("psk=\"([^\"]+)\"");  
                Matcher pskMatcher = psk.matcher(networkBlock);  
                if (pskMatcher.find() ) {  
                    wifiInfo.Password=pskMatcher.group(1);  
                } else {  
                    wifiInfo.Password="无密码";  
                }          
                wifiInfos.add(wifiInfo);  
            }  
              
        }  
  
        return wifiInfos;  
    }
    
    private WifiManager mWifiManager = null;   
    private WifiInfo mWifiInfo = null;   
    private List<ScanResult> mWifiList = null;   
    private List<WifiConfiguration> mWifiConfiguration = null;   
    private WifiLock mWifiLock = null; 
}

使用到wifi的各种功能,记得增加相应地权限:

<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" >
    </uses-permission>
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" >
    </uses-permission>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" >
    </uses-permission>
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" >
    </uses-permission>


2、给定指定内容生成二维码,扫描二维码解析出其中搭载的信息:

本文使用到了开源二维码项目zxing,关于zxing的更多介绍请参看:二维码、条形码扫描——使用Google ZXing

(1)将指定内容生成二维码的方法如下:

@SuppressWarnings("deprecation")
	private void generate( String result ){
		if( TextUtils.isEmpty( result ) ){
			return;
		}
		
		try{
            //判断result合法性
            if (result == null || "".equals(result) || result.length() < 1){
                return;
            }
            Hashtable<EncodeHintType, String> hints = new Hashtable<EncodeHintType, String>();
            hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
            //图像数据转换,使用了矩阵转换
            BitMatrix bitMatrix = new QRCodeWriter().encode(result, BarcodeFormat.QR_CODE, QR_WIDTH, QR_HEIGHT, hints);
            int[] pixels = new int[QR_WIDTH * QR_HEIGHT];
            //下面这里按照二维码的算法,逐个生成二维码的图片,
            //两个for循环是图片横列扫描的结果
            for (int y = 0; y < QR_HEIGHT; y++){
                for (int x = 0; x < QR_WIDTH; x++){
                    if (bitMatrix.get(x, y)){
                        pixels[y * QR_WIDTH + x] = 0xff000000;
                    }else{
                        pixels[y * QR_WIDTH + x] = 0xffffffff;
                    }
                }
            }
            //生成二维码图片的格式,使用ARGB_8888
            Bitmap bitmap = Bitmap.createBitmap(QR_WIDTH, QR_HEIGHT, Bitmap.Config.ARGB_8888);
            bitmap.setPixels(pixels, 0, QR_WIDTH, 0, 0, QR_WIDTH, QR_HEIGHT);
            //显示到一个ImageView上面
            mResultImg.setBackground( new BitmapDrawable( bitmap ) );
            
            decodeBitmap( bitmap );
        }catch (WriterException e) {
            e.printStackTrace();
        }
	}
	
	private String decodeBitmap( Bitmap bitmap ){
		MultiFormatReader multiFormatReader = new MultiFormatReader();
		// 解码的参数
		Hashtable<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>(2);
		// 可以解析的编码类型
		Vector<BarcodeFormat> decodeFormats = new Vector<BarcodeFormat>();
		if (decodeFormats == null || decodeFormats.isEmpty()) {
			decodeFormats = new Vector<BarcodeFormat>();

			// 这里设置可扫描的类型,我这里选择了都支持
			decodeFormats.addAll(DecodeFormatManager.ONE_D_FORMATS);
			decodeFormats.addAll(DecodeFormatManager.QR_CODE_FORMATS);
			decodeFormats.addAll(DecodeFormatManager.DATA_MATRIX_FORMATS);
		}
		hints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats);

		// 设置继续的字符编码格式为UTF8
		// hints.put(DecodeHintType.CHARACTER_SET, "UTF8");

		// 设置解析配置参数
		multiFormatReader.setHints(hints);

		// 开始对图像资源解码
		try {
			Result rawResult = multiFormatReader.decodeWithState(new BinaryBitmap(new HybridBinarizer( new com.uperone.zxing.decoding.BitmapLuminanceSource(bitmap))));
			mDecodeReslutTxt.setText(new StringBuilder().append("包括内容:")
					.append(rawResult.getText()).append("\n编码方式:")
					.append(rawResult.getBarcodeFormat()).toString());
		} catch (NotFoundException e) {
			e.printStackTrace();
		}
	    
		return null;
	}


(2)解析出二维码中搭载的内容方法如下:

/**
   * Decode the data within the viewfinder rectangle, and time how long it took. For efficiency,
   * reuse the same reader objects from one decode to the next.
   *
   * @param data   The YUV preview frame.
   * @param width  The width of the preview frame.
   * @param height The height of the preview frame.
   */
  private void decode(byte[] data, int width, int height) {
    long start = System.currentTimeMillis();
    Result rawResult = null;
    PlanarYUVLuminanceSource source = CameraManager.get().buildLuminanceSource(data, width, height);
    BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
    try {
      rawResult = multiFormatReader.decodeWithState(bitmap);
    } catch (ReaderException re) {
      // continue
    } finally {
      multiFormatReader.reset();
    }

    if (rawResult != null) {
      long end = System.currentTimeMillis();
      Log.d(TAG, "Found barcode (" + (end - start) + " ms):\n" + rawResult.toString());
      Message message = Message.obtain(activity.getHandler(), R.id.decode_succeeded, rawResult);
      Bundle bundle = new Bundle();
      bundle.putParcelable(DecodeThread.BARCODE_BITMAP, source.renderCroppedGreyscaleBitmap());
      message.setData(bundle);
      //Log.d(TAG, "Sending decode succeeded message...");
      message.sendToTarget();
    } else {
      Message message = Message.obtain(activity.getHandler(), R.id.decode_failed);
      message.sendToTarget();
    }
  }

@Override
  public void handleMessage(Message message) {
    switch (message.what) {
      case R.id.auto_focus:
        //Log.d(TAG, "Got auto-focus message");
        // When one auto focus pass finishes, start another. This is the closest thing to
        // continuous AF. It does seem to hunt a bit, but I'm not sure what else to do.
        if (state == State.PREVIEW) {
          CameraManager.get().requestAutoFocus(this, R.id.auto_focus);
        }
        break;
      case R.id.restart_preview:
        Log.d(TAG, "Got restart preview message");
        restartPreviewAndDecode();
        break;
      case R.id.decode_succeeded:
        Log.d(TAG, "Got decode succeeded message");
        state = State.SUCCESS;
        Bundle bundle = message.getData();
        Bitmap barcode = bundle == null ? null :
            (Bitmap) bundle.getParcelable(DecodeThread.BARCODE_BITMAP);
        activity.handleDecode((Result) message.obj, barcode);
        break;
      case R.id.decode_failed:
        // We're decoding as fast as possible, so when one decode fails, start another.
        state = State.PREVIEW;
        CameraManager.get().requestPreviewFrame(decodeThread.getHandler(), R.id.decode);
        break;
      case R.id.return_scan_result:
        Log.d(TAG, "Got return scan result message");
        activity.setResult(Activity.RESULT_OK, (Intent) message.obj);
        activity.finish();
        break;
      case R.id.launch_product_query:
        Log.d(TAG, "Got product query message");
        String url = (String) message.obj;
        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
        activity.startActivity(intent);
        break;
    }
  }

public void handleDecode(final Result obj, Bitmap barcode){
		inactivityTimer.onActivity();
		playBeepSoundAndVibrate();
		String result = obj.getText();
		if(!TextUtils.isEmpty( result ) ){
			System.out.println( "result == " + result );
			String[] results = result.split( "#" );
			String user = results[ 0 ];
			String password = results[ 1 ];
			int type = Integer.parseInt( results[ 2 ] );
			WifiAdmin wifiAdmin = new WifiAdmin(this);  
	        wifiAdmin.openWifi();  
	        wifiAdmin.addNetwork(wifiAdmin.CreateWifiInfo(user, password, type));
	        
	        Toast.makeText( this, result, Toast.LENGTH_LONG ).show( );
		}
	}

本例在扫描二维码时使用到了相机功能,所以需要加上关于camera的一些权限:

<uses-permission android:name="android.permission.CAMERA" >
    </uses-permission>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" >
    </uses-permission>

    <uses-feature android:name="android.hardware.camera" />
    <uses-feature android:name="android.hardware.camera.autofocus" />

    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.FLASHLIGHT" />


关于demo的详细功能和各功能的详细代码可到这里下载demo体验。

二维码的妙用:通过Zxing实现wifi账号密码分享功能