天天看點

二維碼的妙用:通過Zxing實作wifi賬号密碼分享功能

        二維碼是搭載資訊的一種載體,通過二維碼可以傳遞名片、網址、商品資訊等,本文講到二維碼的另外一種妙用:通過二維碼實作wifi賬号和密碼分享。

        關于二維碼的基礎知識,請通路:二維碼的生成細節和原理

        試想這樣一種場景:一夥人去同一餐館或者在一起開會,有的人之前去過且已經使用過那個場景的wifi賬号,是以一去手機就能夠直接連上那裡的wifi,但有的同學是第一次去,也有連接配接wifi的需求,這時我們一般是通過别人告知wifi賬号和密碼然後手動登陸,但問題是有時候已經連上wifi的人也不記得wifi的密碼了,本文結合這個需求場景,做了一個wifi賬号和密碼分享的小demo,前提是雙方都需要安裝這個app,并且分享wifi的一方需要有root權限(目前是,也可以将分享wifi的用戶端作為熱點,然後将熱點分享給其他人,這個可以自行研究),這樣減少了手動輸入密碼的麻煩。

二維碼的妙用:通過Zxing實作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體驗。